feat: configurable Parquet I/O that preserves page pruning - #3
Draft
peterxcli wants to merge 10 commits into
Draft
feat: configurable Parquet I/O that preserves page pruning#3peterxcli wants to merge 10 commits into
peterxcli wants to merge 10 commits into
Conversation
peterxcli
force-pushed
the
codex/parquet-io-policy-df55
branch
from
September 10, 2026 03:19
172f077 to
a37616d
Compare
|
Thank you for opening this pull request! Reviewer note: cargo-semver-checks reported the current version number is not SemVer-compatible with the changes in this pull request (compared against the base branch). Details |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Which issue does this PR close?
It narrows the I/O-policy work in apache#24393 and does not close an upstream issue. The broader filter-pushdown work in apache#20324 and apache#3463 remains open.
Rationale for this change
Parquet filter pushdown can introduce dependent reads: fetch predicate columns, evaluate the filter, then fetch output columns.
progressive_io=falsefetches the required predicate and output pages together, trading opportunities to avoid reads after row filtering for fewer dependent reader calls. Statistics and page-index pruning still run first.What changes are included in this PR?
datafusion.execution.parquet.progressive_iois a session/table option, also set byParquetSource::with_progressive_io. Its default istrue, including for older serialized plans. ForSELECT name FROM t WHERE age > 30, the two paths are:When
progressive_io=false, the reader fetches the selected filter and output pages for the current row group together, before evaluating the row filter. It submits these ranges throughget_byte_rangeswhen the decoder first requests data from that group. Without a usable page selection and offset index, it reads whole column chunks. The request also includes any ranges the decoder needs for its predicate cache; the decoder can request more data later. If every row is known to match, the reader skips row filtering and omits columns used only by the filter. A runtime filter that can still change cannot establish that guarantee.with_row_group_prefetch(bytes, memory_pool)is a separate execution option, disabled by default and not serialized. Settingprogressive_io=falsealone does not start background I/O:Prefetch uses the same page ranges, preserves scan ordering, and retries speculative I/O errors on demand. It follows upstream's restriction that runtime row-group pruning is disabled while a page selection is active. Its memory budget covers additional compressed reservations; current-reader buffers, decoded buffers, and process memory are outside that metric.
The benchmark report compares implementation
801cb0e52with its upstream base408696966, using the same dependency lockfile, Parquet 59.3.0, input files, and two passes with revision order reversed.On the 2 GiB synthetic files, reading filter and output columns together reduced reader API calls from 512 to 256 without indexes and from 513 to 257 with indexes, with identical requested bytes. With page indexes and matching rows grouped into pages, returning 7 columns used 9–14% less elapsed time than upstream reading filter columns first; returning 1 column used 15–21% less. These comparisons have prefetch disabled. Other workloads varied, including a reversal between passes for scattered matching rows and 7 output columns without indexes. The patch reading filter columns first was 3.4%/9.7% slower than upstream using that same mode on scattered matching rows, 7 output columns, and page indexes. This control warrants profiling.
The ClickBench runner now accepts
--prefetch-bytes. All three reading modes were measured with prefetch enabled and disabled, using a 64 MiB budget so the projected row groups fit. Prefetch completed in every enabled execution. Its timing effect was mixed: Q22 used 3–6% less time with prefetch when filtering after decoding, and Q11 used 7–12% less when reading filter and output columns together. Other cases changed direction between passes. Both modes that filter during decoding remained slower than upstream filtering after decoding for all six queries in both passes, even with prefetch enabled.The charts spell out each reading mode, prefetch setting, measurement pass, and upstream reference. These warm local-file samples do not establish full-dataset, remote-storage, or Spark performance; ClickBench uses a one-million-row file containing two row groups.
What is the testing strategy for this PR?
cargo fmt --all,cargo clippy --all-targets --all-features -- -D warnings,dev/rust_lint.sh, and documentation formatting passed.Are there any user-facing changes?
The I/O policy can be configured through SQL before registering the Parquet table:
Reading filter and output columns together can fetch pages that row filtering would otherwise skip. Filter pushdown and prefetch retain their defaults. The ClickBench runner enables prefetch with
--prefetch-bytes 67108864; its default of zero disables prefetch. The separate Comet POC remains pinned to its earlier implementation commit8411b35ba; it has not been rebuilt against this rebase.