Skip to content

Construct manifest fetch futures lazily during scans - #421

Merged
JanKaul merged 2 commits into
JanKaul:mainfrom
Embucket:upstream-lazy-manifest-datafiles
Oct 19, 2026
Merged

JanKaul merged 2 commits into
JanKaul:mainfrom
Embucket:upstream-lazy-manifest-datafiles

Conversation

@osipovartem

Copy link
Copy Markdown
Contributor

Summary

Table::datafiles currently builds a Vec of one fetch/decode future per selected manifest before passing it to a bounded stream. That eagerly clones every manifest URI and Arc<ObjectStore> and allocates O(manifest-count) extra state during scan setup, even though only concurrent_manifest_ops() requests can run at once.

This change maps manifest entries lazily into futures as the stream is polled. It keeps the existing I/O, ordering, deleted-entry filtering, sequence handling, and bounded concurrency unchanged. The public filter behavior (including short or empty masks) is preserved. The tracing span is entered manually because #[instrument] cannot return a stream borrowing manifests.

Validation

  • cargo +1.95.0 test -q -p iceberg-rust --lib: 185 passed, 2060 ignored.
  • cargo +1.95.0 test -p datafusion_iceberg --test integration_df_filters: 18 passed, including partitioned file pruning.
  • cargo +1.95.0 clippy -p iceberg-rust --lib --tests -- -D warnings, cargo +1.95.0 fmt --all -- --check, and git diff --check passed.
  • Independent read-only review approved after confirming the original public filter contract is preserved.

The analogous change in Embucket/iceberg-rust#116 measured a large improvement for constructing a stream with 100k manifests in a local debug microbenchmark. That measurement covers setup only, not manifest I/O or end-to-end query latency.

@JanKaul JanKaul left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Great improvement. Thanks for the PR. I left a small comment regarding the instrument call. I would prefer it to leave it as a macro. It should still compile.

Comment thread iceberg-rust/src/table/mod.rs Outdated
Comment on lines +311 to +317
let span = tracing::debug_span!(
"iceberg_rust::table::datafiles",
manifest_count = manifests.len(),
filter_provided = filter.is_some(),
sequence_range = ?sequence_number_range
);
let _guard = span.enter();

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

What's the motivation to move the instrumentation? I would like to keep the coding style with the macro. Does this have any benefits.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Good point; moving the instrumentation has no benefit for this change. I restored #[instrument] in 62d9829 and kept the lazy manifest futures. I also skipped the full filter vector in tracing and added a test that catches eager per-manifest store clones.

@JanKaul

JanKaul commented Oct 19, 2026

Copy link
Copy Markdown
Owner

Thanks for the PR! Looks good to me

@JanKaul
JanKaul merged commit 691174d into JanKaul:main Oct 19, 2026
2 checks passed
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