Skip to content

from_protobuf: Re-enable protobuf integration tests on OSS Spark and Databricks [databricks] [fast-ut] [reduced-it] - #15814

Open
thirtiseven wants to merge 8 commits into
NVIDIA:mainfrom
thirtiseven:protobuf-test-infrastructure-databricks
Open

from_protobuf: Re-enable protobuf integration tests on OSS Spark and Databricks [databricks] [fast-ut] [reduced-it]#15814
thirtiseven wants to merge 8 commits into
NVIDIA:mainfrom
thirtiseven:protobuf-test-infrastructure-databricks

Conversation

@thirtiseven

@thirtiseven thirtiseven commented Aug 28, 2026

Copy link
Copy Markdown
Collaborator

Part of #14069.

Description

#14885 added the first from_protobuf integration-test infrastructure slice, but it was reverted by #15243 because Databricks builds attempted to resolve unpublished vendor-versioned spark-protobuf artifacts. The follow-up discussion in #15232 also established that disabling external jar injection must not disable the tests on Databricks runtimes, where DBR 12.2+ bundles the API.

This PR restores that reviewed infrastructure and corrects the runtime handling:

  • copy and inject the matching external spark-protobuf jar for supported Apache Spark 3.4+ builds;
  • skip Maven artifact copying for Spark 3.3 and Databricks dependency profiles;
  • use INCLUDE_SPARK_PROTOBUF_JAR to control external-jar injection and Apache Spark test eligibility while detecting Databricks test eligibility independently;
  • use the runtime-bundled API on Databricks, consistently for both direct and wrapper-driven integration-test runs;
  • add path-based and binary-descriptor CPU-fallback smoke tests using a static descriptor set, so the smoke coverage does not depend on Python descriptor construction;
  • install the Python protobuf package in the integration-test image dependencies for follow-up semantic coverage; and
  • skip cleanly when from_protobuf is unavailable.

No GPU implementation is enabled in this PR. This only establishes dependency handling and the CPU-fallback baseline for later slices. The new Python dependency becomes available to regular CI after this PR is merged and the integration-test images are rebuilt. Performance testing is not required because plugin runtime code is unchanged.

AI assistance: OpenAI Codex was used to help implement and review this change; the author reviewed the final diff and description.

Checklists

Documentation

  • Updated for new or modified user-facing features or behaviors
  • No user-facing change

Testing

  • Added or modified tests to cover new code paths
  • Covered by existing tests
    (Please provide the names of the existing tests in the PR description.)
  • Not required

Performance

  • Tests ran and results are added in the PR description
  • Issue filed with a link in the PR description
  • Not required

thirtiseven and others added 2 commits August 28, 2026 15:09
…VIDIA#14885)

Part of NVIDIA#14069. First slice carved out of NVIDIA#14354 following the
plugin-side split plan in the issue.

### Description

#### Problem

`from_protobuf` is provided by Spark's optional `spark-protobuf` module,
which is not normally present on the integration-test classpath.
Subsequent GPU-support PRs need a stable CPU fallback baseline without
introducing an unshaded protobuf runtime dependency.

#### Changes

- Copy `spark-protobuf_${scala.binary.version}` into the
integration-test dependencies for Spark 3.4+, for both Scala 2.12 and
2.13 builds. Spark 3.3 profiles skip this artifact.
- Add `INCLUDE_SPARK_PROTOBUF_JAR` handling to
`run_pyspark_from_build.sh`. Protobuf tests are enabled by default when
exactly one matching jar is found:
  - one jar: add it to `ALL_JARS`;
- no jar: disable the tests, warning when inclusion was explicitly
requested;
- multiple jars: warn and disable the tests to avoid classpath-dependent
API selection;
- `INCLUDE_SPARK_PROTOBUF_JAR=false`: disable the tests without a
warning.
- Add a driver-local `local_tmp_path` fixture. The path-based protobuf
API reads its descriptor with `java.io.File`, so a Hadoop-backed
temporary path is not suitable on distributed filesystems.
- Add two CPU-fallback smoke tests:
  - `test_from_protobuf_smoke_path_api` for Spark 3.4+;
- `test_from_protobuf_smoke_binary_descriptor_api` for Spark 3.5+,
skipped when the PySpark API does not expose `binaryDescriptorSet`.

The tests use static `FileDescriptorSet` bytes and hand-encoded
messages. This avoids depending on whichever unshaded protobuf runtime
the Spark driver provides. The path-based test writes those bytes to
`local_tmp_path`; the Spark 3.5+ test passes the same bytes directly.

No GPU implementation is added. Both tests use
`assert_gpu_fallback_collect("ProtobufDataToCatalyst")` to compare CPU
and GPU-mode results while verifying CPU fallback.

#### Testing

- Spark 3.5.2 targeted integration run:
  - `test_from_protobuf_smoke_path_api`
  - `test_from_protobuf_smoke_binary_descriptor_api`
  - Result: `2 passed`
- Spark 3.4.1 standalone compatibility probe: the path-based API
successfully read the static descriptor; the binary descriptor API is
not available in Spark 3.4.
- Multiple matching `spark-protobuf` jars: warning emitted, jars
excluded, and protobuf tests skipped.
- `bash -n`, `shellcheck -S error`, and `git diff --check` passed.

### Checklists

Documentation
- [ ] Updated for new or modified user-facing features or behaviors
- [x] No user-facing change

Testing
- [x] Added or modified tests to cover new code paths
- [ ] Covered by existing tests
(Please provide the names of the existing tests in the PR description.)
- [ ] Not required

Performance
- [ ] Tests ran and results are added in the PR description
- [ ] Issue filed with a link in the PR description
- [x] Not required

---------

Signed-off-by: Haoyang Li <haoyangl@nvidia.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Signed-off-by: Haoyang Li <haoyangl@nvidia.com>
@thirtiseven thirtiseven changed the title [WIP] Re-enable protobuf integration tests on OSS Spark and Databricks [databricks] from_protobuf: Re-enable protobuf integration tests on OSS Spark and Databricks [databricks] [fast-ut] [reduced-it] Aug 28, 2026
@thirtiseven thirtiseven self-assigned this Aug 28, 2026
@thirtiseven thirtiseven mentioned this pull request Aug 28, 2026
24 tasks
Signed-off-by: Haoyang Li <haoyangl@nvidia.com>
@thirtiseven

Copy link
Copy Markdown
Collaborator Author

build

@amahussein

Copy link
Copy Markdown
Collaborator

Is this split of the old PR #14354 ?

@thirtiseven

Copy link
Copy Markdown
Collaborator Author

Is this split of the old PR #14354 ?

Yes it is.

Signed-off-by: Haoyang Li <haoyangl@nvidia.com>
Signed-off-by: Haoyang Li <haoyangl@nvidia.com>
@thirtiseven thirtiseven added the test Only impacts tests label Aug 31, 2026
Signed-off-by: Haoyang Li <haoyangl@nvidia.com>
@thirtiseven
thirtiseven marked this pull request as ready for review August 31, 2026 07:27
@thirtiseven
thirtiseven requested a review from a team as a code owner August 31, 2026 07:27
@greptile-apps

greptile-apps Bot commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR restores CPU-fallback integration coverage for Spark's from_protobuf API while keeping dependency handling compatible with OSS Spark, Spark 3.3, and Databricks.

  • Copies and injects the matching spark-protobuf artifact for supported OSS Spark releases.
  • Skips external artifact resolution for Spark 3.3, CDH, and Databricks dependency profiles.
  • Detects Databricks protobuf availability independently from external-jar injection.
  • Adds deterministic descriptor-based fallback smoke tests and a driver-local temporary-path fixture.
  • Keeps the Scala 2.12 and Scala 2.13 Maven trees aligned.

Confidence Score: 5/5

The PR appears safe to merge; no actionable correctness, security, build-profile, or repository-rule issue remains.

The current profiles avoid resolving protobuf artifacts where they are unavailable, the launcher keeps OSS test eligibility synchronized with actual jar injection, and supported Databricks runs use the bundled API. The previous Maven inheritance concern was correctly withdrawn after effective-model verification and is resolved.

Important Files Changed

Filename Overview
integration_tests/run_pyspark_from_build.sh Detects Databricks arguments, validates the matching protobuf jar, and keeps test eligibility aligned with actual jar injection.
integration_tests/src/main/python/protobuf_test.py Adds deterministic path-based and binary-descriptor smoke tests that verify the expected protobuf CPU fallback.
integration_tests/src/main/python/spark_session.py Gates protobuf tests by runtime capabilities, independently enabling the bundled API on supported Databricks releases.
integration_tests/src/main/python/conftest.py Adds an isolated driver-local temporary directory fixture with cleanup after each test.
integration_tests/pom.xml Copies the target Spark protobuf artifact during packaging unless the selected build profile disables it.
pom.xml Defines the default protobuf-copy behavior and disables copying across Spark 3.3 release profiles.
scala2.13/integration_tests/pom.xml Mirrors protobuf artifact packaging for the Scala 2.13 integration-test build.
scala2.13/pom.xml Keeps protobuf-copy profile settings synchronized with the Scala 2.12 Maven tree.
shim-deps/pom.xml Disables unavailable external protobuf artifact resolution for CDH and Databricks dependency profiles.
scala2.13/shim-deps/pom.xml Mirrors vendor-specific protobuf-copy exclusions in the Scala 2.13 dependency tree.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
  A[Test launcher] --> B{Databricks runtime?}
  B -- Yes --> C[Disable external protobuf jar injection]
  C --> D[Use runtime-bundled protobuf API]
  B -- No --> E{Spark 3.4+ and exactly one matching jar?}
  E -- Yes --> F[Inject spark-protobuf jar]
  E -- No --> G[Disable protobuf tests]
  D --> H[Run descriptor path and binary descriptor smoke tests]
  F --> H
  H --> I[Compare CPU and GPU runs]
  I --> J[Verify ProtobufDataToCatalyst CPU fallback]
Loading

Reviews (3): Last reviewed commit: "Merge branch 'main' into protobuf-test-i..." | Re-trigger Greptile

Comment thread shim-deps/pom.xml
@thirtiseven

Copy link
Copy Markdown
Collaborator Author

build

NvTimLiu
NvTimLiu previously approved these changes Sep 3, 2026

@NvTimLiu NvTimLiu left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, +1

BTW, how long will protobug_test take?

Whether the protobug_test run or not if the "$LOCAL_JAR_PATH"/spark-protobuf*.jar does not exist for nightly integration tests?

@NvTimLiu

NvTimLiu commented Sep 3, 2026

Copy link
Copy Markdown
Collaborator

LGTM, +1

BTW, how long will protobug_test take?

Whether the protobug_test run or not if the "$LOCAL_JAR_PATH"/spark-protobuf*.jar does not exist for nightly integration tests?

This is what I got from the pre-merge CI:

09ba6825-fbb6-4c0f-a9fe-4fcc2f0992dd

@amahussein amahussein left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @thirtiseven

My comments are not blockers.

One ask, inline: the default configuration skips silently. No jar and INCLUDE_SPARK_PROTOBUF_JAR unset gives 2 skipped, exit 0, no warning. That is NvTimLiu's second question, and widening the existing warning by one condition answers it in code.

Follow-ups, not merge conditions:

  • Neither test asserts a decoded value, so a wire-valid encoding error passes. An off-by-one mutant on the encoder still gave 2 passed. Worth pinning the four known rows in the follow-up that adds semantic coverage.
  • The Python protobuf requirement is unused by this diff.
  • The branch is 34 commits behind main and does not compile standalone against the current JNI snapshot (unrelated applyBooleanMask deprecations fixed by #15848). CI tests the merge result so pre-merge is green, but a merge from main before this lands would keep a plain checkout of the branch buildable.

Comment on lines +188 to +189
elif [[ "$INCLUDE_SPARK_PROTOBUF_JAR_REQUESTED" == "true" ]]; then
>&2 echo "WARNING: INCLUDE_SPARK_PROTOBUF_JAR=true was requested but a spark-protobuf jar was not found (searched: $PROTOBUF_JARS)."

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the default configuration and it fails silently. With no jar on disk and INCLUDE_SPARK_PROTOBUF_JAR unset, neither warning fires (the first needs >1, this one needs an explicit true), so both tests skip and the run exits 0 with no diagnostic. I reproduced it by removing the jar: 2 skipped, rc=0, no log line matching warning.*protobuf. Since injection is opt-out, silence should be reserved for the case where someone actually opted out. This also answers NvTimLiu's question about nightly runs where $LOCAL_JAR_PATH/spark-protobuf*.jar is absent.

Suggested change
elif [[ "$INCLUDE_SPARK_PROTOBUF_JAR_REQUESTED" == "true" ]]; then
>&2 echo "WARNING: INCLUDE_SPARK_PROTOBUF_JAR=true was requested but a spark-protobuf jar was not found (searched: $PROTOBUF_JARS)."
elif [[ "$INCLUDE_SPARK_PROTOBUF_JAR_REQUESTED" != "false" ]]; then
>&2 echo "WARNING: a spark-protobuf jar was not found (searched: $PROTOBUF_JARS); protobuf tests will be skipped."

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in ff62aca. A missing jar now warns unless INCLUDE_SPARK_PROTOBUF_JAR=false is explicitly set.

return _make_smoke_df(spark).select(
from_protobuf_fn(f.col("bin"), "test.Simple", desc_path).alias("d"))

assert_gpu_fallback_collect(run, "ProtobufDataToCatalyst")

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Both tests compare CPU against GPU-with-fallback, and ProtobufDataToCatalyst always falls back, so both sides run the same CPU implementation and no decoded value is ever asserted. I checked what that costs by mutating the encoder to _encode_varint(i32_value + 1): still 2 passed. To be fair the tests are not vacuous, since FAILFAST means a malformed payload or a mismatched descriptor does fail them (I verified both), and the fallback assertion is real. But for a baseline that later GPU slices get diffed against, the values are the part worth pinning. Could the follow-up assert the four known _smoke_rows against the decoded struct?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. This PR only adds smoke coverage for the plugin-0 path. The follow-up semantic tests will compare the decoded rows with _smoke_rows.

# See the License for the specific language governing permissions and
# limitations under the License.
pytest
protobuf

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nothing in this diff imports protobuf; the tests deliberately use a static hex descriptor to avoid the dependency. Since the images have to be rebuilt after merge before it is even available, it seems to belong in the follow-up that needs it rather than here.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need this dependency here so the CI images are ready for the follow-up. The images install requirements.txt only when they are built. If we add protobuf with the semantic tests, that PR will still use an older image without it. The Python package will be used by those follow-up tests.

Signed-off-by: Haoyang Li <haoyangl@nvidia.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

test Only impacts tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants