fix(parquet): prevent cached Mask reads from crossing unloaded sparse pages - #10735
Conversation
|
I noticed the #10288 has been included in a release, I wondering if need a backport. @alamb |
We can try this -- I think another potential solution would be to implement some sort of fuzz test that tests various selection patterns, etc with pushed down predicates |
Thanks @alamb — filed both, since I think they catch different things: #10747 for the fuzz coverage, #10746 for an end-to-end CI check. Fuzzing first, it's cheaper and lives in this repo. One thing I noticed writing it up: That's also why I don't think fuzzing alone closes it. The parameter space is still authored by hand, and the cache axis was missing because caching and row selection look like unrelated subsystems until they aren't. Cross-module interactions are the hard ones to anticipate up front, which is what #10746 is aimed at — running what users actually run, rather than enumerating what we think matters. #10746 is a correctness check, not a performance one, and needs no new infrastructure: Happy to take #10747 first if nobody else has started on it. |
|
Ready to merge. |
|
Thank you @hhhizzz 🚀 |
… sparse pages (#10766) # Which issue does this PR close? - part of #10738 # Rationale for this change Backport the fix for cached Mask reads crossing unloaded sparse pages (#10733) to the `59_maintenance` branch so it is included in the 59.3.0 release. # What changes are included in this PR? Backport / Cherry-pick: - #10735 # Are these changes tested? By CI # Are there any user-facing changes? No Co-authored-by: Huang Qiwei <qiwei.huang@jsessh.com>
Proposed title
fix(parquet): prevent cached Mask reads from crossing unloaded sparse pages
Which issue does this PR close?
Rationale for this change
The async Parquet reader may combine page pruning, predicate caching, and a Mask-backed row selection.
MaskCursor::next_chunkbounds each chunk by the current loaded row range, but previously retained all trailing skipped rows up to that boundary.For example, if a loaded range starts with one selected row and the rest of the range is skipped, the cursor returned the whole loaded range as
chunk_rows. A fixed-size cached reader can then extend that read beyond the cached segment and into a sparse page that was never loaded, producing:DataFusion predicate pushdown exposed this in TPC-DS queries 66, 75, and 81. Disabling the predicate cache avoided the failure, but the cache is enabled by default and should be safe with both Auto and explicit Mask selection.
What changes are included in this PR?
MaskCursor::positionandMaskChunk::chunk_rows, instead of the scan position at the end of the loaded range.initial_skip, allowingArrayReader::skip_recordsto cross unloaded pages.The selected rows and output batch semantics are unchanged. The fix only avoids decoding trailing rows that are not selected.
Are these changes tested?
Yes.
Focused regression test:
bb1e6cd070: failed with the sparse-column offset error.arrow_readertests: 7 passed, 0 failed.I also ran one-iteration end-to-end correctness smoke tests with DataFusion
52964c2966e855f47b96a15d1aace01baee1f2c1. For validation only, the Arrow default policy was changed to Mask on top of this PR so the workloads could not fall back to Selectors:invalid offset,no matching page, panic, or query failure was found in the candidate logs.These workload runs are correctness smoke tests, not performance claims.
Suggested CI follow-up
cc @alamb
This bug requires an interaction between predicate pushdown, page pruning, selection representation, and predicate caching, which is difficult to cover with isolated reader tests alone. I suggest adding a CI or scheduled benchmark correctness check that runs representative TPC-DS, TPC-H, and ClickBench queries with Parquet predicate pushdown enabled;
Are there any user-facing changes?
No API changes. Async Parquet scans using predicate caching and sparse page reads no longer fail when Auto or explicit Mask selection is used.