Skip to content

misc: release parquet row group readers before prefetch - #805

Open
lizhen-0710 wants to merge 1 commit into
bytedance:mainfrom
lizhen-0710:misc-release-parquet-rowgroup-reader
Open

misc: release parquet row group readers before prefetch#805
lizhen-0710 wants to merge 1 commit into
bytedance:mainfrom
lizhen-0710:misc-release-parquet-rowgroup-reader

Conversation

@lizhen-0710

@lizhen-0710 lizhen-0710 commented Jul 29, 2026

Copy link
Copy Markdown
Collaborator

What problem does this PR solve?

Release Parquet column reader state when a row group slides out of the prefetch window. Before loading the next current+prefetch window, the old row group's BufferedInput was erased, but PageReader state such as decompressed page buffers and dictionary string storage could remain attached to column readers until seekToRowGroup() replaced it later.

Type of Change

  • 🐛 Bug fix (non-breaking change which fixes an issue)
  • ✨ New feature (non-breaking change which adds functionality)
  • 🚀 Performance improvement (optimization)
  • ⚠️ Breaking change (fix or feature that would cause existing functionality to change)
  • 🔨 Refactoring (no logic changes)
  • 🔧 Build/CI or Infrastructure changes
  • 📝 Documentation only

Description

That ordering can transiently overlap old row-group decode state with new DirectBufferedInput prefetch allocations. This is most visible for wide dictionary string columns where the dictionary page is held as both decompressed page data and dictionary string backing storage.

Add an explicit recursive release that clears ScanState and resets ParquetData's PageReader before the new row-group window is loaded. This keeps the steady row-group window bounded without changing read semantics; seekToRowGroup() still constructs the reader for the next active row group.

Add a regression test that writes multiple row groups with a large repeated string dictionary and reads them through a constrained memory pool. The test exercises row-group advancement and verifies the scan stays under the memory limit while reading all row groups.

Performance Impact

  • No Impact: This change does not affect the critical path (e.g., build system, doc, error handling).

  • Positive Impact: I have run benchmarks.

    Click to view Benchmark Results
    Paste your google-benchmark or TPC-H results here.
    Before: 10.5s
    After:   8.2s  (+20%)
    
  • Negative Impact: Explained below (e.g., trade-off for correctness).

Release Note

Please describe the changes in this PR

Release Note:

Release Note:
- Fixed a crash in `substr` when input is null.
- optimized `group by` performance by 20%.

Checklist (For Author)

  • I have added/updated unit tests (ctest).
  • I have verified the code with local build (Release/Debug).
  • I have run clang-format / linters.
  • (Optional) I have run Sanitizers (ASAN/TSAN) locally for complex C++ changes.
  • No need to test or manual test.

Breaking Changes

  • No

  • Yes (Description: ...)

    Click to view Breaking Changes
    Breaking Changes:
    - Description of the breaking change.
    - Possible solutions or workarounds.
    - Any other relevant information.
    

Release Parquet column reader state when a row group slides out of the prefetch window. Before loading the next current+prefetch window, the old row group's BufferedInput was erased, but PageReader state such as decompressed page buffers and dictionary string storage could remain attached to column readers until seekToRowGroup() replaced it later.

That ordering can transiently overlap old row-group decode state with new DirectBufferedInput prefetch allocations. This is most visible for wide dictionary string columns where the dictionary page is held as both decompressed page data and dictionary string backing storage.

Add an explicit recursive release that clears ScanState and resets ParquetData's PageReader before the new row-group window is loaded. This keeps the steady row-group window bounded without changing read semantics; seekToRowGroup() still constructs the reader for the next active row group.

Add a regression test that writes multiple row groups with a large repeated string dictionary and reads them through a constrained memory pool. The test exercises row-group advancement and verifies the scan stays under the memory limit while reading all row groups.

Co-authored-by: TRAE CLI <noreply@bytedance.com>
namespace {
void releaseRowGroupReaderRecursive(
dwio::common::SelectiveColumnReader* reader) {
reader->scanState().clear();

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.

will this affect lazy column loading?

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.

The early release should not affect lazy column loading because of the scan/lazy-vector lifecycle.

A LazyVector produced by the Parquet reader is tied to the current reader pass. The downstream operator must consume it in that pass. If the column data is needed, it will be materialized through the lazy loader before the scan reader is advanced to produce another batch. In other words, the Parquet reader is not expected to keep old row-group PageReader state alive for lazy loads after the scan has moved forward.

This change releases the PageReader only when the scan is advancing from row group i - 1 to row group i:

  if (currentGroup >= 1) {
    inputs_.erase(rowGroupIds[currentGroup - 1]);
    reader.releaseRowGroupReader();
  }

At that point, row group i - 1 has already been consumed by the scan pipeline. Then seekToRowGroup(i) creates a new PageReader for the current row group before any data from row group i is read:

reader_ = std::make_unique<PageReader>(std::move(streams_[index]), ...);

So the release only drops page-reader state for the previous row group after its lifecycle is complete. It does not release the PageReader that will be used by lazy vectors produced for the current row group. The intent is only to avoid overlapping old row-group decode state with the next row-group prefetch window.

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.

Thank you for the explanation.

@lizhen-0710
lizhen-0710 requested a review from guhaiyan0221 July 30, 2026 01:47
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