Skip to content

Commit aac969d

Browse files
nathanb9alamb
andauthored
ParquetPushDecoder API to clear all buffered ranges (#9624)
## Which issue does this PR close? - Closes #8676 ## Rationale for this change `ParquetPushDecoder` clears exact requested ranges, but larger speculative pushed ranges can remain buffered in `PushBuffers`. This adds a way for callers to explicitly release non exact ranges ## What changes are included in this PR? This adds `release_all_ranges()`, which clears all byte ranges still staged in the decoder's internal `PushBuffers` ## Are these changes tested? Kinda tested. Tests added to verify the buffer is empty and verified clearing does not interrupt the rowgroup reader ## Are there any user-facing changes? Yes,this adds a new public `release_all_ranges()` API on `ParquetPushDecoder` --------- Co-authored-by: Andrew Lamb <andrew@nerdnetworks.org>
1 parent 86cab0c commit aac969d

4 files changed

Lines changed: 89 additions & 0 deletions

File tree

parquet/src/arrow/push_decoder/mod.rs

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -365,6 +365,15 @@ impl ParquetPushDecoder {
365365
pub fn buffered_bytes(&self) -> u64 {
366366
self.state.buffered_bytes()
367367
}
368+
369+
/// Clear any staged byte ranges currently buffered for future decode work.
370+
///
371+
/// This clears byte ranges still owned by the decoder's internal
372+
/// `PushBuffers`. It does not affect any data that has already been handed
373+
/// off to an active [`ParquetRecordBatchReader`].
374+
pub fn clear_all_ranges(&mut self) {
375+
self.state.clear_all_ranges();
376+
}
368377
}
369378

370379
/// Internal state machine for the [`ParquetPushDecoder`]
@@ -573,6 +582,20 @@ impl ParquetDecoderState {
573582
ParquetDecoderState::Finished => 0,
574583
}
575584
}
585+
586+
/// Clear any staged ranges currently buffered in the decoder.
587+
fn clear_all_ranges(&mut self) {
588+
match self {
589+
ParquetDecoderState::ReadingRowGroup {
590+
remaining_row_groups,
591+
} => remaining_row_groups.clear_all_ranges(),
592+
ParquetDecoderState::DecodingRowGroup {
593+
record_batch_reader: _,
594+
remaining_row_groups,
595+
} => remaining_row_groups.clear_all_ranges(),
596+
ParquetDecoderState::Finished => {}
597+
}
598+
}
576599
}
577600

578601
#[cfg(test)]
@@ -665,6 +688,55 @@ mod test {
665688
assert_eq!(all_output, *TEST_BATCH);
666689
}
667690

691+
/// Releasing staged ranges should free speculative buffers without affecting
692+
/// the active row group reader.
693+
#[test]
694+
fn test_decoder_clear_all_ranges() {
695+
let mut decoder = ParquetPushDecoderBuilder::try_new_decoder(test_file_parquet_metadata())
696+
.unwrap()
697+
.with_batch_size(100)
698+
.build()
699+
.unwrap();
700+
701+
decoder
702+
.push_range(test_file_range(), TEST_FILE_DATA.clone())
703+
.unwrap();
704+
assert_eq!(decoder.buffered_bytes(), test_file_len());
705+
706+
// The current row group reader is built from the prefetched bytes, but
707+
// the speculative full-file range remains staged in the decoder.
708+
let batch1 = expect_data(decoder.try_decode());
709+
assert_eq!(batch1, TEST_BATCH.slice(0, 100));
710+
assert_eq!(decoder.buffered_bytes(), test_file_len());
711+
712+
// All of the buffer is released
713+
decoder.clear_all_ranges();
714+
assert_eq!(decoder.buffered_bytes(), 0);
715+
716+
// The active reader still owns the current row group's bytes, so it can
717+
// continue decoding without consulting PushBuffers.
718+
let batch2 = expect_data(decoder.try_decode());
719+
assert_eq!(batch2, TEST_BATCH.slice(100, 100));
720+
assert_eq!(decoder.buffered_bytes(), 0);
721+
722+
// Moving to the next row group now requires the decoder to ask for data
723+
// again because the staged speculative ranges were released.
724+
let ranges = expect_needs_data(decoder.try_decode());
725+
let num_bytes_requested: u64 = ranges.iter().map(|r| r.end - r.start).sum();
726+
push_ranges_to_decoder(&mut decoder, ranges);
727+
assert_eq!(decoder.buffered_bytes(), num_bytes_requested);
728+
729+
let batch3 = expect_data(decoder.try_decode());
730+
assert_eq!(batch3, TEST_BATCH.slice(200, 100));
731+
assert_eq!(decoder.buffered_bytes(), 0);
732+
733+
let batch4 = expect_data(decoder.try_decode());
734+
assert_eq!(batch4, TEST_BATCH.slice(300, 100));
735+
assert_eq!(decoder.buffered_bytes(), 0);
736+
737+
expect_finished(decoder.try_decode());
738+
}
739+
668740
/// Decode the entire file incrementally, simulating partial reads
669741
#[test]
670742
fn test_decoder_partial() {

parquet/src/arrow/push_decoder/reader_builder/mod.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,11 @@ impl RowGroupReaderBuilder {
212212
self.buffers.buffered_bytes()
213213
}
214214

215+
/// Clear any staged ranges currently buffered for future decode work.
216+
pub fn clear_all_ranges(&mut self) {
217+
self.buffers.clear_all_ranges();
218+
}
219+
215220
/// take the current state, leaving None in its place.
216221
///
217222
/// Returns an error if there the state wasn't put back after the previous

parquet/src/arrow/push_decoder/remaining.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,11 @@ impl RemainingRowGroups {
7070
self.row_group_reader_builder.buffered_bytes()
7171
}
7272

73+
/// Clear any staged ranges currently buffered for future decode work
74+
pub fn clear_all_ranges(&mut self) {
75+
self.row_group_reader_builder.clear_all_ranges();
76+
}
77+
7378
/// returns [`ParquetRecordBatchReader`] suitable for reading the next
7479
/// group of rows from the Parquet data, or the list of data ranges still
7580
/// needed to proceed

parquet/src/util/push_buffers.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,13 @@ impl PushBuffers {
154154
self.ranges = new_ranges;
155155
self.buffers = new_buffers;
156156
}
157+
158+
/// Clear all buffered ranges and their corresponding data
159+
#[cfg(feature = "arrow")]
160+
pub fn clear_all_ranges(&mut self) {
161+
self.ranges.clear();
162+
self.buffers.clear();
163+
}
157164
}
158165

159166
impl Length for PushBuffers {

0 commit comments

Comments
 (0)