Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
1e818b0
feat: read and write Iceberg tables on HDFS via iceberg-rust's hdfs-n…
mixermt Sep 13, 2026
4d80b05
docs: trim duplicated commentary around the HDFS NameNode resolution
mixermt Sep 13, 2026
9c06f1d
test: skip the Iceberg HDFS suite when MiniDFSCluster cannot start
mixermt Sep 13, 2026
c87dcf9
fix: match hadoop-client-minicluster to the hadoop each Spark profile…
mixermt Sep 13, 2026
5cadaa5
Merge branch 'main' into feat/iceberg-hdfs-native
mixermt Sep 15, 2026
e6668ea
Merge branch 'apache:main' into feat/iceberg-hdfs-native
mixermt Sep 15, 2026
abc4945
Merge branch 'apache:main' into feat/iceberg-hdfs-native
mixermt Sep 19, 2026
19ee804
fix: report task input metrics for fused and shuffled native Iceberg …
mixermt Sep 19, 2026
2742d8e
fix: register the native shuffle input bridge ahead of nested inputs
mixermt Sep 19, 2026
91f0587
feat: show Iceberg planning metrics on the native scan node in the SQ…
mixermt Sep 19, 2026
4b04395
Merge branch 'apache:main' into feat/iceberg-hdfs-native
mixermt Sep 19, 2026
2ef6512
Merge branch 'apache:main' into feat/iceberg-hdfs-native
mixermt Sep 21, 2026
8f22fd1
Merge branch 'apache:main' into feat/iceberg-hdfs-native
mixermt Oct 6, 2026
1e2bcd9
Merge branch 'apache:main' into feat/iceberg-hdfs-native
mixermt Oct 6, 2026
b8ec1fa
style: wrap the resolve-needed fallback reason under the 100-column s…
mixermt Oct 6, 2026
29c92e3
chore: move the iceberg-rust pin to the latest head of apache/iceberg…
mixermt Oct 7, 2026
5f03cf6
Merge branch 'apache:main' into feat/iceberg-hdfs-native
mixermt Oct 11, 2026
c906878
Merge branch 'apache:main' into feat/iceberg-hdfs-native
mixermt Oct 23, 2026
70df82a
feat(iceberg): read Iceberg tables on HDFS natively via iceberg-rust'…
mixermt Oct 23, 2026
1a610fa
fix(iceberg): fall back for name-mapped tables that read list or map …
mixermt Oct 23, 2026
b493b62
Merge branch 'main' into feat/iceberg-hdfs-native
mixermt Oct 23, 2026
12f9d33
fix(iceberg): decline scans that mix storage backends or read HDFS en…
mixermt Oct 23, 2026
5858078
Merge remote-tracking branch 'mixermt/feat/iceberg-hdfs-native' into …
mixermt Oct 24, 2026
d562bc8
Merge branch 'main' into feat/iceberg-hdfs-native
mixermt Oct 24, 2026
bb871c7
Merge branch 'main' into feat/iceberg-hdfs-native
mixermt Oct 27, 2026
17c0872
fix(iceberg): claim HDFS scans only as the Spark user, batch the encr…
mixermt Oct 27, 2026
fd1cf58
feat(iceberg): move the iceberg-rust pin past apache/iceberg-rust#3437
mixermt Oct 27, 2026
c6c7312
chore(iceberg): drop the iceberg-rust#3437 note from the pin comment
mixermt Oct 28, 2026
81d5e29
Merge branch 'main' into feat/iceberg-hdfs-native
mixermt Oct 30, 2026
5ea3ba4
Merge branch 'main' into feat/iceberg-hdfs-native
mixermt Oct 31, 2026
d35847b
test(iceberg): create the HDFS encryption zone with the non-deprecate…
mixermt Oct 31, 2026
ffbdf18
test(iceberg): prove the native HDFS client reads as the Spark user
mixermt Oct 31, 2026
c1e96d3
feat(iceberg): forward the HDFS client settings the session sets to t…
mixermt Oct 31, 2026
a302824
fix(iceberg): keep the HDFS encryption-zone listings per parent, with…
mixermt Oct 31, 2026
4bf68ae
test(iceberg): read a merge-on-read table's position deletes over HDFS
mixermt Oct 31, 2026
94ee908
Merge branch 'main' into feat/iceberg-hdfs-native
mixermt Nov 5, 2026
984e91d
fix(iceberg): read the executors' HADOOP_USER_NAME from the SparkCont…
mixermt Nov 5, 2026
a5d2680
fix(iceberg): keep HDFS encryption-zone listings per table and for an…
mixermt Nov 5, 2026
ce24a0c
Merge remote-tracking branch 'origin/main' into feat/iceberg-hdfs-native
mixermt Nov 6, 2026
04cfc9e
Merge remote-tracking branch 'origin/main' into feat/iceberg-hdfs-native
mixermt Nov 6, 2026
8a3a6b7
fix(iceberg): give getMetadataLocation its Scaladoc back
mixermt Nov 6, 2026
f2c8a73
fix(iceberg): probe encryption zones in linear time and bound what is…
mixermt Nov 6, 2026
ce10dde
feat(iceberg): add spark.comet.scan.icebergNative.hdfs.enabled
mixermt Nov 6, 2026
6bca678
Merge remote-tracking branch 'origin/main' into feat/iceberg-hdfs-native
mixermt Nov 6, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/pr_build_linux.yml
Original file line number Diff line number Diff line change
Expand Up @@ -538,6 +538,7 @@ jobs:
org.apache.comet.CometVariantProjectionSuite
org.apache.spark.sql.CometVariantShreddingSuite
org.apache.comet.CometIcebergNativeSuite
org.apache.comet.CometIcebergHdfsSuite
org.apache.comet.CometBroadcastBuildSideSuite
org.apache.comet.CometIcebergEncryptionSuite
org.apache.comet.CometIcebergRewriteActionSuite
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/pr_build_macos.yml
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,7 @@ jobs:
org.apache.comet.CometVariantProjectionSuite
org.apache.spark.sql.CometVariantShreddingSuite
org.apache.comet.CometIcebergNativeSuite
org.apache.comet.CometIcebergHdfsSuite
org.apache.comet.CometBroadcastBuildSideSuite
org.apache.comet.CometIcebergEncryptionSuite
org.apache.comet.CometIcebergRewriteActionSuite
Expand Down
5 changes: 2 additions & 3 deletions docs/source/contributor-guide/roadmap.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,8 @@ Comet's native Iceberg scans read V3 tables, including encrypted tables ([#4991]
([#5853]). We want to add the remaining V3 features so that these scans don't fall back to Spark: row lineage
metadata columns, column default values, and the new V3 types (`variant`, `geometry`, `geography`, and `unknown`).
The work is tracked in [#3376], and upstream `iceberg-rust` support in [iceberg-rust #2411]. Native Iceberg scans
also don't support HDFS-backed tables today: Comet's native Iceberg storage layer handles only local files, S3 and
S3-compatible stores, GCS, and OSS, and would need an HDFS `StorageFactory` upstream in `iceberg-storage-opendal`.
We're scoping what that work would take.
read HDFS-backed tables through iceberg-rust's experimental `hdfs-native` backend, but fall back to Spark on clusters
with Kerberos, because that backend cannot authenticate with delegation tokens yet.

[#3376]: https://github.com/apache/datafusion-comet/issues/3376
[#4991]: https://github.com/apache/datafusion-comet/pull/4991
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ The 5-minute fallback on the Iceberg path bounds how long reqsign reuses a crede

## Property-bag handling on the Iceberg path

The full unfiltered FileIO property bag crosses JNI as `catalog_properties`. The storage-prefix filter (`s3.`/`gcs.`/`adls.`/`client.`/`opendal.`) is applied native-side in `iceberg_common.rs::build_file_io` immediately before `FileIOBuilder.with_prop`. This means the bridge sees `credentials.uri`, OAuth tokens, and any vendor-custom keys with no parallel field on the operator and no driver-side broadcast. Vendors set their own keys on the catalog config and read them back inside `initialize(Map)`.
The full unfiltered FileIO property bag crosses JNI as `catalog_properties`. The storage-prefix filter (`s3.`/`gcs.`/`adls.`/`client.`/`opendal.`/`hdfs.`/`hadoop.`) is applied native-side in `iceberg_common.rs::build_file_io` immediately before `FileIOBuilder.with_prop`. This means the bridge sees `credentials.uri`, OAuth tokens, and any vendor-custom keys with no parallel field on the operator and no driver-side broadcast. Vendors set their own keys on the catalog config and read them back inside `initialize(Map)`.

`IcebergScanExec` derives a redacting `Debug`, and the `FileIO` cache key's `Debug` omits the property bag, so plan dumps and tracing do not leak it.

Expand Down
4 changes: 2 additions & 2 deletions docs/source/user-guide/latest/datasources.md
Original file line number Diff line number Diff line change
Expand Up @@ -166,8 +166,8 @@ attaches its own worker threads, so a worker that has read from HDFS can crash t
the HDFS read itself, typically while an unrelated query is running.
```

Native Iceberg scans do not support HDFS-backed tables; those scans fall back to Spark. See the
[Iceberg](iceberg.md) page.
Native Iceberg scans read HDFS through a different client, iceberg-rust's `hdfs-native` backend. See
the [Iceberg](iceberg.md#hdfs) page.

### Building Comet with HDFS support

Expand Down
67 changes: 58 additions & 9 deletions docs/source/user-guide/latest/iceberg.md
Original file line number Diff line number Diff line change
Expand Up @@ -138,8 +138,7 @@ safe partial pruning is tracked in [#5883](https://github.com/apache/datafusion-
- Local filesystem
- S3-compatible storage (AWS S3, MinIO)
- Google Cloud Storage (`gs`) and Alibaba Cloud OSS (`oss`)

HDFS-backed tables are not supported by the native Iceberg reader and fall back to Spark.
- HDFS (`hdfs`), experimental (see [HDFS](#hdfs))

### REST Catalog

Expand Down Expand Up @@ -194,6 +193,47 @@ For a custom S3-compatible endpoint, configure the catalog with the endpoint, pa

These `s3.*` storage properties are not specific to the Hive catalog shown here. When `s3.access-key-id` / `s3.secret-access-key` are omitted, credentials come from the standard AWS chain (environment variables, instance profiles, and so on). The region is not auto-detected: when neither the catalog (`client.region` or `s3.region`) nor the executor environment (`AWS_REGION` or `AWS_DEFAULT_REGION`) supplies one, Comet uses `us-east-1`, so set it for AWS buckets in any other region. If your REST catalog vends temporary credentials, the native reader does not consume them automatically, and wiring that requires the credential provider bridge. See Iceberg's [S3 FileIO](https://iceberg.apache.org/docs/latest/aws/#s3-fileio) docs for the full property list, and [S3 Credential Providers](s3-credential-providers.md) for vended or per-request credentials.

### HDFS

The native reader reads HDFS through iceberg-rust's experimental `hdfs-native` backend, a Rust HDFS
client that uses neither libhdfs nor the JVM. The client reads `core-site.xml` and `hdfs-site.xml`
from `$HADOOP_CONF_DIR`, or from `$HADOOP_HOME/etc/hadoop`, on each executor. Of the session's
Hadoop configuration (the driver's XML and `spark.hadoop.*`), Comet forwards the settings the
client reads: `dfs.client.use.datanode.hostname`, `dfs.client.socket-timeout`,
`dfs.client.failover.max.attempts`, `dfs.client.failover.sleep.base.millis` and
`dfs.client.failover.sleep.max.millis` when the session sets them, and the NameNodes of an HA
nameservice. A location such as `hdfs://ns1/warehouse` has no port, and Comet passes the client
the NameNodes that the session's Hadoop configuration declares for `ns1` with
`dfs.ha.namenodes.ns1` and `dfs.namenode.rpc-address.ns1.<id>`. A portless location that the
configuration does not declare is a single NameNode on Hadoop's default port, 8020, as it is to
the JVM client. A catalog property under `hadoop.` sets any client key directly and wins over the
forwarded value. The client does not read the JVM client's other settings, among them short-circuit
and hedged reads, `dfs.client.retry.*`, `ipc.client.*`, `hadoop.rpc.protection` and the
per-nameservice `dfs.client.failover.*.<ns>` keys. Data transfer is encrypted when the NameNode
requires it (`dfs.encrypt.data.transfer`), as for the JVM client.

A scan of HDFS files falls back to Spark when `hadoop.security.authentication` is not `simple`,
because the client cannot authenticate with the delegation tokens that Spark executors use on a
Kerberos cluster. It also falls back when a file location has no port and names a nameservice that
the Hadoop configuration does not declare, or when it has no authority (`hdfs:///path`), and when a
data or delete file lies in an HDFS encryption zone: the client is built without KMS support and
cannot decrypt it. Comet checks that at planning, on clusters whose NameNode advertises a key
provider, with one directory listing per parent of the data directories. A listing is kept per
table for an hour, or for `spark.sql.metadataCacheTTLSeconds` when that is set, so a table
re-created at the same path lists again on its first scan, and a new partition lists its parent
again; a scan that would list more than a million directory entries falls back, and later scans
continue from the listings kept, which hold a million entries at most.

On a cluster without Kerberos, the client connects as `HADOOP_USER_NAME`, or as the executor's
operating system user when that variable is unset, rather than as the Spark user. Comet therefore
claims an HDFS scan only when the executors are known to read as the Spark user: set
`spark.executorEnv.HADOOP_USER_NAME` to the user that submits the job, when the application is
submitted (Comet reads it from the SparkContext's configuration; a value set on a session
afterwards never reaches the executors). In local mode, the JVM's `HADOOP_USER_NAME` or operating
system user must be that user. Otherwise the scan falls back with a reason that names the setting.
To read HDFS tables with Spark's Iceberg reader and keep the native scan for other storage, set
`spark.comet.scan.icebergNative.hdfs.enabled=false`. Writes to HDFS use Iceberg's own writer.

### Current limitations

The following scenarios will fall back to the JVM Iceberg reader:
Expand All @@ -210,6 +250,12 @@ The following scenarios will fall back to the JVM Iceberg reader:
- Queries that use `input_file_name()`, `input_file_block_start()` or `input_file_block_length()`
- Scans whose data or delete files span more than one S3 bucket (the native reader uses one
object-store configuration per scan)
- HDFS scans on a cluster with Kerberos, or whose executors are not known to read as the Spark
user; HDFS file locations that name a nameservice without NameNodes or that have no authority;
and HDFS files in an encryption zone (see [HDFS](#hdfs))
- Scans whose data or delete files are on a different storage backend than the table metadata, for
example a local or REST-served metadata location with `write.data.path` on S3 or HDFS. The native
reader opens every file of a scan through one storage, chosen from the metadata location
- Scans where Iceberg reports an output ordering (for example with
`spark.sql.iceberg.planning.preserve-data-ordering=true`). Spark may then skip the sort above the
scan, and the native reader does not preserve file order
Expand Down Expand Up @@ -793,16 +839,19 @@ the [S3](datasources.md#s3-limitations) and [Azure](datasources.md#azure-limitat

Which entries apply depends on which native paths are enabled:

| Native path | Setting | Default |
| -------------- | ---------------------------------------- | ------- |
| Iceberg scan | `spark.comet.scan.icebergNative.enabled` | `true` |
| Iceberg writer | `spark.comet.write.iceberg.enabled` | `false` |
| Native path | Setting | Default |
| -------------------- | --------------------------------------------- | ------- |
| Iceberg scan | `spark.comet.scan.icebergNative.enabled` | `true` |
| Iceberg scan of HDFS | `spark.comet.scan.icebergNative.hdfs.enabled` | `true` |
| Iceberg writer | `spark.comet.write.iceberg.enabled` | `false` |

### Reads that differ from Spark

- **Data files with neither field ids nor a name mapping.** Spark reads every nested field of such
a file as NULL, and the native scan returns the values stored in the file. A read of only some
of a struct's fields fails instead
- **Data files with neither field ids nor a name mapping.** Neither reader can match such a
file's nested fields to the table's, so a struct's fields read as NULL in both. The containers
differ: Spark's reader returns NULL for an optional struct itself, where the native scan
returns a struct of NULL fields, and an empty list for a list of structs, where the native scan
returns the list with each element's fields NULL
([#6794](https://github.com/apache/datafusion-comet/issues/6794)).
- **`VERSION AS OF` reads of a column renamed after the snapshot.** The renamed column reads as
NULL, and two columns that swapped names return each other's values
Expand Down
Loading
Loading