Skip to content

feat: serialize Iceberg data source plans - #684

Merged
alexanderbianchi merged 4 commits into
datafusion-contrib:iceberg-0.10from
alexanderbianchi:iceberg/issue-603
Sep 5, 2026
Merged

feat: serialize Iceberg data source plans#684
alexanderbianchi merged 4 commits into
datafusion-contrib:iceberg-0.10from
alexanderbianchi:iceberg/issue-603

Conversation

@alexanderbianchi

@alexanderbianchi alexanderbianchi commented Aug 26, 2026

Copy link
Copy Markdown
Collaborator

Summary

Implements IcebergCodec so physical plans containing IcebergDataSource can be serialized by the coordinator and reconstructed on workers.

The generated protobuf preserves the source schema, work-unit feed, DataFusion partitioning, fetch limit, and storage properties. Decoding rebuilds FileIO using the worker-local storage factory and Iceberg runtime. Local statistics retain the existing current-snapshot behavior; remote sources report unknown statistics rather than serializing planning-only information.

The Iceberg session integration registers both the plan codec and work-unit feed getter. A harness-backed codec round-trip test covers schema, partitioning, fetch, feed identity, storage properties, and remote unknown statistics.

Closes #603.

Scope

Based on iceberg-0.10, which now includes #683's file scan task serialization and #690's unknown-statistics fix.

Per review, distributed harness setup and end-to-end distributed tests are deferred until #605 supplies the real task-count handler. This PR does not add a temporary fixed-count handler or a separate distributed harness mode.

Explicit non-empty storage-property round-trip coverage is tracked in draft #703, based on #700's harness builder. This avoids a hidden test dependency on default fixture options.

Validation

  • cargo test -p datafusion-distributed-iceberg
  • cargo clippy -p datafusion-distributed-iceberg --lib -- -D warnings
  • cargo fmt --all -- --check
  • git diff --check origin/iceberg-0.10...HEAD

@alexanderbianchi
alexanderbianchi changed the base branch from iceberg-0.10 to iceberg/issue-604 August 27, 2026 22:40

@gabotechs gabotechs left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Nice! this is going in the right direction, left some comments that should make things a bit easier.

Comment thread iceberg/src/data_source.rs Outdated
Comment thread iceberg/src/data_source.rs Outdated
Comment thread iceberg/src/codec.rs Outdated
Comment on lines +54 to +58
#[prost(uint64, tag = "3")]
partitions: u64,
#[prost(enumeration = "PartitioningKind", tag = "4")]
partitioning: i32,
#[prost(uint64, optional, tag = "5")]

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This proto message design is likely not going to age well. As soon as Range or Hash partitioning are supported, new fields for holding the SplitPoints and the hashing expressions will need to be added, and consistency across fields will not be granted at compile time.

Take NetworkShuffleExec as an example:

https://github.com/datafusion-contrib/datafusion-distributed/blob/main/src/codec/distributed_codec.rs#L427

You can just rely on upstream functions for [de]serializaing Partitioning

Comment thread iceberg/src/codec.rs Outdated
Comment on lines +62 to +66
#[prost(uint64, tag = "7")]
num_rows: u64,
#[prost(uint64, tag = "8")]
total_byte_size: u64,
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

As mentioned in the other message, we should be good without this.

Comment thread iceberg/src/codec.rs Outdated
Comment on lines +212 to +216
let codec = IcebergCodec::new(
Arc::new(FixtureStorageFactory::default()),
iceberg::Runtime::current(),
);
let mut bytes = Vec::new();

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Ideally, the codec should be injected by the testing harness, not manually in each test.

Comment thread iceberg/tests/distributed_read.rs Outdated
Comment on lines +23 to +33
let (mut ctx, _guard, _) = start_localhost_context(2, build_worker_state).await;
ctx.set_iceberg_integration(integration_options());
ctx.set_distributed_desired_task_count_handler(iceberg_test_task_count);
ctx.sql(&format!(
"CREATE EXTERNAL TABLE taxi STORED AS ICEBERG \
LOCATION '{FIXTURE_URI}/metadata/v1.metadata.json'"
))
.await?
.collect()
.await?;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Here, we should be using the IcebergTestHarness rather than constructing the context manually.

Comment thread iceberg/src/test_utils/harness.rs Outdated

#[derive(Debug, Clone, Serialize, Deserialize)]
struct FixtureStorageFactory {
pub struct FixtureStorageFactory {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I see this was made public so that the integration tests can use it, but the integration tests should be using the IcebergTestHarness itself instead.

The main purpose of the IcebergTestHarness is to reduce all the boilerplate and preparation code from each individual test. If you see yourself needing to prepare a DataFusion SessionContext manually inside a test, it means that the harness should instead be used.

This means that maybe you need to extend a bit the harness to hold more functionality, like spawning a fleet of localhost workers while enriching the context with distributed extensions for example. This is fine, it's part of the expected natural evolution of IcebergTestHarness

@gabotechs gabotechs left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I think it's worth deferring changes to the test harness until we ship #605. I see the new functions in the harness are only used by one small test, so I think we can just remove it from this PR, and add a bunch of more tests in an follow up once we get the DesiredTaskCountHandler in place.

The rest looks pretty good! so +1

Comment thread iceberg/src/test_utils/harness.rs Outdated
Comment on lines 40 to 42
#[cfg(feature = "integration")]
_worker_guard: Option<datafusion::common::runtime::JoinSet<()>>,
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

🤔 all the feature checks in this file are getting a bit hard to manage. Probably the whole harness should be hidden behind the integration flag, not just pieces of it.

Comment thread iceberg/src/test_utils/harness.rs Outdated
Comment on lines +111 to +115
#[cfg(feature = "integration")]
fn iceberg_test_task_count(
event: DesiredTaskCountEvent,
) -> Option<Result<DesiredTaskCountEventResponse>> {
event

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I can imagine how this will disappear in the near future:

After #605, we'd have one of these handlers that has a configured "bytes_per_partition"-like setting that we can set in the IcebergConfig to something unreasonably low for forcing distribution.

Comment thread iceberg/src/test_utils/harness.rs Outdated
}

#[cfg(feature = "integration")]
pub async fn new_distributed() -> Result<Self> {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

🤔 I can imagine how we'd want the distributed planner to be enabled in every test, whether the test ends up distributing or not.

@alexanderbianchi
alexanderbianchi changed the base branch from iceberg/issue-604 to iceberg-0.10 September 5, 2026 16:44
Comment thread iceberg/src/codec.rs Outdated
source.iceberg_file_io.config().props(),
decoded.iceberg_file_io.config().props()
);
assert!(!source.iceberg_file_io.config().props().is_empty());

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

This line depends on the update to the harness which is a really tricky link, I don't like that and would rather remove it for now and wait for #700

@alexanderbianchi

alexanderbianchi commented Sep 5, 2026

Copy link
Copy Markdown
Collaborator Author

Originally there were some conflicts inbound with #687 - i've stripped this PR to be more focused (good practice anyways) and @sandugood should have an easier time now. I do want to make sure a couple things don't fall through the cracks so I opened
#702
#703
Which build off #700 to add test coverage for this PR and 687

@alexanderbianchi
alexanderbianchi merged commit fab6425 into datafusion-contrib:iceberg-0.10 Sep 5, 2026
31 of 32 checks passed
alexanderbianchi added a commit that referenced this pull request Sep 8, 2026
Extends `roundtrips_data_source_plan`, the existing codec test from
#684, with two explicit storage properties supplied through #700's
harness builder. Checks their decoded values, including a
quote-containing value that exercises SQL literal escaping, while
retaining the existing schema, partitioning, fetch, feed, property-map,
and statistics assertions.

The diff against #700 remains 15 changed lines in
`iceberg/src/codec.rs`, with no new harness methods or test functions.

Stacked on #700, now rebased onto `iceberg-0.10` at `6507bf8` after #687
merged. Only #700 is a prerequisite.

Validation:
- `cargo test -p datafusion-distributed-iceberg --locked` — 90 tests
passed, including the codec roundtrip test and doctest
- `cargo clippy -p datafusion-distributed-iceberg --tests --locked -- -D
warnings`
- `cargo fmt --all -- --check`
- `git diff --check`
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants