andygrove opened a new issue, #6536:
URL: https://github.com/apache/datafusion-comet/issues/6536
#6478 (for #6462) added per-location S3 credentials for native Iceberg reads
and writes. Reviewing it turned up the follow-on work below. None of it blocked
the PR.
- [ ] Native Iceberg scans skip the configured provider when the table's
metadata location has no host
- [ ] Narrow the multi-bucket wording in the user guide and the
`CometS3LocationScopedCredentialProvider` Javadoc
- [ ] Update the design doc's description of how location fetches block
- [ ] Add native write and cross-bucket read cases to
`CometS3CredentialBridgeSuite`
### 1. Hostless metadata locations skip the provider
When an Iceberg catalog sets `s3.comet.credential.provider.class`, the
native Iceberg scan builds the credential bridge from the bucket in the table's
metadata location (`build_s3_access` in
`native/core/src/execution/operators/iceberg_common.rs`). An opted-in
S3-compliant alias scheme (`fs.comet.s3Compliant.schemes`) can record hostless
locations such as `blob:///bucket/warehouse/db/t/metadata/v3.metadata.json`,
with the bucket as the first path segment. That URL has no host, so
`build_s3_access` returns `S3Access::Loader(None)` before it looks up the
provider class. The scan then signs every request with opendal's default
credential chain. The configured provider is never called, and a
`CometS3LocationScopedCredentialProvider`'s policy locations are not used. The
IRSA web-identity take-over is skipped as well, and nothing is logged.
Such a table still scans natively. `CometScanRule` checks the metadata
location's scheme but not its host, and `hasOpenableAuthority` accepts hostless
alias data and delete files, because `BlobHostPromotingS3Storage` promotes the
bucket when a file is opened. So the requests reach S3 with whatever
credentials the executor's environment provides. Depending on those, reads fail
with 403 or succeed with broader credentials than the provider would have
vended.
The Parquet path promotes the bucket from the first path segment before it
builds the store (`prepare_object_store_with_configs`), so this gap is specific
to the Iceberg path. Native Iceberg writes are not affected, because the write
gate declines alias schemes. #6478 documents the limitation in the user guide
("Enabling a bridge") and in the Javadoc.
This was found by reading the code. The test FileSystem in `CometS3TestBase`
only handles host-bearing `blob://bucket/...` paths, so the MinIO suites cannot
produce a hostless table today.
Expected: the configured provider serves the table, as it does for
`blob://<bucket>/...` locations and on the Parquet path. If Comet cannot do
that, it should fall back to Spark rather than read with the default chain. One
possible fix is to promote the reference path with `promote_hostless_alias_url`
before `build_s3_access` reads its host, when the scheme is an opted-in alias.
The `FileIO` cache key can keep the raw path.
### 2. Multi-bucket wording
The user guide (`s3-credential-providers.md`, "Credentials per location")
and the `@Public` `CometS3LocationScopedCredentialProvider` Javadoc say a table
whose files span several locations or buckets gets each file's credential
right. But `CometScanRule` still falls back to Spark when a scan's data and
delete files span more than one S3 bucket, because one `FileIO` carries one S3
configuration. So the cross-bucket case Comet serves natively is a table whose
data and delete files sit in a single bucket other than its metadata's.
That case does work. A MinIO run with the metadata in one bucket and
`write.data.path` in another requested each data file's credential for its own
bucket and location, and called `getPolicyLocations` once per bucket. Both
places should describe that case instead, since vendors read the Javadoc as the
contract.
### 3. Design doc on blocking calls
`s3-credential-provider-design.md` ("Location-scoped credentials on the
Iceberg path") says another bucket's locations are fetched inside an async
storage call, "so the JVM call runs in `tokio::task::block_in_place`". Since
fe0384671 every fetch, including refreshes, goes through `run_blocking` in
`iceberg_location_scoped.rs`. It uses `block_in_place` only on a multi-thread
runtime and otherwise runs the call directly. `block_in_place` panics on a
current-thread runtime, which is where `AbortOnDrop` deletes a failed write's
files, and a 403 on one of those deletes refreshes the locations. The doc
should describe `run_blocking` and that reason, so the invariant is not lost.
### 4. End-to-end tests
`CometS3CredentialBridgeSuite` (a manual MinIO suite) covers a native
Iceberg read through a location-scoped provider. Writes and other buckets are
covered only by Rust tests with fake storages. So nothing drives the real
OpenDAL storage, the derived bridges (`CometS3CredentialBridge::for_location`),
or a second bucket's `getPolicyLocations` end to end. Two cases would close
that:
- A native write that asserts `CometIcebergWriteExec` ran and that each
location got a WRITE-mode credential request.
- A read of a table whose metadata is in `scopedBucket` and whose
`write.data.path` is in `testBucketName`. It should assert each data file's
bucket and location, and one `getPolicyLocations` call per bucket.
`MinioLocationScopedCredentialProvider` would need to record the mode and
bucket along with the path. Both cases pass when run alone against #6478.
One thing to watch: a registration keeps its locations while any of its
cached `FileIO`s is alive. So a test that installs new locations after an
earlier test already read through the same catalog still sees the old list, and
its reads route to `/`. Install the locations before the catalog's first read,
or give the new tests their own catalog.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]