1use std::sync::Arc;
18
19use async_trait::async_trait;
20use datatypes::arrow::array::{Array, BinaryArray};
21use datatypes::arrow::compute::concat_batches;
22use datatypes::arrow::record_batch::RecordBatch;
23use futures::{Stream, TryStreamExt};
24use snafu::ResultExt;
25use store_api::storage::{FileId, TimeSeriesRowSelector};
26
27use crate::cache::{
28 CacheStrategy, SelectorResult, SelectorResultKey, SelectorResultValue,
29 selector_result_cache_hit, selector_result_cache_miss,
30};
31use crate::error::{ComputeArrowSnafu, Result};
32use crate::read::read_columns::JsonTargetTypes;
33use crate::read::{
34 Batch, BatchReader, BoxedBatchReader, BoxedRecordBatchStream, timestamp_array_to_i64_slice,
35};
36use crate::sst::parquet::DEFAULT_READ_BATCH_SIZE;
37use crate::sst::parquet::flat_format::{primary_key_column_index, time_index_column_index};
38use crate::sst::parquet::format::{PrimaryKeyArray, primary_key_offsets};
39use crate::sst::parquet::read_columns::ParquetReadColumns;
40use crate::sst::parquet::reader::FlatRowGroupReader;
41
42#[allow(dead_code)]
51pub(crate) struct LastRowReader {
52 reader: BoxedBatchReader,
54 selector: LastRowSelector,
56}
57
58#[allow(dead_code)]
59impl LastRowReader {
60 pub(crate) fn new(reader: BoxedBatchReader) -> Self {
62 Self {
63 reader,
64 selector: LastRowSelector::default(),
65 }
66 }
67
68 pub(crate) async fn next_last_row(&mut self) -> Result<Option<Batch>> {
70 while let Some(batch) = self.reader.next_batch().await? {
71 if let Some(yielded) = self.selector.on_next(batch) {
72 return Ok(Some(yielded));
73 }
74 }
75 Ok(self.selector.finish())
76 }
77}
78
79#[async_trait]
80impl BatchReader for LastRowReader {
81 async fn next_batch(&mut self) -> Result<Option<Batch>> {
82 self.next_last_row().await
83 }
84}
85
86#[derive(Default)]
88pub struct LastRowSelector {
89 last_batch: Option<Batch>,
90}
91
92impl LastRowSelector {
93 pub fn on_next(&mut self, batch: Batch) -> Option<Batch> {
95 if let Some(last) = &self.last_batch {
96 if last.primary_key() == batch.primary_key() {
97 self.last_batch = Some(batch);
99 None
100 } else {
101 debug_assert!(!last.is_empty());
104 let last_row = last.slice(last.num_rows() - 1, 1);
105 self.last_batch = Some(batch);
106 Some(last_row)
107 }
108 } else {
109 self.last_batch = Some(batch);
110 None
111 }
112 }
113
114 pub fn finish(&mut self) -> Option<Batch> {
116 if let Some(last) = self.last_batch.take() {
117 let last_row = last.slice(last.num_rows() - 1, 1);
119 return Some(last_row);
120 }
121 None
122 }
123}
124
125pub(crate) enum FlatRowGroupLastRowCachedReader {
129 Hit(FlatLastRowCacheReader),
131 Miss(FlatRowGroupLastRowReader),
133}
134
135impl FlatRowGroupLastRowCachedReader {
136 pub(crate) fn new(
137 file_id: FileId,
138 row_group_idx: usize,
139 cache_strategy: CacheStrategy,
140 read_cols: &ParquetReadColumns,
141 json_target_types: JsonTargetTypes,
142 reader: FlatRowGroupReader,
143 ) -> Self {
144 let key = SelectorResultKey {
145 file_id,
146 row_group_idx,
147 selector: TimeSeriesRowSelector::LastRow,
148 };
149
150 if let Some(value) = cache_strategy.get_selector_result(&key) {
151 let is_flat = matches!(&value.result, SelectorResult::Flat(_));
152 let schema_matches = value.read_cols == *read_cols;
153 let json_target_types_matches = value.json_target_types == json_target_types;
154 if is_flat && schema_matches && json_target_types_matches {
155 Self::new_hit(value)
156 } else {
157 Self::new_miss(key, read_cols, json_target_types, reader, cache_strategy)
158 }
159 } else {
160 Self::new_miss(key, read_cols, json_target_types, reader, cache_strategy)
161 }
162 }
163
164 pub(crate) async fn next_batch(&mut self) -> Result<Option<RecordBatch>> {
166 match self {
167 FlatRowGroupLastRowCachedReader::Hit(r) => r.next_batch(),
168 FlatRowGroupLastRowCachedReader::Miss(r) => r.next_batch().await,
169 }
170 }
171
172 fn new_hit(value: Arc<SelectorResultValue>) -> Self {
173 selector_result_cache_hit();
174 Self::Hit(FlatLastRowCacheReader { value, idx: 0 })
175 }
176
177 fn new_miss(
178 key: SelectorResultKey,
179 read_cols: &ParquetReadColumns,
180 json_target_types: JsonTargetTypes,
181 reader: FlatRowGroupReader,
182 cache_strategy: CacheStrategy,
183 ) -> Self {
184 selector_result_cache_miss();
185 Self::Miss(FlatRowGroupLastRowReader::new(
186 key,
187 read_cols.clone(),
188 json_target_types,
189 reader,
190 cache_strategy,
191 ))
192 }
193}
194
195pub(crate) struct FlatLastRowCacheReader {
197 value: Arc<SelectorResultValue>,
198 idx: usize,
199}
200
201impl FlatLastRowCacheReader {
202 fn next_batch(&mut self) -> Result<Option<RecordBatch>> {
203 let batches = match &self.value.result {
204 SelectorResult::Flat(batches) => batches,
205 SelectorResult::PrimaryKey(_) => unreachable!(),
206 };
207 if self.idx < batches.len() {
208 let res = Ok(Some(batches[self.idx].clone()));
209 self.idx += 1;
210 res
211 } else {
212 Ok(None)
213 }
214 }
215}
216
217pub(crate) struct BatchBuffer {
219 batches: Vec<RecordBatch>,
220 num_rows: usize,
221}
222
223impl BatchBuffer {
224 fn new() -> Self {
225 Self {
226 batches: Vec::new(),
227 num_rows: 0,
228 }
229 }
230
231 fn is_full(&self) -> bool {
233 self.num_rows >= DEFAULT_READ_BATCH_SIZE
234 }
235
236 fn extend_from_slice(&mut self, batches: &[RecordBatch]) {
238 for batch in batches {
239 self.num_rows += batch.num_rows();
240 }
241 self.batches.extend_from_slice(batches);
242 }
243
244 fn is_empty(&self) -> bool {
246 self.batches.is_empty()
247 }
248
249 fn concat(&mut self) -> Result<RecordBatch> {
251 debug_assert!(!self.batches.is_empty());
252 let schema = self.batches[0].schema();
253 let merged = concat_batches(&schema, &self.batches).context(ComputeArrowSnafu)?;
254 self.batches.clear();
255 self.num_rows = 0;
256 Ok(merged)
257 }
258}
259
260pub(crate) struct FlatRowGroupLastRowReader {
262 key: SelectorResultKey,
263 reader: FlatRowGroupReader,
264 selector: FlatLastTimestampSelector,
265 yielded_batches: Vec<RecordBatch>,
266 cache_strategy: CacheStrategy,
267 read_cols: ParquetReadColumns,
268 json_target_types: JsonTargetTypes,
269 pending: BatchBuffer,
271}
272
273impl FlatRowGroupLastRowReader {
274 fn new(
275 key: SelectorResultKey,
276 read_cols: ParquetReadColumns,
277 json_target_types: JsonTargetTypes,
278 reader: FlatRowGroupReader,
279 cache_strategy: CacheStrategy,
280 ) -> Self {
281 Self {
282 key,
283 reader,
284 selector: FlatLastTimestampSelector::default(),
285 yielded_batches: vec![],
286 cache_strategy,
287 read_cols,
288 json_target_types,
289 pending: BatchBuffer::new(),
290 }
291 }
292
293 fn flush_pending(&mut self) -> Result<Option<RecordBatch>> {
295 if self.pending.is_empty() {
296 return Ok(None);
297 }
298 let merged = self.pending.concat()?;
299 self.yielded_batches.push(merged.clone());
300 Ok(Some(merged))
301 }
302
303 async fn next_batch(&mut self) -> Result<Option<RecordBatch>> {
304 if self.pending.is_full() {
305 return self.flush_pending();
306 }
307
308 while let Some(batch) = self.reader.next_batch().await? {
309 self.selector.on_next(batch, &mut self.pending)?;
310 if self.pending.is_full() {
311 return self.flush_pending();
312 }
313 }
314
315 self.selector.finish(&mut self.pending)?;
317 if !self.pending.is_empty() {
318 let result = self.flush_pending();
319 self.maybe_update_cache();
321 return result;
322 }
323
324 self.maybe_update_cache();
326 Ok(None)
327 }
328
329 fn maybe_update_cache(&mut self) {
330 if self.yielded_batches.is_empty() {
331 return;
332 }
333 let batches = std::mem::take(&mut self.yielded_batches);
334 let value = Arc::new(SelectorResultValue::new_flat(
335 batches,
336 self.read_cols.clone(),
337 self.json_target_types.clone(),
338 ));
339 self.cache_strategy.put_selector_result(self.key, value);
340 }
341}
342
343#[derive(Default)]
348pub(crate) struct FlatLastTimestampSelector {
349 current_key: Option<LastKeyState>,
351}
352
353#[derive(Debug)]
354struct LastKeyState {
355 key: Vec<u8>,
356 last_timestamp: i64,
357 slices: Vec<RecordBatch>,
358}
359
360impl LastKeyState {
361 fn new(key: Vec<u8>, last_timestamp: i64, first_slice: RecordBatch) -> Self {
362 Self {
363 key,
364 last_timestamp,
365 slices: vec![first_slice],
366 }
367 }
368}
369
370impl FlatLastTimestampSelector {
371 pub(crate) fn on_next(
373 &mut self,
374 batch: RecordBatch,
375 output_buffer: &mut BatchBuffer,
376 ) -> Result<()> {
377 if batch.num_rows() == 0 {
378 return Ok(());
379 }
380
381 let num_columns = batch.num_columns();
382 let pk_col_idx = primary_key_column_index(num_columns);
383 let ts_col_idx = time_index_column_index(num_columns);
384
385 let pk_array = batch
386 .column(pk_col_idx)
387 .as_any()
388 .downcast_ref::<PrimaryKeyArray>()
389 .unwrap();
390 let offsets = primary_key_offsets(pk_array)?;
391 if offsets.is_empty() {
392 return Ok(());
393 }
394
395 let ts_values = timestamp_array_to_i64_slice(batch.column(ts_col_idx));
396 for i in 0..offsets.len() - 1 {
397 let range_start = offsets[i];
398 let range_end = offsets[i + 1];
399 let range_key = primary_key_bytes_at(&batch, pk_col_idx, range_start);
400 let range_last_ts = ts_values[range_end - 1];
401 let range_last_ts_start = last_timestamp_start(ts_values, range_start, range_end);
402 let range_slice = batch.slice(range_last_ts_start, range_end - range_last_ts_start);
403
404 match self.current_key.as_mut() {
405 Some(state) if state.key.as_slice() == range_key => {
406 if range_last_ts > state.last_timestamp {
407 state.last_timestamp = range_last_ts;
408 state.slices.clear();
409 state.slices.push(range_slice);
410 } else if range_last_ts == state.last_timestamp {
411 state.slices.push(range_slice);
412 }
413 }
414 Some(_) => {
415 self.flush_current_key(output_buffer);
416 self.current_key = Some(LastKeyState::new(
417 range_key.to_vec(),
418 range_last_ts,
419 range_slice,
420 ));
421 }
422 None => {
423 self.current_key = Some(LastKeyState::new(
424 range_key.to_vec(),
425 range_last_ts,
426 range_slice,
427 ));
428 }
429 }
430 }
431
432 Ok(())
433 }
434
435 pub(crate) fn finish(&mut self, output_buffer: &mut BatchBuffer) -> Result<()> {
437 self.flush_current_key(output_buffer);
438 Ok(())
439 }
440
441 fn flush_current_key(&mut self, output_buffer: &mut BatchBuffer) {
442 let Some(state) = self.current_key.take() else {
443 return;
444 };
445 output_buffer.extend_from_slice(&state.slices);
446 }
447}
448
449pub(crate) struct FlatLastRowReader {
452 stream: BoxedRecordBatchStream,
453 selector: FlatLastTimestampSelector,
454 pending: BatchBuffer,
455}
456
457impl FlatLastRowReader {
458 pub(crate) fn new(stream: BoxedRecordBatchStream) -> Self {
460 Self {
461 stream,
462 selector: FlatLastTimestampSelector::default(),
463 pending: BatchBuffer::new(),
464 }
465 }
466
467 pub(crate) fn into_stream(mut self) -> impl Stream<Item = Result<RecordBatch>> {
469 async_stream::try_stream! {
470 while let Some(batch) = self.stream.try_next().await? {
471 self.selector.on_next(batch, &mut self.pending)?;
472 if self.pending.is_full() {
473 yield self.pending.concat()?;
474 }
475 }
476 self.selector.finish(&mut self.pending)?;
477 if !self.pending.is_empty() {
478 yield self.pending.concat()?;
479 }
480 }
481 }
482}
483
484fn primary_key_bytes_at(batch: &RecordBatch, pk_col_idx: usize, index: usize) -> &[u8] {
486 let pk_dict = batch
487 .column(pk_col_idx)
488 .as_any()
489 .downcast_ref::<PrimaryKeyArray>()
490 .unwrap();
491 let key = pk_dict.keys().value(index);
492 let binary_values = pk_dict
493 .values()
494 .as_any()
495 .downcast_ref::<BinaryArray>()
496 .unwrap();
497 binary_values.value(key as usize)
498}
499
500fn last_timestamp_start(ts_values: &[i64], range_start: usize, range_end: usize) -> usize {
503 debug_assert!(range_start < range_end);
504
505 let last_ts = ts_values[range_end - 1];
506 let mut start = range_end - 1;
507 while start > range_start && ts_values[start - 1] == last_ts {
508 start -= 1;
509 }
510 start
511}
512
513#[cfg(test)]
514mod tests {
515 use std::sync::Arc;
516
517 use api::v1::OpType;
518 use datatypes::arrow::array::{
519 ArrayRef, BinaryDictionaryBuilder, Int64Array, TimestampMillisecondArray, UInt8Array,
520 UInt64Array,
521 };
522 use datatypes::arrow::datatypes::{DataType, Field, Schema, SchemaRef, TimeUnit, UInt32Type};
523 use datatypes::arrow::record_batch::RecordBatch;
524
525 use super::*;
526 use crate::test_util::{VecBatchReader, check_reader_result, new_batch};
527
528 #[tokio::test]
529 async fn test_last_row_one_batch() {
530 let input = [new_batch(
531 b"k1",
532 &[1, 2],
533 &[11, 11],
534 &[OpType::Put, OpType::Put],
535 &[21, 22],
536 )];
537 let reader = VecBatchReader::new(&input);
538 let mut reader = LastRowReader::new(Box::new(reader));
539 check_reader_result(
540 &mut reader,
541 &[new_batch(b"k1", &[2], &[11], &[OpType::Put], &[22])],
542 )
543 .await;
544
545 let input = [new_batch(b"k1", &[1], &[11], &[OpType::Put], &[21])];
547 let reader = VecBatchReader::new(&input);
548 let mut reader = LastRowReader::new(Box::new(reader));
549 check_reader_result(
550 &mut reader,
551 &[new_batch(b"k1", &[1], &[11], &[OpType::Put], &[21])],
552 )
553 .await;
554 }
555
556 #[tokio::test]
557 async fn test_last_row_multi_batch() {
558 let input = [
559 new_batch(
560 b"k1",
561 &[1, 2],
562 &[11, 11],
563 &[OpType::Put, OpType::Put],
564 &[21, 22],
565 ),
566 new_batch(
567 b"k1",
568 &[3, 4],
569 &[11, 11],
570 &[OpType::Put, OpType::Put],
571 &[23, 24],
572 ),
573 new_batch(
574 b"k2",
575 &[1, 2],
576 &[11, 11],
577 &[OpType::Put, OpType::Put],
578 &[31, 32],
579 ),
580 ];
581 let reader = VecBatchReader::new(&input);
582 let mut reader = LastRowReader::new(Box::new(reader));
583 check_reader_result(
584 &mut reader,
585 &[
586 new_batch(b"k1", &[4], &[11], &[OpType::Put], &[24]),
587 new_batch(b"k2", &[2], &[11], &[OpType::Put], &[32]),
588 ],
589 )
590 .await;
591 }
592
593 fn new_flat_batch(primary_keys: &[&[u8]], timestamps: &[i64], fields: &[i64]) -> RecordBatch {
595 let num_rows = timestamps.len();
596 assert_eq!(primary_keys.len(), num_rows);
597 assert_eq!(fields.len(), num_rows);
598
599 let columns: Vec<ArrayRef> = vec![
600 Arc::new(Int64Array::from_iter_values(fields.iter().copied())),
602 Arc::new(TimestampMillisecondArray::from_iter_values(
604 timestamps.iter().copied(),
605 )),
606 {
608 let mut builder = BinaryDictionaryBuilder::<UInt32Type>::new();
609 for &pk in primary_keys {
610 builder.append(pk).unwrap();
611 }
612 Arc::new(builder.finish())
613 },
614 Arc::new(UInt64Array::from_iter_values(vec![1u64; num_rows])),
616 Arc::new(UInt8Array::from_iter_values(vec![1u8; num_rows])),
618 ];
619
620 RecordBatch::try_new(test_flat_schema(), columns).unwrap()
621 }
622
623 fn test_flat_schema() -> SchemaRef {
624 let fields = vec![
625 Field::new("field0", DataType::Int64, false),
626 Field::new(
627 "ts",
628 DataType::Timestamp(TimeUnit::Millisecond, None),
629 false,
630 ),
631 Field::new(
632 "__primary_key",
633 DataType::Dictionary(Box::new(DataType::UInt32), Box::new(DataType::Binary)),
634 false,
635 ),
636 Field::new("__sequence", DataType::UInt64, false),
637 Field::new("__op_type", DataType::UInt8, false),
638 ];
639 Arc::new(Schema::new(fields))
640 }
641
642 fn collect_flat_results(
644 selector: &mut FlatLastTimestampSelector,
645 batches: Vec<RecordBatch>,
646 ) -> Vec<(Vec<u8>, i64)> {
647 let mut output_buffer = BatchBuffer::new();
648 let mut results = Vec::new();
649 for batch in batches {
650 selector.on_next(batch, &mut output_buffer).unwrap();
651 for r in output_buffer.batches.drain(..) {
652 extract_flat_rows(&r, &mut results);
653 }
654 output_buffer.num_rows = 0;
655 }
656 selector.finish(&mut output_buffer).unwrap();
657 for r in output_buffer.batches.drain(..) {
658 extract_flat_rows(&r, &mut results);
659 }
660 results
661 }
662
663 fn extract_flat_rows(batch: &RecordBatch, out: &mut Vec<(Vec<u8>, i64)>) {
665 let ts_col = batch
666 .column(1)
667 .as_any()
668 .downcast_ref::<TimestampMillisecondArray>()
669 .unwrap();
670 let pk_col = batch
671 .column(2)
672 .as_any()
673 .downcast_ref::<PrimaryKeyArray>()
674 .unwrap();
675 let binary_values = pk_col
676 .values()
677 .as_any()
678 .downcast_ref::<BinaryArray>()
679 .unwrap();
680
681 for i in 0..batch.num_rows() {
682 let key_idx = pk_col.keys().value(i);
683 let pk = binary_values.value(key_idx as usize).to_vec();
684 let ts = ts_col.value(i);
685 out.push((pk, ts));
686 }
687 }
688
689 #[test]
690 fn test_flat_single_batch_one_key() {
691 let mut selector = FlatLastTimestampSelector::default();
692 let batch = new_flat_batch(&[b"k1", b"k1", b"k1"], &[1, 2, 3], &[10, 20, 30]);
693 let results = collect_flat_results(&mut selector, vec![batch]);
694 assert_eq!(vec![(b"k1".to_vec(), 3)], results);
695 }
696
697 #[test]
698 fn test_flat_single_batch_multiple_keys() {
699 let mut selector = FlatLastTimestampSelector::default();
700 let batch = new_flat_batch(
701 &[b"k1", b"k1", b"k2", b"k2", b"k3"],
702 &[1, 2, 3, 4, 5],
703 &[10, 20, 30, 40, 50],
704 );
705 let results = collect_flat_results(&mut selector, vec![batch]);
706 assert_eq!(
707 vec![
708 (b"k1".to_vec(), 2),
709 (b"k2".to_vec(), 4),
710 (b"k3".to_vec(), 5),
711 ],
712 results
713 );
714 }
715
716 #[test]
717 fn test_flat_key_spans_batches() {
718 let mut selector = FlatLastTimestampSelector::default();
719 let batches = vec![
720 new_flat_batch(&[b"k1", b"k1"], &[1, 2], &[10, 20]),
721 new_flat_batch(&[b"k1", b"k2"], &[3, 4], &[30, 40]),
722 new_flat_batch(&[b"k2", b"k3"], &[5, 6], &[50, 60]),
723 ];
724 let results = collect_flat_results(&mut selector, batches);
725 assert_eq!(
726 vec![
727 (b"k1".to_vec(), 3),
728 (b"k2".to_vec(), 5),
729 (b"k3".to_vec(), 6),
730 ],
731 results
732 );
733 }
734
735 #[test]
736 fn test_flat_duplicate_last_timestamps() {
737 let mut selector = FlatLastTimestampSelector::default();
738 let batch = new_flat_batch(
740 &[b"k1", b"k1", b"k1", b"k2"],
741 &[1, 3, 3, 5],
742 &[10, 20, 30, 40],
743 );
744 let results = collect_flat_results(&mut selector, vec![batch]);
745 assert_eq!(
746 vec![
747 (b"k1".to_vec(), 3),
748 (b"k1".to_vec(), 3),
749 (b"k2".to_vec(), 5),
750 ],
751 results
752 );
753 }
754
755 #[test]
756 fn test_flat_duplicate_last_timestamps_across_batches() {
757 let mut selector = FlatLastTimestampSelector::default();
758 let batches = vec![
760 new_flat_batch(&[b"k1", b"k1"], &[1, 3], &[10, 20]),
761 new_flat_batch(&[b"k1", b"k2"], &[3, 5], &[30, 40]),
762 ];
763 let results = collect_flat_results(&mut selector, batches);
764 assert_eq!(
765 vec![
766 (b"k1".to_vec(), 3),
767 (b"k1".to_vec(), 3),
768 (b"k2".to_vec(), 5),
769 ],
770 results
771 );
772 }
773
774 #[test]
775 fn test_flat_pending_chain_dropped_by_higher_timestamp() {
776 let mut selector = FlatLastTimestampSelector::default();
777 let batches = vec![
778 new_flat_batch(&[b"k1", b"k1"], &[1, 3], &[10, 20]),
779 new_flat_batch(&[b"k1", b"k1"], &[3, 3], &[21, 22]),
780 new_flat_batch(&[b"k1", b"k1"], &[4, 4], &[23, 24]),
781 ];
782 let results = collect_flat_results(&mut selector, batches);
783 assert_eq!(vec![(b"k1".to_vec(), 4), (b"k1".to_vec(), 4)], results);
784 }
785
786 #[test]
787 fn test_flat_finish_is_one_shot() {
788 let mut selector = FlatLastTimestampSelector::default();
789 let batch = new_flat_batch(&[b"k1", b"k1", b"k2"], &[1, 2, 3], &[10, 20, 30]);
790 let mut output_buffer = BatchBuffer::new();
791
792 selector.on_next(batch, &mut output_buffer).unwrap();
794 let mut pre_finish = Vec::new();
795 for r in output_buffer.batches.drain(..) {
796 extract_flat_rows(&r, &mut pre_finish);
797 }
798 output_buffer.num_rows = 0;
799 assert_eq!(vec![(b"k1".to_vec(), 2)], pre_finish);
800
801 selector.finish(&mut output_buffer).unwrap();
803 assert!(!output_buffer.is_empty());
804 output_buffer.batches.clear();
805 output_buffer.num_rows = 0;
806
807 selector.finish(&mut output_buffer).unwrap();
809 assert!(output_buffer.is_empty());
810 }
811}