Describe the bug
A native Iceberg scan fails on a hard-wired 10 second per-IO timeout that nothing in Comet can configure:
org.apache.comet.CometNativeException: Iceberg scan error: Unexpected => Failed to read a Parquet file,
source: External: Unexpected => Failure in doing io operation,
source: Unexpected (persistent) at read, context: { timeout: 10 } => io operation timeout reached
The 10 is OpenDAL's default io_timeout. iceberg-storage-opendal applies TimeoutLayer::new() to every FileIO operator with no way to override it:
https://github.com/apache/iceberg-rust/blob/665c64e48e8d33797ecb1a421f327edd9b024879/crates/storage/opendal/src/lib.rs#L394
It bounds one ReadStream::read() chunk, not the whole file, so this is not a large-file problem. The RetryLayer outside it re-sends the same request, so a range that cannot fit in 10s fails identically on all four attempts and surfaces as persistent.
Comet cannot work around it: create_operator and the S3 config parsers are pub(crate), and iceberg::io::Storage is opaque, so BlobHostPromotingS3StorageFactory receives an already-layered Arc<dyn Storage>. Layers stack rather than replace, so adding a longer one on the outside does not help.
Expected behavior
The per-IO timeout should be tunable rather than failing the job.
Additional context
Fix proposed upstream: apache/iceberg-rust#3263, which adds client.io-timeout-ms and hands it to TimeoutLayer::with_io_timeout. Unset keeps the 10s default. Tracked by apache/iceberg-rust#2977; #3179 covers only the write path.
Once it merges, the Comet side is small because "client." is already in STORAGE_PROPERTY_PREFIXES (native/core/src/execution/operators/iceberg_common.rs:39):
- Bump the pinned rev at
native/Cargo.toml:67-68. No native change needed.
- Add a
CometConf entry (.timeConf(TimeUnit.MILLISECONDS)) and inject it at CometScanRule.scala:590 and CometIcebergNativeWrite.scala:687. Both read SQLConf at planning time, so unlike the fs.comet.* Hadoop keys this one would honor a runtime spark.conf.set.
Worth ruling out first, unverified for this report: TimeoutLayer measures wall clock, and Comet shares one tokio runtime per executor sized to spark.executor.cores (jni_api.rs:288-296). CPU-bound shuffle work on the same workers can leave an IO future unpolled past 10s on a healthy connection. Raising COMET_WORKER_THREADS tests that without a code change.
Describe the bug
A native Iceberg scan fails on a hard-wired 10 second per-IO timeout that nothing in Comet can configure:
The
10is OpenDAL's defaultio_timeout.iceberg-storage-opendalappliesTimeoutLayer::new()to everyFileIOoperator with no way to override it:https://github.com/apache/iceberg-rust/blob/665c64e48e8d33797ecb1a421f327edd9b024879/crates/storage/opendal/src/lib.rs#L394
It bounds one
ReadStream::read()chunk, not the whole file, so this is not a large-file problem. TheRetryLayeroutside it re-sends the same request, so a range that cannot fit in 10s fails identically on all four attempts and surfaces as persistent.Comet cannot work around it:
create_operatorand the S3 config parsers arepub(crate), andiceberg::io::Storageis opaque, soBlobHostPromotingS3StorageFactoryreceives an already-layeredArc<dyn Storage>. Layers stack rather than replace, so adding a longer one on the outside does not help.Expected behavior
The per-IO timeout should be tunable rather than failing the job.
Additional context
Fix proposed upstream: apache/iceberg-rust#3263, which adds
client.io-timeout-msand hands it toTimeoutLayer::with_io_timeout. Unset keeps the 10s default. Tracked by apache/iceberg-rust#2977; #3179 covers only the write path.Once it merges, the Comet side is small because
"client."is already inSTORAGE_PROPERTY_PREFIXES(native/core/src/execution/operators/iceberg_common.rs:39):native/Cargo.toml:67-68. No native change needed.CometConfentry (.timeConf(TimeUnit.MILLISECONDS)) and inject it atCometScanRule.scala:590andCometIcebergNativeWrite.scala:687. Both readSQLConfat planning time, so unlike thefs.comet.*Hadoop keys this one would honor a runtimespark.conf.set.Worth ruling out first, unverified for this report:
TimeoutLayermeasures wall clock, and Comet shares one tokio runtime per executor sized tospark.executor.cores(jni_api.rs:288-296). CPU-bound shuffle work on the same workers can leave an IO future unpolled past 10s on a healthy connection. RaisingCOMET_WORKER_THREADStests that without a code change.