Fix double escaping of Iceberg S3 input-file URIs - #15360
Conversation
Signed-off-by: Ray Liu <liurenjie2008@gmail.com>
Greptile SummaryThis PR fixes a double-escaping bug in the RAPIDS Iceberg S3 reader: the old
Confidence Score: 5/5Safe to merge: the change is a targeted, mechanical replacement of an intermediate URI object with raw bucket+key strings, removing the only site that caused double-escaping. The fix is minimal and directly addresses the root cause — the component-based URI constructor that re-encoded already-escaped keys. The deleted class had no other callers, the new code stays in the same package so the package-private BaseS3File.uri() cast remains valid, and inputFile.location() is the correct string to pass to resolveClient. A companion integration test exercises the corrected path with a URI-invalid character in the key. No GPU allocation, retry, or shim logic is touched. Files Needing Attention: No files require special attention. Important Files Changed
Sequence DiagramsequenceDiagram
participant IC as IcebergS3InputFile
participant BS as BaseS3File (Iceberg)
participant S3U as S3URI
participant RC as IcebergS3RangeCopier
Note over IC: Before fix (double-escape path)
IC->>BS: instanceof BaseS3File cast
IC->>BS: uri()
BS-->>IC: S3URI (key already has URL escapes e.g. %3A)
IC->>IC: new URI(s3, bucket, /+key, null) → %3A re-encoded to %253A
IC->>RC: resolveClient(s3Uri.toString(), ...)
IC->>RC: copyToHMB(client, output, s3Uri, ...)
Note over IC: After fix (no double-escape)
IC->>BS: instanceof BaseS3File cast
IC->>BS: uri()
BS-->>IC: S3URI
IC->>S3U: bucket() → s3Bucket
IC->>S3U: key() → s3Key (raw, preserves %3A)
IC->>RC: resolveClient(inputFile.location(), ...)
IC->>RC: copyToHMB(client, output, s3Bucket, s3Key, ...)
Reviews (8): Last reviewed commit: "Merge remote-tracking branch 'upstream/r..." | Re-trigger Greptile |
|
build |
gerashegalov
left a comment
There was a problem hiding this comment.
The cited integration test covers existing %HH escapes, not URI-invalid raw keys or fallback behavior.
The PR description also ends with an internal drafting instruction; remove it
|
Comment addressed, requires a change in internal repo. cc @res-life @gerashegalov |
| @pytest.mark.parametrize('reader_type', rapids_reader_types) | ||
| @pytest.mark.skipif(not is_iceberg_rest_catalog(), | ||
| reason="S3 path handling is exercised only with the REST catalog") | ||
| def test_iceberg_parquet_read_from_uri_invalid_s3_path(spark_tmp_table_factory, reader_type): |
There was a problem hiding this comment.
Assert effective PerfIO S3 enablement and preferably verify that no Iceberg fallback was recorded. Otherwise this test does not really exercise the previously-broken call path.
There was a problem hiding this comment.
Add a config in test to ensure perf io has been enabled.
|
NOTE: release/26.08 has been created from main. Please retarget your PR to release/26.08 if it should be included in the release. |
|
build |
| assert_gpu_and_cpu_are_equal_collect( | ||
| lambda spark: spark.sql(f"SELECT * FROM {table}"), | ||
| conf={ | ||
| 'spark.rapids.perfio.s3.enabled': 'true', |
There was a problem hiding this comment.
This configuration is startup-only and is cached during executor plugin initialization. If PerfIO is not enabled when Spark starts, the test will use the standard Iceberg
delegate. As a result, the CPU/GPU equality test may still pass even if the newly fixed range-copy code is never executed.
There was a problem hiding this comment.
Could this help?
def is_perfio_s3_enabled(spark):
return spark._jvm.com.nvidia.spark.rapids.fileio \
.RapidsInputFiles.isS3PerfEnabled()| {"spark": "356"} | ||
| {"spark": "357"} | ||
| {"spark": "358"} | ||
| {"spark": "359"} |
|
build |
1 similar comment
|
build |
Fixes #15323. ### Description This is a follow-up to #15360 that makes the Iceberg S3 path regression test reliable with a REST catalog. The test previously constructed the problematic path by overriding `write.data.path` with the configured warehouse. For a REST catalog, the warehouse value can be a logical identifier rather than a physical S3 location. The updated test follows `test_iceberg_parquet_read_from_url_encoded_path`: - Adds a string containing a raw space as special-case partition data. The value is valid in an S3 object key but invalid in a URI unless encoded. - Creates an Iceberg table partitioned by that column and lets Iceberg construct the data-file path through its normal partition writer. - Continues to run only with the REST catalog, where the optimized S3 reader is exercised. - Verifies the startup-only `spark.rapids.perfio.s3.enabled` setting through SparkConf instead of querying an executor-initialized PerfIO singleton from the driver. Testing performed: - The REST-catalog run completed 184 existing tests successfully; the three new parameterized variants reached table setup and exposed the driver-side PerfIO assertion corrected here. - `python3 -m py_compile integration_tests/src/main/python/iceberg/iceberg_test.py` - `git diff --check` A final REST-catalog pipeline rerun is pending. ### 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 - [ ] 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: Ray Liu <liurenjie2008@gmail.com>
### Description #15360 reintroduced an `IllegalAccessError` when Iceberg runtime jars are loaded through `spark.driver.extraClassPath` and `spark.executor.extraClassPath`. Iceberg’s `BaseS3File` and `S3URI` classes are package-private. JVM runtime-package identity includes both the package name and defining classloader, so the shim-loaded `IcebergS3InputFile` cannot access those classes when the Iceberg runtime is loaded by the app classloader. This change: - Restores the root-loadable `IcebergS3InputFileAccess` bridge. - Keeps all access to `BaseS3File` and `S3URI` inside that bridge. - Adds comments documenting the classloader requirement and why the access must remain isolated. - Restores the accessor to `unshimmed-common-from-single-shim.txt`. - Returns raw S3 bucket and key values from the accessor, preserving #15360’s fix for URI double escaping. Non-S3 inputs and unsupported S3 client configurations continue to fall back to the standard Iceberg input path. ### 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 (`test_iceberg_parquet_read_from_uri_invalid_s3_path` and the Iceberg read/write integration suite.) - [ ] 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: Renjie Liu <liurenjie2008@gmail.com> --------- Signed-off-by: Ray Liu <liurenjie2008@gmail.com>
Fixes #15323.
Description
Iceberg preserves URL escapes in
S3URI.key(). When the RAPIDS plugin convertsan Iceberg
S3InputFileto a JavaURI, it previously passed that key to acomponent-based
URIconstructor. As a result,%was escaped again: forexample, an object key containing
%3Awas converted to%253A, and theplugin attempted to read a different S3 object.
Build the URI from the complete S3 location instead of supplying the already
escaped key as a URI path component. This preserves Iceberg's original escaped
S3 key and allows the RAPIDS Iceberg S3 reader to access the intended object.
The change does not introduce user-facing configuration or behavior beyond
correctly reading existing URL-escaped S3 paths.
The existing Iceberg integration test
test_iceberg_parquet_read_from_url_encoded_pathcovers URL-escaped partitionpaths. The direct-S3 reproduction runs its
PERFILEparameterization againstan S3-backed Iceberg catalog, which exercises
IcebergS3InputFileAccess.Checklists
Documentation
Testing
Performance