feat: serialize Iceberg data source plans - #684
Conversation
0ed3a5a to
f87a81d
Compare
gabotechs
left a comment
There was a problem hiding this comment.
Nice! this is going in the right direction, left some comments that should make things a bit easier.
| #[prost(uint64, tag = "3")] | ||
| partitions: u64, | ||
| #[prost(enumeration = "PartitioningKind", tag = "4")] | ||
| partitioning: i32, | ||
| #[prost(uint64, optional, tag = "5")] |
There was a problem hiding this comment.
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:
You can just rely on upstream functions for [de]serializaing Partitioning
| #[prost(uint64, tag = "7")] | ||
| num_rows: u64, | ||
| #[prost(uint64, tag = "8")] | ||
| total_byte_size: u64, | ||
| } |
There was a problem hiding this comment.
As mentioned in the other message, we should be good without this.
| let codec = IcebergCodec::new( | ||
| Arc::new(FixtureStorageFactory::default()), | ||
| iceberg::Runtime::current(), | ||
| ); | ||
| let mut bytes = Vec::new(); |
There was a problem hiding this comment.
Ideally, the codec should be injected by the testing harness, not manually in each test.
| 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?; | ||
|
|
There was a problem hiding this comment.
Here, we should be using the IcebergTestHarness rather than constructing the context manually.
|
|
||
| #[derive(Debug, Clone, Serialize, Deserialize)] | ||
| struct FixtureStorageFactory { | ||
| pub struct FixtureStorageFactory { |
There was a problem hiding this comment.
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
f87a81d to
6f50e29
Compare
93a76dd to
b9b3316
Compare
6f50e29 to
ae2e0a6
Compare
gabotechs
left a comment
There was a problem hiding this comment.
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
| #[cfg(feature = "integration")] | ||
| _worker_guard: Option<datafusion::common::runtime::JoinSet<()>>, | ||
| } |
There was a problem hiding this comment.
🤔 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.
| #[cfg(feature = "integration")] | ||
| fn iceberg_test_task_count( | ||
| event: DesiredTaskCountEvent, | ||
| ) -> Option<Result<DesiredTaskCountEventResponse>> { | ||
| event |
There was a problem hiding this comment.
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.
| } | ||
|
|
||
| #[cfg(feature = "integration")] | ||
| pub async fn new_distributed() -> Result<Self> { |
There was a problem hiding this comment.
🤔 I can imagine how we'd want the distributed planner to be enabled in every test, whether the test ends up distributing or not.
ae2e0a6 to
d2533c9
Compare
| source.iceberg_file_io.config().props(), | ||
| decoded.iceberg_file_io.config().props() | ||
| ); | ||
| assert!(!source.iceberg_file_io.config().props().is_empty()); |
There was a problem hiding this comment.
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
|
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 |
fab6425
into
datafusion-contrib:iceberg-0.10
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`
Summary
Implements
IcebergCodecso physical plans containingIcebergDataSourcecan 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
FileIOusing 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-icebergcargo clippy -p datafusion-distributed-iceberg --lib -- -D warningscargo fmt --all -- --checkgit diff --check origin/iceberg-0.10...HEAD