fix: Fall back to Spark for native Iceberg writes to gs:// through HadoopFileIO by zhangfengcdt · Pull Request #5935 · apache/datafusion-comet · GitHub
Skip to content

fix: Fall back to Spark for native Iceberg writes to gs:// through HadoopFileIO - #5935

Open
zhangfengcdt wants to merge 2 commits into
apache:mainfrom
zhangfengcdt:fix/iceberg-native-write-gcs-hadoop-fileio
Open

zhangfengcdt wants to merge 2 commits into
apache:mainfrom
zhangfengcdt:fix/iceberg-native-write-gcs-hadoop-fileio

Conversation

@zhangfengcdt

Copy link
Copy Markdown
Member

Which issue does this PR close?

Closes #5637.

Rationale for this change

The native Iceberg write path accepts gs:// data locations, but the only bridge from the Hadoop Configuration into the native FileIO translates fs.s3a.* keys. A HadoopFileIO takes its GCS credentials, endpoint and project from fs.gs.*, so none of that reached the native writer and it could resolve a different storage identity or endpoint than the JVM writer would. Following the issue's recommendation, this change fails closed for that combination rather than attempting an fs.gs.* to gcs.* bridge, which is not a simple key rename.

What changes are included in this PR?

  1. CometIcebergNativeWrite: a new trigger rule that declines a gs:// data location unless the FileIO opening it is a GCSFileIO. The decision is a small package-visible function so it can be unit-tested directly.
  2. IcebergReflection: a helper that returns the effective FileIO class for a location. A ResolvingFileIO is asked for its delegate via ioClass(location), so a table that resolves to GCSFileIO stays eligible. Reflection failures fail closed.
  3. iceberg-writes.md: documents the new condition.

How are these changes tested?

New and updated tests in CometIcebergWriteDetectionSuite

@github-actions github-actions Bot added bug Something isn't working area:writer Native Parquet writer area:Iceberg labels Sep 14, 2026
…doopFileIO

HadoopFileIO takes its GCS configuration from fs.gs.*, which is not
forwarded to the native writer, so a native write could run with a
different storage identity or endpoint than the JVM writer. Decline a
gs:// data location unless the FileIO Iceberg resolves for it is a
GCSFileIO.

Closes apache#5637
@zhangfengcdt
zhangfengcdt force-pushed the fix/iceberg-native-write-gcs-hadoop-fileio branch from 455bffb to f014058 Compare September 14, 2026 21:38
ResolvingFileIO.ioClass throws a NoClassDefFoundError when the GCS
client libraries are absent, which aborted the suite on the Spark 3.4
and 3.5 CI jobs. Resolve the delegate the way the gate does and expect a
decline when it cannot be resolved.

@sunchao sunchao left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Correctness

This addresses #5637: the native Iceberg writer accepts GCS data locations but its Hadoop configuration bridge translates only S3 settings. The new trigger correctly rejects a direct HadoopFileIO at a gs:// data location before native conversion, leaving the JVM write operator in place. It uses the table's data-location provider, so a separate write.data.path is covered; other schemes retain their existing behavior.

One P2 remains: ResolvingFileIO.ioClass(location) reports the scheme-mapped class rather than the instantiated delegate. Initialization can select HadoopFileIO while this helper reports GCSFileIO, admitting the same unsupported Hadoop GCS configuration. The inline comment gives a concrete Iceberg 1.8.1 case. This distinction also exists in the inspected 1.5.2, 1.10.0 and 1.11.0 resolver implementations.

Validation

Reviewed head 44b6a9bdf77b3a19a532b7d34178247236b7aa32 against base 683c7219e39928d267dc45c664d8656a4157da78, including all four changed files and current public discussion. CI checked out merge 298abf56e26de79dd99e65973d4d374eb6a22af6, with identical changed files and relevant dependency/dispatch sources. The new GCS tests passed on Spark 3.4, 3.5, 4.0 and 4.1; Spark 4.2 canceled them because Iceberg is unavailable on that profile. The checks report 53 successes and 10 skips.

A local component probe against the actual Iceberg 1.8.1 runtime confirmed that the invalid chunk-size property throws NumberFormatException, which is an IllegalArgumentException. The resulting delegate selection and native eligibility were traced through source; no local end-to-end GCS write was executed. The planning-only test helper agrees with CommandExecutionMode.SKIP in the maintained Spark 3.5 and 4.0 branches. Maintained 3.4, 4.1 and 4.2 source branches were unavailable, so CI evidence is separate from canonical source coverage.

Performance

The new work occurs during write planning: another data-location lookup, class-hierarchy checks, and reflective class resolution for GCS locations. The existing method cache avoids repeated method discovery; there is no new per-row or per-file data-path work. No material performance claim or measured regression is established here, so a throughput microbenchmark is not needed for this guard.

Design

Adding the check to the existing eligibility rules is a small, appropriate way to preserve JVM storage behavior without implementing an incomplete credential translation. Earlier storage-scheme and FileIO checks still fail closed. The resolver helper needs to inspect the effective delegate before this design fulfills its stated guarantee. The regression should compare the helper with that actual delegate and exercise its initialization fallback.

Abstraction & complexity

The shared scheme parser and package-visible decision function are modest additions that fit the existing structure. The helper's contract currently promises more than the Iceberg API provides; correcting that contract and testing it independently is the substantive simplification needed. No additional abstraction or broader property translation is required.

Comment on lines +575 to +576

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Correctness

[P2] Inspect the instantiated ResolvingFileIO delegate

ioClass(location) does not return the delegate that opens the file: it maps the scheme to a class and calls Class.forName, without consulting ioInstances. The actual io(location) separately initializes that class and catches IllegalArgumentException to cache a HadoopFileIO. See Iceberg 1.8.1.

For example, with the GCS libraries present, gcs.channel.read.chunk-size-bytes=invalid makes 1.8.1's GCSFileIO.initialize throw NumberFormatException, so JVM writes use the Hadoop fallback. This helper still returns GCSFileIO, and the new gate admits a native write that drops that delegate's fs.gs.* configuration. This recreates the identity/endpoint mismatch this PR is intended to prevent.

Please inspect the actual delegate (including initialization fallback), or decline the unresolved case. Add a regression where the scheme-mapped class is GCSFileIO but the effective delegate is HadoopFileIO; deriving the test expectation from this same helper misses that distinction.

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

Labels

area:Iceberg area:writer Native Parquet writer bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Native Iceberg writes drop Hadoop GCS configuration for HadoopFileIO + gs://

2 participants