Repository navigation
fix(reader): read the cache in cache-sized batches - #525
Merged
anoop-narang merged 1 commit intoOct 13, 2026
Merged
anoop-narang merged 1 commit into
anoop-narang merged 1 commit into
Conversation
`current_batch_id` indexes stored cache chunks, and both the cache read and the parquet fallback turn that id back into rows by multiplying it by the cache batch size. The reader walked the selection in windows of `datafusion.execution.batch_size` instead, so whenever a caller set that to anything other than the cache's size, batch id N named one range of rows to the reader and a different one to everything that resolved it. The scan then either ran off the end of the row group, or — with a LIMIT stopping it first — returned rows from the wrong offsets and succeeded. The fully-cached path misaligned the same way: a selection mask sized by the session batch size was applied to a chunk sized by the cache's, and `arrow::compute::filter` only rejects a mask longer than its target, so a shorter one silently took the wrong rows. Window by the cached row group's batch size, read once at the one place it is used, and drop the session batch size from the reader config and the row-group planner, which no longer need it. The caller is unaffected: `DataSourceExec` re-splits every source stream to the session batch size, which is what makes ignoring it inside the reader safe. The invariant this replaces was a `debug_assert_eq!`, so release builds carried the misalignment silently rather than failing.
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #525 +/- ##
=======================================
Coverage ? 81.14%
=======================================
Files ? 80
Lines ? 13447
Branches ? 13447
=======================================
Hits ? 10911
Misses ? 2212
Partials ? 324 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
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.
Setting
datafusion.execution.batch_sizeto anything other than the cache's batch size makes liquid-cache scans fail or return the wrong rows.LiquidCacheLocalBuilder::buildpins the session batch size to the cache's, so the default configuration does not hit this. A host that changes it afterwards — per query, or by sizing batches to row width — does.Why
LiquidCacheReaderwalks the row selection in windows of the session batch size, and numbers each window withcurrent_batch_id. But that id is a cache chunk index: the cache read and the parquet fallback both turn it back into rows using the cache batch size. When the two sizes differ, batch id N names one range of rows to the reader and a different range to everything that resolves it.LIMIT, the scan runs off the end of the row group:parquet fallback ended before batch N.LIMITthat stops it first, it returns rows from the wrong offsets and succeeds.arrow::compute::filteronly rejects a mask longer than its target, so a shorter one silently selects the wrong rows.The only guard was a
debug_assert_eq!inLiquidCacheReader::new, so debug builds panic and release builds carry on.The fix
Window the selection by the cached row group's batch size, and drop the session batch size from
LiquidCacheReaderConfigandLiquidRowGroupPlanner, which no longer need it. Callers still get session-sized batches:DataSourceExecre-splits every source stream todatafusion.execution.batch_size, andsession_batch_size_still_bounds_the_batches_the_caller_receiveschecks that.Tests
src/datafusion-local/src/tests/batch_size_alignment.rscovers a narrowed and a widened session batch size,LIMIT, the fully cached path, multiple row groups and multiple partitions, all against a cache at the default 8192.On
mainwith the fix left out, in release (the debug build stops at the assert):With the fix, all 7 pass in debug and release, and
cargo test -p liquid-cache-datafusion -p liquid-cache-datafusion-localpasses.One case is not covered here: page-index pruning that starts the selection partway into a row group. A scan with page-index pruning plus a pushed-down predicate panics in
boolean_buffer_and_thenonmaineven with matching batch sizes, so it cannot be tested until that is fixed; that is a separate issue.