Skip to content

Support Iceberg v3 initial defaults in GPU reads [databricks] - #15624

Open
res-life wants to merge 48 commits into
NVIDIA:mainfrom
res-life:fix/iceberg-v3-default-semantics
Open

res-life wants to merge 48 commits into
NVIDIA:mainfrom
res-life:fix/iceberg-v3-default-semantics

Conversation

@res-life

@res-life res-life commented Aug 11, 2026 •

Copy link
Copy Markdown
Collaborator

Contributes to #15437.

Description

This PR supports Iceberg v3 initial-default semantics in GPU reads, including fields required implicitly by equality-delete files.

It materializes supported primitive and nested defaults while preserving parent/container nullability, and falls back during planning for unsupported default types or nested equality-delete keys.

Earlier planning-only measurement, before the reader fixes: on an AMD EPYC 7R13 host with Spark local[16], the opt-in traversal added a median paired 0.694 ms (0.71 us/task) across 978 scan tasks, with a paired p95 of 4.687 ms. This used 30 measured pairs after five warm-up pairs. Both modes asserted CPU BatchScan fallback, so this measures planning rather than GPU reader performance.

Reader-only runtime measurements at 259d6b98c, before the shim/visitor refactor and the latest base integration, show the cost of retaining helper leaves and bypassing unsafe pruning. The control substitutes reader.scala and GpuParquetReaderPostProcessor.scala from be4e548e4 while holding the other PR dependencies fixed. Spark 3.5.6 / Scala 2.12 / Iceberg 1.10.1, one NVIDIA L4, local[4], PERFILE, file cache disabled, two million rows in 16 files; A/B/B/A ordering, five warm-ups per JVM and 20 measured samples per artifact/shape.

Warm-cache workload Control median Candidate median Spark input bytes: control → candidate
Normal projection, narrow helper 541.8 ms 542.6 ms 79.85 MB → 79.85 MB
Normal projection, wide helper 541.2 ms 544.1 ms 79.85 MB → 79.85 MB
Missing-default predicate with no matches, narrow helper 115.4 ms 286.4 ms 0.10 MB → 2.43 MB
Missing-default predicate with no matches, wide helper 114.0 ms 358.5 ms 0.10 MB → 39.37 MB

The missing-default predicate returns the same correct empty result in both versions; the candidate reads old row groups conservatively before evaluating the residual. Input metrics report requested Spark bytes, not physical disk I/O. These measurements do not establish the performance of the final head, which has not been rebenchmarked after the shim/visitor refactor and VARIANT integration.

  • Version-dispatched Parquet projection preserves physical container paths and validity leaves when only newly added fields are requested. Row-group filtering retains row groups when a predicate references an absent non-null initial default, so Spark can evaluate residuals after materialization. Iceberg 1.6–1.11 retain the compatibility implementation; future shims can override these boundaries with validated native behavior.
  • A physical-schema visitor decodes unprojected helpers by physical type, including opaque VARIANT groups, while retaining projected logical types and LIST/MAP wrapper structure. Helpers receive IDs outside the requested schema to prevent accidental binding, and legacy two-level LISTs retain their element interpretation.
  • Planning recursively checks structs, lists and maps and includes implicit equality-delete fields. Unsupported initial defaults and nested equality-delete keys trigger CPU fallback. GPU write-default support is tracked separately in Support Iceberg v3 write defaults in GPU writes res-life/spark-rapids#16.
  • Read regressions live in iceberg/iceberg_read_test.py, since they cover both format v2 and v3. Delete regressions are consolidated in iceberg/iceberg_delete_test.py. The move preserves all 47 function/class definitions, and all 83 moved parametrized case IDs match the prior runtime report.

Latest base integration: merged 98d729483 (#16443) without rewriting the reviewed commits. The merge retains both shim APIs and adapts two new unit-test calls to the version-dispatched ParquetUnshader.

Combined-head validation at 1513612ac against base 98d729483:

  • JDK 17 builds passed for Spark 3.5.6 / Scala 2.12 and Spark 4.0.4 + 4.1.1 / Scala 2.13; Spark 3.5.0 / Iceberg 1.6.1 compilation/package compatibility also passed. The converter modules were cleaned before rebuilding their moved generated sources.
  • GpuPostProcessorSuite: 46/46; GpuTypeToSparkTypeSuite: 9/9; Spark 4.1 GpuIcebergVariantSchemaSuite: 10/10. No unit-test failures, errors or skips.
  • Integration runs: 287 passed, 6 expected VARIANT skips on Spark 3.5.6, zero failures/errors. Normal and system-classpath runs cover Spark 3.5.6 / Iceberg 1.10.1 and Spark 4.0.4 / Iceberg 1.11.0; Spark 3.5.6 / 1.9.2 passed the selected v2 helper cases. Spark 4.1.1 / 1.11.0 passed its default-only struct, primitive-default and VARIANT-helper cases in both deployment modes.
  • All 13 affected shim/reader classes remain byte-identical across the overlapping Iceberg modules and match the packaged aggregator/distribution copies.
  • Source-selection preflight passed all 32 profiles on the current-base combination. The bounded cross-PR advisory checked five combinations: three had no new modelled static interactions; Pin shared Spark installation dependency generator [databricks] #16549 and Use shared shim source inheritance in public builds [databricks] #16595 could not be combined with the base because of merge conflicts. Remaining candidates were not checked within the tool limit. This advisory models sql-plugin source selection rather than API or full-runtime compatibility.

The test reorganization additionally collected 153 cases across 37 test functions, preserving all 83 moved case IDs and all 47 function/class definitions; 17 targeted post-reorganization cases passed before the base merge. Earlier America/New_York runs at 259d6b98c passed four cases on each of Spark 3.5.6 and 4.0.4; they were not repeated for the merged head.

Baseline checks reproduced Unknown logical type annotation: VARIANT(1), unmapped helpers supplying 99/100 instead of default 11, and Not a struct type: long on a legacy LIST. The corresponding regressions now pass. Legacy LIST tests use Spark's raw Parquet CPU reader as their oracle because Iceberg's CPU row reader separately fails on that layout.

The title retains [databricks] for filesystem-path and execution-plan coverage. AI assistance: Codex assisted with implementation, tests and local validation under Chong Gao's direction.

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

Chong Gao added 2 commits August 11, 2026 17:33
Materialize initial defaults during GPU Parquet reads, preserve Spark default metadata, and fall back for unsupported default types. Add unit and integration coverage for schema evolution and nested structs.

Refs NVIDIA#15437.

Signed-off-by: Chong Gao <chongg@nvidia.com>
Signed-off-by: Chong Gao <chongg@nvidia.com>
@res-life
res-life requested a review from liurenjie1024 August 13, 2026 08:34
Chong Gao added 2 commits August 13, 2026 18:17
Signed-off-by: Chong Gao <chongg@nvidia.com>
Signed-off-by: Chong Gao <chongg@nvidia.com>
@res-life
res-life marked this pull request as ready for review August 13, 2026 10:44
@greptile-apps

greptile-apps Bot commented Aug 13, 2026 •

Copy link
Copy Markdown
Contributor

RetriggerView in GreptileConfidence Score: 5/5

[Medium impact] The reviewed code appears safe to merge; no blocking defect was established.

Summary

Adds GPU reads for supported Iceberg v3 initial defaults.

  • GPU reads fill missing Iceberg fields with supported initial defaults.
  • Scan planning checks defaults and equality-delete keys before using the GPU path.
  • Parquet reads keep container validity while using physical types for helper fields.

No actionable new issue was established. No previous review threads were supplied.

Shim compatibility evidence is incomplete: the referenced skill file is missing, and GitHub access requires authentication. Exact-head core and related-PR reports could not be checked. No builds or runtime tests were run during this review.

Diagram

%%{init: {'theme': 'neutral'}}%%
flowchart TD
  A[Iceberg scan] --> B[Check defaults and equality-delete keys]
  B -->|Unsupported| C[CPU fallback]
  B -->|Supported| D[Project physical fields and retain helpers]
  D --> E[Decode Parquet]
  E --> F[Fill missing defaults and preserve parent nulls]
  F --> G[Apply deletes and remaining predicates]
Loading

Reviews (19) · Last reviewed commit: "Merge origin/main for VARIANT schema con..." · Reviewed by Greptile

Signed-off-by: Chong Gao <chongg@nvidia.com>
Signed-off-by: Chong Gao <chongg@nvidia.com>
Signed-off-by: Chong Gao <chongg@nvidia.com>
Signed-off-by: Chong Gao <chongg@nvidia.com>

@liurenjie1024 liurenjie1024 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.

One blocking correctness issue remains in the planning-time default validation; see the inline comment. The updated head now correctly rejects UUID/FIXED defaults and adds direct coverage for supported primitive types.

Non-blocking test note: the end-to-end write-default section only runs outside Spark 3.5, while the reported successful integration validation is Spark 3.5.x, so that branch has not yet been exercised in the listed validation.

Signed-off-by: Chong Gao <chongg@nvidia.com>
Signed-off-by: Chong Gao <chongg@nvidia.com>
Signed-off-by: Chong Gao <chongg@nvidia.com>
Signed-off-by: Chong Gao <chongg@nvidia.com>
Signed-off-by: Chong Gao <chongg@nvidia.com>
Signed-off-by: Chong Gao <chongg@nvidia.com>
Signed-off-by: Chong Gao <chongg@nvidia.com>
@res-life
res-life marked this pull request as draft September 2, 2026 09:33
Signed-off-by: Chong Gao <chongg@nvidia.com>
Signed-off-by: Chong Gao <chongg@nvidia.com>
Signed-off-by: Chong Gao <chongg@nvidia.com>
Signed-off-by: Chong Gao <chongg@nvidia.com>
Signed-off-by: Chong Gao <chongg@nvidia.com>
…t-semantics

Signed-off-by: Chong Gao <chongg@nvidia.com>
Signed-off-by: Chong Gao <chongg@nvidia.com>
GpuDeleteFilter currently resolves equality fields only from top-level schemas. Validate every equality field during planning so nested keys fall back safely, and cover the behavior with a nested delete-file integration test.

Signed-off-by: Chong Gao <chongg@nvidia.com>
Move write-default schema metadata and GPU write coverage into a separate follow-up PR.

Signed-off-by: Chong Gao <chongg@nvidia.com>
@res-life
res-life marked this pull request as ready for review September 9, 2026 06:12
@sameerz sameerz added the task Work required that improves the product but is not user facing label Sep 17, 2026
@nvauto

nvauto commented Sep 29, 2026

Copy link
Copy Markdown
Collaborator

NOTE: release/26.10 has been created from main. Please retarget your PR to release/26.10 if it should be included in the release.

Signed-off-by: Chong Gao <chongg@nvidia.com>
@res-life res-life self-assigned this Oct 12, 2026

@liurenjie1024 liurenjie1024 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.

One blocking correctness issue remains for projections that select only a newly defaulted child of an existing struct; see the inline comment.

AI-assisted review. The schema-projection case was reproduced with Iceberg 1.10.1. Full GPU validation was unavailable locally because of an NVIDIA driver/library version mismatch.

Retain a physical descendant when pruning would leave an empty struct so the GPU reader preserves parent validity before filling initial defaults. Keep map key/value wrappers intact and drop helper fields from the requested output.

Add schema/action coverage through nested containers and a deterministic CPU/GPU regression for default-only child projections across all three Parquet readers.

Signed-off-by: Chong Gao <chongg@nvidia.com>
Use the GroupType view to select the single-name getType overload and avoid ambiguity with MessageType.getType(String*) during Scala compilation.

Signed-off-by: Chong Gao <chongg@nvidia.com>
Allow physical siblings already retained by Iceberg while requiring exactly the original projected leaf paths plus the expected minimum helper paths. Keep field-ID and default-action assertions.

Signed-off-by: Chong Gao <chongg@nvidia.com>
Disable nested schema pruning only for the CPU reference because Iceberg otherwise loses null-parent validity. Keep GPU pruning enabled and preserve exact CPU/GPU and expected default-value assertions.

Signed-off-by: Chong Gao <chongg@nvidia.com>
Use required field IDs to restore physical nested containers dropped entirely by Iceberg pruning. Preserve file order and existing projected fields, retain valid helper paths, and keep metadata-only projections empty.

Pass the requested schema through the shared reader and keep all nested-container action tests.

Signed-off-by: Chong Gao <chongg@nvidia.com>
Signed-off-by: Chong Gao <chongg@nvidia.com>
Keep row groups when a filter references an absent non-null initial
value so residual evaluation happens after default materialization.
Decode unprojected NTZ helper leaves as timestamps to retain parent
validity without enabling unsupported projected types.

Add interaction coverage for predicates, nested containers, mixed-file
combining, schema evolution, name mapping, and equality/position/DV
deletes. The final matrix passes 172 integration executions and 41
Scala unit tests across Spark 3.5.6 and Spark 4.0.4.

Signed-off-by: Chong Gao <chongg@nvidia.com>
@liurenjie1024
liurenjie1024 self-requested a review October 20, 2026 06:42
Restore requested physical containers recursively before selecting helper leaves, including Parquet list and map wrappers without field IDs. This preserves sibling collections and their cardinality in default-only projections.

Cover unrelated first siblings, nested list/map projections, and multiple requested collections with schema and end-to-end query regressions.

Signed-off-by: Chong Gao <chongg@nvidia.com>
Retain initial-default planning imports together with the upstream encrypted-table fallback in GpuSparkScan.

Signed-off-by: Chong Gao <chongg@nvidia.com>
Read unprojected validity helpers using their raw Parquet types so UUID and timestamp annotations cannot fail schema conversion. Preserve projected logical types and include helpers retained for deletion-vector reads.

Cover v2 and v3 struct, list, and map projections with unprojected UUIDs across all three readers, plus physical schema conversion and projected logical types.

Signed-off-by: Chong Gao <chongg@nvidia.com>
Assign unmapped physical helpers IDs outside the requested schema and preserve the element shape of legacy two-level LIST projections. Cover initial defaults and v2 null filling with schema and end-to-end regressions.

Signed-off-by: Chong Gao <chongg@nvidia.com>
Comment thread integration_tests/src/main/python/iceberg/iceberg_read_test.py
Comment thread integration_tests/src/main/python/iceberg/delete_test.py Outdated
Signed-off-by: Chong Gao <chongg@nvidia.com>
Signed-off-by: Chong Gao <chongg@nvidia.com>
Signed-off-by: Chong Gao <chongg@nvidia.com>
Signed-off-by: Chong Gao <chongg@nvidia.com>
Signed-off-by: Chong Gao <chongg@nvidia.com>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

task Work required that improves the product but is not user facing

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants