Skip to content

test(parquet): add I/O access tests for parquet pruning, filter pushdown and TopK - #27022

Merged
xudong963 merged 17 commits into
apache:mainfrom
alamb:alamb/parquet-io-pruning-tests
Nov 6, 2026
Merged

xudong963 merged 17 commits into
apache:mainfrom
alamb:alamb/parquet-io-pruning-tests

Conversation

@alamb

@alamb alamb commented Nov 2, 2026 •

Copy link
Copy Markdown
Contributor

Which issue does this PR close?

Rationale for this change

We have several issues / PRs open that contemplate changing the I/O pattern such as

I want to have a test that clearly shows how those proposals effect the scan

What changes are included in this PR?

Add some new tests that show the Parquet I/O patterns. For example I think this is quite clear now the difference in I/O between filter pushdown and not

/// Without filter pushdown, the scan fetches every column of every row group
#[tokio::test]
async fn test_filter_pushdown_io_disabled() {
    let test = filter_pushdown_test(false).await;
    assert_snapshot!(test.query("SELECT * FROM t WHERE val = 7").await, @r"
    --- Query: SELECT * FROM t WHERE val = 7 (1 rows) ---
    Request 1
        Read Parquet footer (offsets 0..1402)
    Request 2
        Read dictionary page and data page 0 for column 'id' (rg 0) (offsets 4..51)
        Read dictionary page and data page 0 for column 'val' (rg 0) (offsets 51..98)
    Request 3
        Read dictionary page and data page 0 for column 'id' (rg 1) (offsets 98..145)
        Read dictionary page and data page 0 for column 'val' (rg 1) (offsets 145..192)
    Request 4
        Read dictionary page and data page 0 for column 'id' (rg 2) (offsets 192..239)
        Read dictionary page and data page 0 for column 'val' (rg 2) (offsets 239..286)
    Produce batch (1 rows)
    ");
}

/// **With** filter pushdown, the scan first fetches and evaluates the filter
/// column (`val`) for each row group, and then fetches the remaining columns
/// (`id`) only for the row groups with matching rows (row group 1).
#[tokio::test]
async fn test_filter_pushdown_io_enabled() {
    let test = filter_pushdown_test(true).await;
    assert_snapshot!(test.query("SELECT * FROM t WHERE val = 7").await, @r"
    --- Query: SELECT * FROM t WHERE val = 7 (1 rows) ---
    Request 1
        Read Parquet footer (offsets 0..1402)
    Request 2
        Read dictionary page and data page 0 for column 'val' (rg 0) (offsets 51..98)
    Request 3
        Read dictionary page and data page 0 for column 'val' (rg 1) (offsets 145..192)
    Request 4
        Read dictionary page for column 'id' (rg 1) (offsets 98..124)
        Read data page 0 for column 'id' (rg 1) (offsets 124..145)
    Produce batch (1 rows)
    Request 5
        Read dictionary page and data page 0 for column 'val' (rg 2) (offsets 239..286)
    ");
}

What is the testing strategy for this PR?

CI

Are there any user-facing changes?

No

mohitgurav20 and others added 8 commits October 31, 2026 12:54
…ng baseline

- Add InstrumentedObjectStore that correlates requested byte ranges to Parquet file structures (footers, page indexes, bloom filters, data pages)

- Add test_parquet_bloom_filter_io_baseline using public SessionContext SQL APIs

- Use insta snapshot to capture and compare the storage I/O trace

- Documents baseline on main where all candidate column Bloom filters are loaded upfront, establishing the target diff for incremental pruning (apache#25811)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Split the Bloom filter I/O test into reusable setup code in
tests/parquet/io/mod.rs and the test itself in
tests/parquet/io/bloom_filter_io.rs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ushdown and TopK

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions github-actions Bot added the core Core DataFusion crate label Nov 2, 2026
@alamb alamb changed the title test(parquet): add I/O tracking tests for row group pruning, filter pushdown and TopK test(parquet): add I/O acess tests for row group pruning, filter pushdown and TopK Nov 4, 2026
@alamb alamb changed the title test(parquet): add I/O acess tests for row group pruning, filter pushdown and TopK test(parquet): add I/O acess tests for parquet pruning, filter pushdown and TopK Nov 4, 2026
@alamb
alamb marked this pull request as ready for review November 4, 2026 20:58
@alamb

alamb commented Nov 4, 2026

Copy link
Copy Markdown
Contributor Author

@adriangb @Dandandan @zhuqi-lucas and @xudong963 -- I wonder what you think about this style test to illustrate the IO patterns in our existing parquet reader?

I am hoping that as we improve the patterns we'll be able to use this style test to more easily document and verify the behavior

@adriangb adriangb left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I like the idea a lot!

I wonder if there's isn't a more general version of this, e.g. if we added tracing as a dev-dep and sprinkled tracing::debug! around in important places so we could track planning and other operations as well.

@zhuqi-lucas zhuqi-lucas left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Nice harness, this makes the I/O differences very easy to see. A few small things, none blocking:

  1. The test files are small enough that "Read Parquet footer" is the whole file (offsets 0..1402 / 0..838). In page_pruning_io the page index is clearly used (only page 1 is read for id = 5) but never shows up as its own request, while bloom_filter_io shows the OffsetIndex as a separate one. A short note (or a file where the footer read does not cover everything) would help readers not assume the page index is free.
  2. describe_range returns on the first overlapping column chunk, so a coalesced request spanning several chunks would only show the first one.
  3. record_request and record_range take the lock separately, so with more than one partition the Request N lines could interleave with another request's reads. Fine with target_partitions = 1 today.

Nits: "acess" in the title, and double spaces in the topk_io.rs docs ("TopK filter is", "which proves").

//!
//! ```text
//! row group 0
//! ┌──────┬───────┐

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.

I do like ascii art

Read Bloom filter for column 'b' (rg 0) (offsets 145..192)
Read data page for column 'a' (rg 0) (offsets 4..51)
Read data page for column 'b' (rg 0) (offsets 51..98)
Request 1

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.

I think showing the distinct requests is what is important as that is the actual IO that is generated

let test = filter_pushdown_test(true).await;
assert_snapshot!(test.query("SELECT * FROM t WHERE val = 7").await, @r"
--- Query: SELECT * FROM t WHERE val = 7 (1 rows) ---
Request 1

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.

I do love seeing this difference

description: String,
}

/// Byte ranges of one column chunk:

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.

this is infrastructure to handle tracking what byte ranges mean

@alamb

alamb commented Nov 5, 2026

Copy link
Copy Markdown
Contributor Author

I like the idea a lot!

I wonder if there's isn't a more general version of this, e.g. if we added tracing as a dev-dep and sprinkled tracing::debug! around in important places so we could track planning and other operations as well.

I reviewed

It is a great idea -- I don't like the use of the tracing crate, but otherwise 👌

@alamb alamb changed the title test(parquet): add I/O acess tests for parquet pruning, filter pushdown and TopK test(parquet): add I/O access tests for parquet pruning, filter pushdown and TopK Nov 5, 2026
@alamb

alamb commented Nov 5, 2026

Copy link
Copy Markdown
Contributor Author
  1. The test files are small enough that "Read Parquet footer" is the whole file (offsets 0..1402 / 0..838). In page_pruning_io the page index is clearly used (only page 1 is read for id = 5) but never shows up as its own request, while bloom_filter_io shows the OffsetIndex as a separate one. A short note (or a file where the footer read does not cover everything) would help readers not assume the page index is free.

This is an excellent point -- I actually adjusted the test so that it sets the metadata hint so that it only covers the metadata and not also the bloom filters and offset indexes

  1. describe_range returns on the first overlapping column chunk, so a coalesced request spanning several chunks would only show the first one.

Fixex

  1. record_request and record_range take the lock separately, so with more than one partition the Request N lines could interleave with another request's reads. Fine with target_partitions = 1 today.

Fixex

Nits: "acess" in the title, and double spaces in the topk_io.rs docs ("TopK filter is", "which proves").

Fixed

@alamb alamb left a comment

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.

Thanks everyone

@alamb
alamb added this pull request to the merge queue Nov 5, 2026
@alamb

alamb commented Nov 5, 2026 •

Copy link
Copy Markdown
Contributor Author

I'll merge this in and hopefully we can keep iterating to make the tests easier to understand / cover more

@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Nov 5, 2026
@xudong963
xudong963 added this pull request to the merge queue Nov 6, 2026
Merged via the queue into apache:main with commit 90b55e3 Nov 6, 2026
44 checks passed
adriangb added a commit to adriangb/datafusion that referenced this pull request Nov 6, 2026
Main has the merged version of apache#27022. It describes multi-structure
reads, sets the metadata size hint to the footer size, and clears the
metadata cache before each query. This merge keeps those changes and
ports them to the tracing-based harness, then regenerates the snapshots.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

core Core DataFusion crate v57.0.0

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants