Follow-up to #24352 / #24354 (and the second instance #24355), from @adriangb's review on #24354.
Problem
The parquet runtime row-group prune path in PushDecoderStreamState maintains state parallel to the arrow-rs push decoder and updates it independently of the decoder's own frontier:
rg_plan: VecDeque<RgPlanEntry> — a DataFusion-side queue of pending row groups, and
- a carried flat
RowSelection over the concatenation of the remaining row groups (arrow-rs side).
Both must stay aligned with the decoder's row-group frontier, but nothing enforces that structurally, and each drift is a silent wrong-results bug:
The #24354 fix also couples rg_plan's correctness to row_group_pruner.is_some() (it syncs only when a pruner exists) — sound today because the pruner is the only consumer that rebuilds from rg_plan, but fragile: the invariant is enforced only where a pruner happens to be present.
Proposed direction
Give the decoder ownership of the drop so the row-group queue and the RowSelection cannot be updated independently. As @adriangb suggested:
-
arrow-rs: add ParquetPushDecoder::retain_row_groups(impl FnMut(usize) -> bool) (or remaining_row_groups() -> impl ExactSizeIterator<Item = usize>) that filters the frontier in place, slicing the RowSelection alongside the row-group queue.
-
DataFusion: replace rg_plan / RgPlanEntry / sync_rg_plan_to_decoder_frontier / advance_rg_plan_to / the Data-arm pop / and the into_builder() + is_at_row_group_boundary() dance with a single
decoder.retain_row_groups(|rg| !pruner.should_prune(&[rg]));
This removes the parallel state entirely, so there is no alignment left to drift — closing the whole family (#24352, #24355) at the root and dropping the pruner.is_some() coupling.
Either repo can host the change; the in-place retain really wants to live in arrow-rs, which owns both the queue and the selection.
cc @alamb @adriangb @hhhizzz
Follow-up to #24352 / #24354 (and the second instance #24355), from @adriangb's review on #24354.
Problem
The parquet runtime row-group prune path in
PushDecoderStreamStatemaintains state parallel to the arrow-rs push decoder and updates it independently of the decoder's own frontier:rg_plan: VecDeque<RgPlanEntry>— a DataFusion-side queue of pending row groups, andRowSelectionover the concatenation of the remaining row groups (arrow-rs side).Both must stay aligned with the decoder's row-group frontier, but nothing enforces that structurally, and each drift is a silent wrong-results bug:
rg_plantrails the decoder by one and a later rebuild re-reads an already-delivered row group. Fixed in fix(parquet): sync rg_plan to decoder frontier — fix wrong TopK results from re-reading already-delivered row groups (#24352) #24354 by syncingrg_planto the frontier viapeek_next_row_group().into_builder().with_row_groups(new_indices)drops row groups without slicing the carriedRowSelectionto match, so selectors intended for a dropped RG are applied to the next surviving one.The #24354 fix also couples
rg_plan's correctness torow_group_pruner.is_some()(it syncs only when a pruner exists) — sound today because the pruner is the only consumer that rebuilds fromrg_plan, but fragile: the invariant is enforced only where a pruner happens to be present.Proposed direction
Give the decoder ownership of the drop so the row-group queue and the
RowSelectioncannot be updated independently. As @adriangb suggested:arrow-rs: add
ParquetPushDecoder::retain_row_groups(impl FnMut(usize) -> bool)(orremaining_row_groups() -> impl ExactSizeIterator<Item = usize>) that filters the frontier in place, slicing theRowSelectionalongside the row-group queue.DataFusion: replace
rg_plan/RgPlanEntry/sync_rg_plan_to_decoder_frontier/advance_rg_plan_to/ theData-arm pop / and theinto_builder()+is_at_row_group_boundary()dance with a singleThis removes the parallel state entirely, so there is no alignment left to drift — closing the whole family (#24352, #24355) at the root and dropping the
pruner.is_some()coupling.Either repo can host the change; the in-place
retainreally wants to live in arrow-rs, which owns both the queue and the selection.cc @alamb @adriangb @hhhizzz