1#[cfg(test)]
16use std::cell::Cell;
17use std::cmp::Ordering;
18use std::collections::BinaryHeap;
19use std::fmt;
20use std::ops::Range;
21use std::sync::Arc;
22use std::time::{Duration, Instant};
23
24use async_stream::try_stream;
25use common_telemetry::debug;
26use datatypes::arrow::array::{
27 Array, ArrayRef, AsArray, BinaryBuilder, Int64Array, UInt32Array, UInt64Array,
28};
29use datatypes::arrow::compute::interleave;
30use datatypes::arrow::datatypes::{ArrowNativeType, BinaryType, DataType, SchemaRef, Utf8Type};
31use datatypes::arrow::error::ArrowError;
32use datatypes::arrow::record_batch::RecordBatch;
33use datatypes::arrow_array::BinaryArray;
34use datatypes::timestamp::timestamp_array_to_primitive;
35use futures::{Stream, TryStreamExt};
36use snafu::{OptionExt, ResultExt, ensure};
37use store_api::storage::SequenceNumber;
38use store_api::storage::consts::{PRIMARY_KEY_COLUMN_NAME, SEQUENCE_COLUMN_NAME};
39use winner_tree::WinnerTree;
40
41use crate::error::{ComputeArrowSnafu, InvalidRecordBatchSnafu, Result};
42use crate::memtable::BoxedRecordBatchIterator;
43use crate::metrics::READ_STAGE_ELAPSED;
44use crate::read::BoxedRecordBatchStream;
45use crate::sst::parquet::flat_format::{
46 primary_key_column_index, sequence_column_index, time_index_column_index,
47};
48use crate::sst::parquet::format::{FIXED_POS_COLUMN_NUM, PrimaryKeyArray};
49
50fn check_interleave_bytes_overflow<T: datatypes::arrow::datatypes::ByteArrayType>(
59 batches: &[(usize, RecordBatch)],
60 col_idx: usize,
61 indices: &[(usize, usize)],
62) -> std::result::Result<(), ArrowError> {
63 let total: usize = batches
66 .iter()
67 .map(|(_, batch)| batch.column(col_idx).as_bytes::<T>().value_data().len())
68 .sum();
69 if T::Offset::from_usize(total).is_some() {
70 return Ok(());
71 }
72 let mut capacity: usize = 0;
74 for &(a, b) in indices {
75 let array = batches[a].1.column(col_idx).as_bytes::<T>();
76 let o = array.value_offsets();
77 let element_len = o[b + 1].as_usize() - o[b].as_usize();
78 capacity += element_len;
79 T::Offset::from_usize(capacity).ok_or(ArrowError::OffsetOverflowError(capacity))?;
80 }
81 Ok(())
82}
83
84fn check_interleave_overflow(
86 batches: &[(usize, RecordBatch)],
87 schema: &SchemaRef,
88 indices: &[(usize, usize)],
89) -> Result<()> {
90 for (col_idx, field) in schema.fields.iter().enumerate() {
91 match field.data_type() {
92 DataType::Utf8 => {
93 check_interleave_bytes_overflow::<Utf8Type>(batches, col_idx, indices)
94 .context(ComputeArrowSnafu)?;
95 }
96 DataType::Binary => {
97 check_interleave_bytes_overflow::<BinaryType>(batches, col_idx, indices)
98 .context(ComputeArrowSnafu)?;
99 }
100 _ => continue,
101 }
102 }
103 Ok(())
104}
105
106fn interleave_primary_key(
108 arrays: &[&dyn Array],
109 indices: &[(usize, usize)],
110) -> std::result::Result<ArrayRef, ArrowError> {
111 if arrays.is_empty() {
112 return Err(ArrowError::InvalidArgumentError(
113 "interleave requires input of at least one array".to_string(),
114 ));
115 }
116
117 let dictionaries = arrays
118 .iter()
119 .map(|array| {
120 let dictionary = array
121 .as_any()
122 .downcast_ref::<PrimaryKeyArray>()
123 .ok_or_else(|| {
124 ArrowError::CastError(format!(
125 "expected Dictionary(UInt32, Binary) primary key, got {}",
126 array.data_type()
127 ))
128 })?;
129 let values = dictionary
130 .values()
131 .as_any()
132 .downcast_ref::<BinaryArray>()
133 .ok_or_else(|| {
134 ArrowError::CastError(format!(
135 "expected Binary primary-key dictionary values, got {}",
136 dictionary.values().data_type()
137 ))
138 })?;
139 Ok((dictionary, values))
140 })
141 .collect::<std::result::Result<Vec<_>, ArrowError>>()?;
142
143 let mut keys = Vec::with_capacity(indices.len());
144 let mut values = BinaryBuilder::with_capacity(indices.len(), 0);
145 let mut previous_primary_key = None;
146 let mut current_key = 0;
147 let mut num_dictionary_values = 0_usize;
148 let mut value_bytes = 0_usize;
149
150 for &(array_idx, row_idx) in indices {
151 let (dictionary, dictionary_values) = dictionaries.get(array_idx).ok_or_else(|| {
152 ArrowError::InvalidArgumentError(format!(
153 "primary-key source index {array_idx} is out of bounds for {} arrays",
154 dictionaries.len()
155 ))
156 })?;
157 if row_idx >= dictionary.len() {
158 return Err(ArrowError::InvalidArgumentError(format!(
159 "primary-key row index {row_idx} is out of bounds for array of length {}",
160 dictionary.len()
161 )));
162 }
163 let source_key = dictionary.key(row_idx).ok_or_else(|| {
164 ArrowError::InvalidArgumentError(
165 "internal primary-key dictionary contains a null key".to_string(),
166 )
167 })?;
168 if dictionary_values.is_null(source_key) {
169 return Err(ArrowError::InvalidArgumentError(
170 "internal primary-key dictionary contains a null dictionary value".to_string(),
171 ));
172 }
173 let primary_key = dictionary_values.value(source_key);
174
175 if previous_primary_key != Some(primary_key) {
176 current_key = u32::try_from(num_dictionary_values)
177 .map_err(|_| ArrowError::DictionaryKeyOverflowError)?;
178 value_bytes = value_bytes.checked_add(primary_key.len()).ok_or_else(|| {
179 ArrowError::ArithmeticOverflow(
180 "primary-key dictionary value length overflow".to_string(),
181 )
182 })?;
183 if value_bytes > i32::MAX as usize {
184 return Err(ArrowError::OffsetOverflowError(value_bytes));
185 }
186 values.append_value(primary_key);
187 num_dictionary_values += 1;
188 previous_primary_key = Some(primary_key);
189 }
190 keys.push(current_key);
191 }
192
193 let dictionary = PrimaryKeyArray::try_new(UInt32Array::from(keys), Arc::new(values.finish()))?;
194 Ok(Arc::new(dictionary))
195}
196
197#[derive(Debug, Copy, Clone, Default)]
199struct BatchCursor {
200 batch_idx: usize,
202 row_idx: usize,
204}
205
206pub trait MergeMetricsReport: Send + Sync {
208 fn report(&self, metrics: &mut MergeMetrics);
210}
211
212#[derive(Default)]
214pub struct MergeMetrics {
215 pub(crate) init_cost: Duration,
217 pub(crate) scan_cost: Duration,
219 pub(crate) num_fetch_by_batches: usize,
221 pub(crate) num_fetch_by_rows: usize,
223 pub(crate) fetch_cost: Duration,
225}
226
227impl fmt::Debug for MergeMetrics {
228 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
229 if self.scan_cost.is_zero() {
230 return write!(f, "{{}}");
231 }
232
233 write!(f, r#"{{"scan_cost":"{:?}""#, self.scan_cost)?;
234
235 if !self.init_cost.is_zero() {
236 write!(f, r#", "init_cost":"{:?}""#, self.init_cost)?;
237 }
238 if self.num_fetch_by_batches > 0 {
239 write!(
240 f,
241 r#", "num_fetch_by_batches":{}"#,
242 self.num_fetch_by_batches
243 )?;
244 }
245 if self.num_fetch_by_rows > 0 {
246 write!(f, r#", "num_fetch_by_rows":{}"#, self.num_fetch_by_rows)?;
247 }
248 if !self.fetch_cost.is_zero() {
249 write!(f, r#", "fetch_cost":"{:?}""#, self.fetch_cost)?;
250 }
251
252 write!(f, "}}")
253 }
254}
255
256impl MergeMetrics {
257 pub(crate) fn merge(&mut self, other: &MergeMetrics) {
259 let MergeMetrics {
260 init_cost,
261 scan_cost,
262 num_fetch_by_batches,
263 num_fetch_by_rows,
264 fetch_cost,
265 } = other;
266
267 self.init_cost += *init_cost;
268 self.scan_cost += *scan_cost;
269 self.num_fetch_by_batches += *num_fetch_by_batches;
270 self.num_fetch_by_rows += *num_fetch_by_rows;
271 self.fetch_cost += *fetch_cost;
272 }
273
274 pub(crate) fn maybe_report(&mut self, reporter: &Option<Arc<dyn MergeMetricsReport>>) {
276 if self.scan_cost.as_millis() > 10
277 && let Some(r) = reporter
278 {
279 r.report(self);
280 }
281 }
282}
283
284#[derive(Debug)]
288pub struct BatchBuilder {
289 schema: SchemaRef,
291
292 primary_key_column_idx: Option<usize>,
294
295 batches: Vec<(usize, RecordBatch)>,
297
298 cursors: Vec<BatchCursor>,
300
301 indices: Vec<(usize, usize)>,
304}
305
306impl BatchBuilder {
307 pub fn new(schema: SchemaRef, stream_count: usize, batch_size: usize) -> Self {
309 let primary_key_column_idx = (schema.fields.len() >= 3)
314 .then(|| primary_key_column_index(schema.fields.len()))
315 .filter(|&column_idx| schema.field(column_idx).name() == PRIMARY_KEY_COLUMN_NAME);
316 Self {
317 schema,
318 primary_key_column_idx,
319 batches: Vec::with_capacity(stream_count * 2),
320 cursors: vec![BatchCursor::default(); stream_count],
321 indices: Vec::with_capacity(batch_size),
322 }
323 }
324
325 pub fn push_batch(&mut self, stream_idx: usize, batch: RecordBatch) {
327 let batch_idx = self.batches.len();
328 self.batches.push((stream_idx, batch));
329 self.cursors[stream_idx] = BatchCursor {
330 batch_idx,
331 row_idx: 0,
332 };
333 }
334
335 pub fn push_row(&mut self, stream_idx: usize) {
337 let cursor = &mut self.cursors[stream_idx];
338 let row_idx = cursor.row_idx;
339 cursor.row_idx += 1;
340 self.indices.push((cursor.batch_idx, row_idx));
341 }
342
343 pub fn len(&self) -> usize {
345 self.indices.len()
346 }
347
348 pub fn is_empty(&self) -> bool {
350 self.indices.is_empty()
351 }
352
353 pub fn schema(&self) -> &SchemaRef {
355 &self.schema
356 }
357
358 pub fn build_record_batch(&mut self) -> Result<Option<RecordBatch>> {
364 if self.is_empty() {
365 return Ok(None);
366 }
367
368 check_interleave_overflow(&self.batches, &self.schema, &self.indices)?;
369
370 let columns = (0..self.schema.fields.len())
371 .map(|column_idx| {
372 let arrays: Vec<_> = self
373 .batches
374 .iter()
375 .map(|(_, batch)| batch.column(column_idx).as_ref())
376 .collect();
377 if Some(column_idx) == self.primary_key_column_idx {
378 interleave_primary_key(&arrays, &self.indices).context(ComputeArrowSnafu)
379 } else {
380 interleave(&arrays, &self.indices).context(ComputeArrowSnafu)
381 }
382 })
383 .collect::<Result<Vec<_>>>()?;
384
385 self.indices.clear();
386
387 self.retain_batches();
393
394 RecordBatch::try_new(Arc::clone(&self.schema), columns)
395 .context(ComputeArrowSnafu)
396 .map(Some)
397 }
398
399 pub fn take_remaining_rows(
402 &mut self,
403 stream_idx: usize,
404 next: Option<RecordBatch>,
405 ) -> RecordBatch {
406 let cursor = &mut self.cursors[stream_idx];
407 let batch = &self.batches[cursor.batch_idx];
408 let output = batch
409 .1
410 .slice(cursor.row_idx, batch.1.num_rows() - cursor.row_idx);
411 cursor.row_idx = batch.1.num_rows();
412
413 if let Some(b) = next {
414 self.push_batch(stream_idx, b);
415 self.retain_batches();
416 }
417
418 output
419 }
420
421 fn retain_batches(&mut self) {
422 let mut batch_idx = 0;
423 let mut retained = 0;
424 self.batches.retain(|(stream_idx, _)| {
425 let stream_cursor = &mut self.cursors[*stream_idx];
426 let retain = stream_cursor.batch_idx == batch_idx;
427 batch_idx += 1;
428
429 if retain {
430 stream_cursor.batch_idx = retained;
431 retained += 1;
432 }
433 retain
434 });
435 }
436}
437
438trait NodeCmp: Eq + Ord {
440 fn is_eof(&self) -> bool;
442
443 fn is_behind(&self, other: &Self) -> bool;
449}
450
451struct MergeAlgo<T: Ord> {
453 hot: WinnerTree<T>,
459 cold: BinaryHeap<T>,
463}
464
465impl<T: NodeCmp> MergeAlgo<T> {
466 fn new(mut nodes: Vec<T>) -> Self {
470 nodes.retain(|node| !node.is_eof());
472 let hot = WinnerTree::with_capacity(nodes.len());
473 let cold = BinaryHeap::from(nodes);
474
475 let mut algo = MergeAlgo { hot, cold };
476 algo.refill_hot();
478
479 algo
480 }
481
482 fn refill_hot(&mut self) {
485 while !self.cold.is_empty() {
486 if let Some(merge_window) = self.hot.peek() {
487 let warmest = self.cold.peek().unwrap();
488 if warmest.is_behind(merge_window) {
489 break;
493 }
494 }
495
496 let warmest = self.cold.pop().unwrap();
497 self.hot.push(warmest);
498 }
499 }
500
501 fn hottest_mut(&mut self) -> Option<&mut T> {
503 self.hot.winner_mut()
504 }
505
506 fn pop_hot_for_batch_transition(&mut self) -> Option<T> {
508 self.hot.pop()
509 }
510
511 fn reheap_after_batch_transition(&mut self, node: T) {
513 if node.is_eof() {
514 self.refill_hot();
515 return;
516 }
517
518 let node_is_cold = self
519 .hot
520 .peek()
521 .is_none_or(|hottest| node.is_behind(hottest));
522 if node_is_cold {
523 self.cold.push(node);
524 } else {
525 self.hot.push(node);
526 }
527 self.refill_hot();
528 }
529
530 fn repair_hot_root(&mut self) {
532 if self.hot.peek().is_some_and(NodeCmp::is_eof) {
533 self.hot.pop();
534 } else {
535 let (winner, second_best) = self.hot.winner_and_second_best();
536 let Some(winner) = winner else {
537 self.refill_hot();
538 return;
539 };
540 let root_is_cold = second_best.is_some_and(|best| winner.is_behind(best));
541 let winner_lost = second_best.is_some_and(|best| winner < best);
545 if root_is_cold {
546 self.cold.push(self.hot.pop().unwrap());
547 } else if winner_lost {
548 self.hot.replay_winner();
549 }
550 }
551
552 self.refill_hot();
553 }
554
555 fn has_rows(&self) -> bool {
557 !self.hot.is_empty()
558 }
559
560 fn can_fetch_batch(&self) -> bool {
562 self.hot.len() == 1
563 }
564}
565
566struct SortColumns {
569 primary_key: PrimaryKeyArray,
570 primary_key_values: BinaryArray,
571 timestamp: Int64Array,
572 sequence: UInt64Array,
573 #[cfg(test)]
574 primary_key_lookups: Cell<usize>,
575}
576
577impl SortColumns {
578 fn try_new(batch: &RecordBatch) -> Result<Self> {
587 let num_columns = batch.num_columns();
588 ensure!(
589 num_columns >= FIXED_POS_COLUMN_NUM,
590 InvalidRecordBatchSnafu {
591 reason: format!(
592 "flat merge batch only has {num_columns} columns, expect at least {FIXED_POS_COLUMN_NUM}"
593 ),
594 }
595 );
596 let primary_key = batch
597 .column(primary_key_column_index(num_columns))
598 .as_any()
599 .downcast_ref::<PrimaryKeyArray>()
600 .with_context(|| InvalidRecordBatchSnafu {
601 reason: format!(
602 "expected a {PRIMARY_KEY_COLUMN_NAME} column of type Dictionary(UInt32, Binary) at index {}",
603 primary_key_column_index(num_columns),
604 ),
605 })?
606 .clone();
607 let primary_key_values = primary_key
608 .values()
609 .as_any()
610 .downcast_ref::<BinaryArray>()
611 .with_context(|| InvalidRecordBatchSnafu {
612 reason: format!(
613 "expected Binary {PRIMARY_KEY_COLUMN_NAME} dictionary values, got {}",
614 primary_key.values().data_type()
615 ),
616 })?
617 .clone();
618 let timestamp = batch.column(time_index_column_index(num_columns));
619 let (timestamp, _unit) =
620 timestamp_array_to_primitive(timestamp).with_context(|| InvalidRecordBatchSnafu {
621 reason: format!(
622 "expected a timestamp time index column at index {}, got {}",
623 time_index_column_index(num_columns),
624 batch
625 .column(time_index_column_index(num_columns))
626 .data_type(),
627 ),
628 })?;
629 let sequence = batch
630 .column(sequence_column_index(num_columns))
631 .as_any()
632 .downcast_ref::<UInt64Array>()
633 .with_context(|| InvalidRecordBatchSnafu {
634 reason: format!(
635 "expected a UInt64 {SEQUENCE_COLUMN_NAME} column at index {}",
636 sequence_column_index(num_columns),
637 ),
638 })?
639 .clone();
640
641 Ok(Self {
642 primary_key,
643 primary_key_values,
644 timestamp,
645 sequence,
646 #[cfg(test)]
647 primary_key_lookups: Cell::new(0),
648 })
649 }
650
651 fn primary_key_at(&self, index: usize) -> &[u8] {
652 let range = self.primary_key_range_at(index);
653 &self.primary_key_values.value_data()[range]
654 }
655
656 fn primary_key_range_at(&self, index: usize) -> Range<usize> {
657 #[cfg(test)]
658 self.primary_key_lookups
659 .set(self.primary_key_lookups.get() + 1);
660 let key = self.primary_key.keys().value(index) as usize;
661 let offsets = self.primary_key_values.value_offsets();
662 offsets[key].as_usize()..offsets[key + 1].as_usize()
663 }
664
665 #[cfg(test)]
666 fn primary_key_lookups(&self) -> usize {
667 self.primary_key_lookups.get()
668 }
669
670 fn timestamp_at(&self, index: usize) -> i64 {
671 self.timestamp.value(index)
672 }
673
674 fn sequence_at(&self, index: usize) -> SequenceNumber {
675 self.sequence.value(index)
676 }
677
678 fn num_rows(&self) -> usize {
679 self.timestamp.len()
680 }
681}
682
683struct RowCursor {
688 offset: usize,
690 primary_key_range: Range<usize>,
692 columns: SortColumns,
694}
695
696impl RowCursor {
697 fn new(columns: SortColumns) -> Self {
698 debug_assert!(columns.num_rows() > 0);
699 let primary_key_range = columns.primary_key_range_at(0);
700
701 Self {
702 offset: 0,
703 primary_key_range,
704 columns,
705 }
706 }
707
708 fn is_finished(&self) -> bool {
709 self.offset >= self.columns.num_rows()
710 }
711
712 fn is_last_row(&self) -> bool {
714 self.offset.checked_add(1) == Some(self.columns.num_rows())
715 }
716
717 fn advance(&mut self) {
718 self.offset += 1;
719 if !self.is_finished() {
720 self.primary_key_range = self.columns.primary_key_range_at(self.offset);
721 }
722 }
723
724 fn first_primary_key(&self) -> &[u8] {
725 &self.columns.primary_key_values.value_data()[self.primary_key_range.clone()]
726 }
727
728 fn first_timestamp(&self) -> i64 {
729 self.columns.timestamp_at(self.offset)
730 }
731
732 fn first_sequence(&self) -> SequenceNumber {
733 self.columns.sequence_at(self.offset)
734 }
735
736 fn last_primary_key(&self) -> &[u8] {
737 self.columns.primary_key_at(self.columns.num_rows() - 1)
738 }
739
740 fn last_timestamp(&self) -> i64 {
741 self.columns.timestamp_at(self.columns.num_rows() - 1)
742 }
743}
744
745impl PartialEq for RowCursor {
746 fn eq(&self, other: &Self) -> bool {
747 self.first_primary_key() == other.first_primary_key()
748 && self.first_timestamp() == other.first_timestamp()
749 && self.first_sequence() == other.first_sequence()
750 }
751}
752
753impl Eq for RowCursor {}
754
755impl PartialOrd for RowCursor {
756 fn partial_cmp(&self, other: &RowCursor) -> Option<Ordering> {
757 Some(self.cmp(other))
758 }
759}
760
761impl Ord for RowCursor {
762 fn cmp(&self, other: &RowCursor) -> Ordering {
764 self.first_primary_key()
765 .cmp(other.first_primary_key())
766 .then_with(|| self.first_timestamp().cmp(&other.first_timestamp()))
767 .then_with(|| other.first_sequence().cmp(&self.first_sequence()))
768 }
769}
770
771pub struct FlatMergeIterator {
781 algo: MergeAlgo<IterNode>,
783 in_progress: BatchBuilder,
785 output_batch: Option<RecordBatch>,
787 batch_size: usize,
791}
792
793impl FlatMergeIterator {
794 pub fn new(
796 schema: SchemaRef,
797 iters: Vec<BoxedRecordBatchIterator>,
798 batch_size: usize,
799 ) -> Result<Self> {
800 let mut in_progress = BatchBuilder::new(schema, iters.len(), batch_size);
801 let mut nodes = Vec::with_capacity(iters.len());
802 for (node_index, iter) in iters.into_iter().enumerate() {
804 let mut node = IterNode {
805 node_index,
806 iter,
807 cursor: None,
808 };
809 if let Some(batch) = node.advance_batch()? {
810 in_progress.push_batch(node_index, batch);
811 nodes.push(node);
812 }
813 }
814
815 let algo = MergeAlgo::new(nodes);
816
817 let iter = Self {
818 algo,
819 in_progress,
820 output_batch: None,
821 batch_size,
822 };
823
824 Ok(iter)
825 }
826
827 pub fn next_batch(&mut self) -> Result<Option<RecordBatch>> {
829 while self.algo.has_rows() && self.output_batch.is_none() {
830 if self.algo.can_fetch_batch() && !self.in_progress.is_empty() {
831 self.output_batch = self.in_progress.build_record_batch()?;
833 debug_assert!(self.output_batch.is_some());
834 } else if self.algo.can_fetch_batch() {
835 self.fetch_batch_from_hottest()?;
836 } else {
837 self.fetch_row_from_hottest()?;
838 }
839 }
840
841 Ok(self.output_batch.take())
842 }
843
844 fn fetch_batch_from_hottest(&mut self) -> Result<()> {
846 debug_assert!(self.in_progress.is_empty());
847
848 let mut hottest = self.algo.pop_hot_for_batch_transition().unwrap();
850 debug_assert!(!hottest.current_cursor().is_finished());
851 let node_index = hottest.node_index;
852 let next = hottest.advance_batch()?;
853 let batch = self.in_progress.take_remaining_rows(node_index, next);
855 Self::maybe_output_batch(batch, &mut self.output_batch);
856 self.algo.reheap_after_batch_transition(hottest);
857
858 Ok(())
859 }
860
861 fn fetch_row_from_hottest(&mut self) -> Result<()> {
863 let (node_index, at_batch_boundary) = {
864 let hottest = self.algo.hottest_mut().unwrap();
866 debug_assert!(!hottest.current_cursor().is_finished());
867 (hottest.node_index, hottest.current_cursor().is_last_row())
868 };
869 let mut boundary_node =
870 at_batch_boundary.then(|| self.algo.pop_hot_for_batch_transition().unwrap());
871 self.in_progress.push_row(node_index);
872 if self.in_progress.len() >= self.batch_size {
873 if let Some(output) = self.in_progress.build_record_batch()? {
875 Self::maybe_output_batch(output, &mut self.output_batch);
876 }
877 }
878
879 let next = if let Some(hottest) = &mut boundary_node {
880 hottest.advance_row()?
881 } else {
882 self.algo.hottest_mut().unwrap().advance_row()?
883 };
884 if let Some(next) = next {
885 self.in_progress.push_batch(node_index, next);
886 }
887
888 if let Some(hottest) = boundary_node {
889 self.algo.reheap_after_batch_transition(hottest);
890 } else {
891 self.algo.repair_hot_root();
892 }
893 Ok(())
894 }
895
896 fn maybe_output_batch(batch: RecordBatch, output_batch: &mut Option<RecordBatch>) {
898 debug_assert!(output_batch.is_none());
899 if batch.num_rows() > 0 {
900 *output_batch = Some(batch);
901 }
902 }
903}
904
905impl Iterator for FlatMergeIterator {
906 type Item = Result<RecordBatch>;
907
908 fn next(&mut self) -> Option<Self::Item> {
909 self.next_batch().transpose()
910 }
911}
912
913pub struct FlatMergeReader {
923 algo: MergeAlgo<StreamNode>,
925 in_progress: BatchBuilder,
927 output_batch: Option<RecordBatch>,
929 batch_size: usize,
933 metrics: MergeMetrics,
935 metrics_reporter: Option<Arc<dyn MergeMetricsReport>>,
937}
938
939impl FlatMergeReader {
940 pub async fn new(
942 schema: SchemaRef,
943 iters: Vec<BoxedRecordBatchStream>,
944 batch_size: usize,
945 metrics_reporter: Option<Arc<dyn MergeMetricsReport>>,
946 ) -> Result<Self> {
947 let start = Instant::now();
948 let metrics = MergeMetrics::default();
949 let mut in_progress = BatchBuilder::new(schema, iters.len(), batch_size);
950 let mut nodes = Vec::with_capacity(iters.len());
951 for (node_index, iter) in iters.into_iter().enumerate() {
953 let mut node = StreamNode {
954 node_index,
955 iter,
956 cursor: None,
957 };
958 if let Some(batch) = node.advance_batch().await? {
959 in_progress.push_batch(node_index, batch);
960 nodes.push(node);
961 }
962 }
963
964 let algo = MergeAlgo::new(nodes);
965
966 let mut reader = Self {
967 algo,
968 in_progress,
969 output_batch: None,
970 batch_size,
971 metrics,
972 metrics_reporter,
973 };
974 let elapsed = start.elapsed();
975 reader.metrics.init_cost += elapsed;
976 reader.metrics.scan_cost += elapsed;
977
978 Ok(reader)
979 }
980
981 pub async fn next_batch(&mut self) -> Result<Option<RecordBatch>> {
983 let start = Instant::now();
984 while self.algo.has_rows() && self.output_batch.is_none() {
985 if self.algo.can_fetch_batch() && !self.in_progress.is_empty() {
986 self.output_batch = self.in_progress.build_record_batch()?;
988 debug_assert!(self.output_batch.is_some());
989 } else if self.algo.can_fetch_batch() {
990 self.fetch_batch_from_hottest().await?;
991 self.metrics.num_fetch_by_batches += 1;
992 } else {
993 self.fetch_row_from_hottest().await?;
994 self.metrics.num_fetch_by_rows += 1;
995 }
996 }
997
998 if let Some(batch) = self.output_batch.take() {
999 self.metrics.scan_cost += start.elapsed();
1000 self.metrics.maybe_report(&self.metrics_reporter);
1001 Ok(Some(batch))
1002 } else {
1003 self.metrics.scan_cost += start.elapsed();
1005 self.metrics.maybe_report(&self.metrics_reporter);
1006 Ok(None)
1007 }
1008 }
1009
1010 pub fn into_stream(mut self) -> impl Stream<Item = Result<RecordBatch>> {
1012 try_stream! {
1013 while let Some(batch) = self.next_batch().await? {
1014 yield batch;
1015 }
1016 }
1017 }
1018
1019 async fn fetch_batch_from_hottest(&mut self) -> Result<()> {
1021 debug_assert!(self.in_progress.is_empty());
1022
1023 let mut hottest = self.algo.pop_hot_for_batch_transition().unwrap();
1025 debug_assert!(!hottest.current_cursor().is_finished());
1026 let node_index = hottest.node_index;
1027 let start = Instant::now();
1028 let next = hottest.advance_batch().await?;
1029 self.metrics.fetch_cost += start.elapsed();
1030 let batch = self.in_progress.take_remaining_rows(node_index, next);
1032 Self::maybe_output_batch(batch, &mut self.output_batch);
1033 self.algo.reheap_after_batch_transition(hottest);
1034
1035 Ok(())
1036 }
1037
1038 async fn fetch_row_from_hottest(&mut self) -> Result<()> {
1040 let (node_index, at_batch_boundary) = {
1041 let hottest = self.algo.hottest_mut().unwrap();
1043 debug_assert!(!hottest.current_cursor().is_finished());
1044 (hottest.node_index, hottest.current_cursor().is_last_row())
1045 };
1046 let mut boundary_node =
1047 at_batch_boundary.then(|| self.algo.pop_hot_for_batch_transition().unwrap());
1048 self.in_progress.push_row(node_index);
1049 if self.in_progress.len() >= self.batch_size {
1050 if let Some(output) = self.in_progress.build_record_batch()? {
1052 Self::maybe_output_batch(output, &mut self.output_batch);
1053 }
1054 }
1055
1056 let start = at_batch_boundary.then(Instant::now);
1057 let next = if let Some(hottest) = &mut boundary_node {
1058 hottest.advance_row().await?
1059 } else {
1060 self.algo.hottest_mut().unwrap().advance_row().await?
1061 };
1062 if let Some(start) = start {
1063 self.metrics.fetch_cost += start.elapsed();
1064 }
1065 if let Some(next) = next {
1066 self.in_progress.push_batch(node_index, next);
1067 }
1068
1069 if let Some(hottest) = boundary_node {
1070 self.algo.reheap_after_batch_transition(hottest);
1071 } else {
1072 self.algo.repair_hot_root();
1073 }
1074 Ok(())
1075 }
1076
1077 fn maybe_output_batch(batch: RecordBatch, output_batch: &mut Option<RecordBatch>) {
1079 debug_assert!(output_batch.is_none());
1080 if batch.num_rows() > 0 {
1081 *output_batch = Some(batch);
1082 }
1083 }
1084}
1085
1086impl Drop for FlatMergeReader {
1087 fn drop(&mut self) {
1088 debug!("Flat merge reader finished, metrics: {:?}", self.metrics);
1089
1090 READ_STAGE_ELAPSED
1091 .with_label_values(&["flat_merge"])
1092 .observe(self.metrics.scan_cost.as_secs_f64());
1093 READ_STAGE_ELAPSED
1094 .with_label_values(&["flat_merge_fetch"])
1095 .observe(self.metrics.fetch_cost.as_secs_f64());
1096
1097 if let Some(reporter) = &self.metrics_reporter {
1099 reporter.report(&mut self.metrics);
1100 }
1101 }
1102}
1103
1104struct GenericNode<T> {
1106 node_index: usize,
1108 iter: T,
1110 cursor: Option<RowCursor>,
1115}
1116
1117impl<T> NodeCmp for GenericNode<T> {
1118 fn is_eof(&self) -> bool {
1119 self.cursor.is_none()
1120 }
1121
1122 fn is_behind(&self, other: &Self) -> bool {
1123 debug_assert!(!self.current_cursor().is_finished());
1124 debug_assert!(!other.current_cursor().is_finished());
1125
1126 self.current_cursor()
1130 .first_primary_key()
1131 .cmp(other.current_cursor().last_primary_key())
1132 .then_with(|| {
1133 self.current_cursor()
1134 .first_timestamp()
1135 .cmp(&other.current_cursor().last_timestamp())
1136 })
1137 == Ordering::Greater
1138 }
1139}
1140
1141impl<T> PartialEq for GenericNode<T> {
1142 fn eq(&self, other: &GenericNode<T>) -> bool {
1143 self.cursor == other.cursor
1144 }
1145}
1146
1147impl<T> Eq for GenericNode<T> {}
1148
1149impl<T> PartialOrd for GenericNode<T> {
1150 fn partial_cmp(&self, other: &GenericNode<T>) -> Option<Ordering> {
1151 Some(self.cmp(other))
1152 }
1153}
1154
1155impl<T> Ord for GenericNode<T> {
1156 fn cmp(&self, other: &GenericNode<T>) -> Ordering {
1157 other.cursor.cmp(&self.cursor)
1160 }
1161}
1162
1163impl<T> GenericNode<T> {
1164 fn current_cursor(&self) -> &RowCursor {
1169 self.cursor.as_ref().unwrap()
1170 }
1171}
1172
1173impl GenericNode<BoxedRecordBatchIterator> {
1174 fn advance_batch(&mut self) -> Result<Option<RecordBatch>> {
1178 let batch = self.advance_inner_iter()?;
1179 let columns = batch.as_ref().map(SortColumns::try_new).transpose()?;
1180 self.cursor = columns.map(RowCursor::new);
1181
1182 Ok(batch)
1183 }
1184
1185 fn advance_row(&mut self) -> Result<Option<RecordBatch>> {
1188 let cursor = self.cursor.as_mut().unwrap();
1189 cursor.advance();
1190 if !cursor.is_finished() {
1191 return Ok(None);
1192 }
1193
1194 self.advance_batch()
1196 }
1197
1198 fn advance_inner_iter(&mut self) -> Result<Option<RecordBatch>> {
1200 while let Some(batch) = self.iter.next().transpose()? {
1201 if batch.num_rows() > 0 {
1202 return Ok(Some(batch));
1203 }
1204 }
1205 Ok(None)
1206 }
1207}
1208
1209type StreamNode = GenericNode<BoxedRecordBatchStream>;
1210type IterNode = GenericNode<BoxedRecordBatchIterator>;
1211
1212impl GenericNode<BoxedRecordBatchStream> {
1213 async fn advance_batch(&mut self) -> Result<Option<RecordBatch>> {
1217 let batch = self.advance_inner_iter().await?;
1218 let columns = batch.as_ref().map(SortColumns::try_new).transpose()?;
1219 self.cursor = columns.map(RowCursor::new);
1220
1221 Ok(batch)
1222 }
1223
1224 async fn advance_row(&mut self) -> Result<Option<RecordBatch>> {
1227 let cursor = self.cursor.as_mut().unwrap();
1228 cursor.advance();
1229 if !cursor.is_finished() {
1230 return Ok(None);
1231 }
1232
1233 self.advance_batch().await
1235 }
1236
1237 async fn advance_inner_iter(&mut self) -> Result<Option<RecordBatch>> {
1239 while let Some(batch) = self.iter.try_next().await? {
1240 if batch.num_rows() > 0 {
1241 return Ok(Some(batch));
1242 }
1243 }
1244 Ok(None)
1245 }
1246}
1247
1248#[cfg(test)]
1249mod tests {
1250 use std::cmp::Reverse;
1251 use std::rc::Rc;
1252 use std::sync::Arc;
1253 use std::sync::atomic::{AtomicBool, Ordering as AtomicOrdering};
1254 use std::task::Poll;
1255
1256 use api::v1::OpType;
1257 use datatypes::arrow::array::builder::BinaryDictionaryBuilder;
1258 use datatypes::arrow::array::{
1259 DictionaryArray, Int64Array, StringArray, StringDictionaryBuilder,
1260 TimestampMillisecondArray, UInt8Array, UInt64Array,
1261 };
1262 use datatypes::arrow::datatypes::{DataType, Field, Schema, TimeUnit, UInt32Type};
1263 use datatypes::arrow::record_batch::RecordBatch;
1264 use futures::FutureExt;
1265
1266 use super::*;
1267 use crate::error::UnexpectedSnafu;
1268
1269 #[derive(Debug, Eq, PartialEq)]
1270 struct TestNode {
1271 id: usize,
1272 current_rank: Option<usize>,
1273 end_rank: usize,
1274 }
1275
1276 impl TestNode {
1277 fn new(id: usize, current_rank: usize, end_rank: usize) -> Self {
1278 Self {
1279 id,
1280 current_rank: Some(current_rank),
1281 end_rank,
1282 }
1283 }
1284 }
1285
1286 impl NodeCmp for TestNode {
1287 fn is_eof(&self) -> bool {
1288 self.current_rank.is_none()
1289 }
1290
1291 fn is_behind(&self, other: &Self) -> bool {
1292 self.current_rank.unwrap() > other.end_rank
1293 }
1294 }
1295
1296 impl PartialOrd for TestNode {
1297 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
1298 Some(self.cmp(other))
1299 }
1300 }
1301
1302 impl Ord for TestNode {
1303 fn cmp(&self, other: &Self) -> Ordering {
1304 Reverse((self.current_rank, self.id)).cmp(&Reverse((other.current_rank, other.id)))
1305 }
1306 }
1307
1308 #[test]
1309 fn test_merge_algo_repairs_overlapping_hot_root_in_place() {
1310 let mut algo = MergeAlgo::new(vec![
1311 TestNode::new(0, 0, 10),
1312 TestNode::new(1, 5, 15),
1313 TestNode::new(2, 20, 25),
1314 ]);
1315 assert_eq!(0, algo.hot.peek().unwrap().id);
1316 assert_eq!((2, 1), (algo.hot.len(), algo.cold.len()));
1317
1318 algo.hottest_mut().unwrap().current_rank = Some(6);
1319 algo.repair_hot_root();
1320
1321 assert_eq!(1, algo.hot.peek().unwrap().id);
1322 assert_eq!((2, 1), (algo.hot.len(), algo.cold.len()));
1323 }
1324
1325 #[test]
1326 fn test_merge_algo_moves_root_beyond_remaining_hot_range_to_cold() {
1327 let mut algo = MergeAlgo::new(vec![
1328 TestNode::new(0, 0, 10),
1329 TestNode::new(1, 5, 7),
1330 TestNode::new(2, 20, 25),
1331 ]);
1332
1333 algo.hottest_mut().unwrap().current_rank = Some(8);
1334 algo.repair_hot_root();
1335
1336 assert_eq!(1, algo.hot.peek().unwrap().id);
1337 assert_eq!((1, 2), (algo.hot.len(), algo.cold.len()));
1338 }
1339
1340 #[test]
1341 fn test_merge_algo_removes_eof_root_and_refills_hot() {
1342 let mut algo = MergeAlgo::new(vec![TestNode::new(0, 0, 4), TestNode::new(1, 10, 14)]);
1343 assert_eq!((1, 1), (algo.hot.len(), algo.cold.len()));
1344
1345 algo.hottest_mut().unwrap().current_rank = None;
1346 algo.repair_hot_root();
1347
1348 assert_eq!(1, algo.hot.peek().unwrap().id);
1349 assert_eq!((1, 0), (algo.hot.len(), algo.cold.len()));
1350 }
1351
1352 #[test]
1353 fn test_merge_algo_single_hot_node_can_fetch_batch() {
1354 let algo = MergeAlgo::new(vec![TestNode::new(0, 0, 4), TestNode::new(1, 10, 14)]);
1355
1356 assert_eq!(0, algo.hot.peek().unwrap().id);
1357 assert_eq!((1, 1), (algo.hot.len(), algo.cold.len()));
1358 assert!(algo.can_fetch_batch());
1359 }
1360
1361 #[derive(Debug)]
1366 struct CountedNode {
1367 id: usize,
1368 current_rank: Option<usize>,
1369 end_rank: usize,
1370 compares: Rc<Cell<usize>>,
1371 }
1372
1373 impl CountedNode {
1374 fn new(id: usize, current_rank: usize, compares: &Rc<Cell<usize>>) -> Self {
1375 Self {
1376 id,
1377 current_rank: Some(current_rank),
1378 end_rank: usize::MAX,
1380 compares: Rc::clone(compares),
1381 }
1382 }
1383 }
1384
1385 impl NodeCmp for CountedNode {
1386 fn is_eof(&self) -> bool {
1387 self.current_rank.is_none()
1388 }
1389
1390 fn is_behind(&self, other: &Self) -> bool {
1391 self.current_rank.unwrap() > other.end_rank
1392 }
1393 }
1394
1395 impl PartialEq for CountedNode {
1396 fn eq(&self, other: &Self) -> bool {
1397 self.current_rank == other.current_rank
1398 }
1399 }
1400
1401 impl Eq for CountedNode {}
1402
1403 impl PartialOrd for CountedNode {
1404 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
1405 Some(self.cmp(other))
1406 }
1407 }
1408
1409 impl Ord for CountedNode {
1410 fn cmp(&self, other: &Self) -> Ordering {
1411 self.compares.set(self.compares.get() + 1);
1412 Reverse(self.current_rank).cmp(&Reverse(other.current_rank))
1413 }
1414 }
1415
1416 fn drain_merge_algo<T: NodeCmp>(algo: &mut MergeAlgo<T>) -> Vec<T> {
1417 let mut nodes = Vec::with_capacity(algo.hot.len());
1418 while let Some(node) = algo.pop_hot_for_batch_transition() {
1419 nodes.push(node);
1420 }
1421 nodes
1422 }
1423
1424 #[test]
1425 fn test_merge_algo_skips_tree_replay_when_winner_stays_hottest() {
1426 let compares = Rc::new(Cell::new(0));
1427 let mut algo = MergeAlgo::new(vec![
1428 CountedNode::new(0, 10, &compares),
1429 CountedNode::new(1, 50, &compares),
1430 CountedNode::new(2, 40, &compares),
1431 CountedNode::new(3, 30, &compares),
1432 ]);
1433 assert_eq!(0, algo.hot.peek().unwrap().id);
1434 assert_eq!((4, 0), (algo.hot.len(), algo.cold.len()));
1435
1436 algo.hottest_mut().unwrap().current_rank = Some(20);
1439 compares.set(0);
1440 algo.repair_hot_root();
1441
1442 assert_eq!(2, compares.get());
1445 assert_eq!(0, algo.hot.peek().unwrap().id);
1446 assert_eq!((4, 0), (algo.hot.len(), algo.cold.len()));
1447
1448 let drained: Vec<_> = drain_merge_algo(&mut algo)
1450 .into_iter()
1451 .map(|node| node.id)
1452 .collect();
1453 assert_eq!(vec![0, 3, 2, 1], drained);
1454 }
1455
1456 #[test]
1457 fn test_merge_algo_retains_winner_tied_with_second_best() {
1458 let compares = Rc::new(Cell::new(0));
1459 let mut algo = MergeAlgo::new(vec![
1460 CountedNode::new(0, 10, &compares),
1461 CountedNode::new(1, 20, &compares),
1462 CountedNode::new(2, 20, &compares),
1463 ]);
1464 let winner_id = algo.hot.peek().unwrap().id;
1465
1466 algo.hottest_mut().unwrap().current_rank = Some(20);
1468 compares.set(0);
1469 algo.repair_hot_root();
1470
1471 assert_eq!(2, compares.get());
1474 assert_eq!(winner_id, algo.hot.peek().unwrap().id);
1475 assert_eq!((3, 0), (algo.hot.len(), algo.cold.len()));
1476
1477 let mut drained: Vec<_> = drain_merge_algo(&mut algo)
1478 .into_iter()
1479 .map(|node| node.id)
1480 .collect();
1481 drained.sort_unstable();
1482 assert_eq!(vec![0, 1, 2], drained);
1483 }
1484
1485 #[test]
1486 fn test_merge_algo_caches_second_best_across_retention_repairs() {
1487 let compares = Rc::new(Cell::new(0));
1488 let mut algo = MergeAlgo::new(vec![
1489 CountedNode::new(0, 10, &compares),
1490 CountedNode::new(1, 50, &compares),
1491 CountedNode::new(2, 40, &compares),
1492 CountedNode::new(3, 30, &compares),
1493 ]);
1494
1495 algo.hottest_mut().unwrap().current_rank = Some(20);
1497 algo.repair_hot_root();
1498 assert_eq!(0, algo.hot.peek().unwrap().id);
1499
1500 compares.set(0);
1504 for rank in 21..=23 {
1505 algo.hottest_mut().unwrap().current_rank = Some(rank);
1506 algo.repair_hot_root();
1507 }
1508 assert_eq!(3, compares.get());
1509 assert_eq!(0, algo.hot.peek().unwrap().id);
1510 assert_eq!((4, 0), (algo.hot.len(), algo.cold.len()));
1511
1512 let drained: Vec<_> = drain_merge_algo(&mut algo)
1514 .into_iter()
1515 .map(|node| node.id)
1516 .collect();
1517 assert_eq!(vec![0, 3, 2, 1], drained);
1518 }
1519
1520 fn drain_winner_tree<T: Ord>(tree: &mut WinnerTree<T>) -> Vec<T> {
1521 let mut values = Vec::with_capacity(tree.len());
1522 while let Some(value) = tree.pop() {
1523 values.push(value);
1524 }
1525 values
1526 }
1527
1528 #[test]
1529 fn test_winner_tree_empty() {
1530 let mut tree = WinnerTree::<i32>::with_capacity(0);
1531
1532 assert!(tree.is_empty());
1533 assert_eq!(0, tree.len());
1534 assert_eq!(None, tree.peek());
1535 assert_eq!(None, tree.winner_mut());
1536 assert_eq!(None, tree.second_best());
1537 assert_eq!(None, tree.pop());
1538 tree.replay_winner();
1539 }
1540
1541 #[test]
1542 fn test_winner_tree_single_element() {
1543 let mut tree = WinnerTree::with_capacity(1);
1544 tree.push(7);
1545
1546 assert!(!tree.is_empty());
1547 assert_eq!(1, tree.len());
1548 assert_eq!(Some(&7), tree.peek());
1549 assert_eq!(None, tree.second_best());
1550 assert_eq!(Some(7), tree.pop());
1551 assert!(tree.is_empty());
1552 }
1553
1554 #[test]
1555 fn test_winner_tree_drains_in_descending_order() {
1556 let mut tree = WinnerTree::with_capacity(8);
1557 for value in [3, 1, 4, 1, 5, 9, 2, 6] {
1558 tree.push(value);
1559 }
1560
1561 assert_eq!(Some(&9), tree.peek());
1562 assert_eq!(Some(&6), tree.second_best());
1563 assert_eq!(vec![9, 6, 5, 4, 3, 2, 1, 1], drain_winner_tree(&mut tree));
1564 }
1565
1566 #[test]
1567 fn test_winner_tree_non_power_of_two_capacity() {
1568 let mut tree = WinnerTree::with_capacity(5);
1569 for value in [40, 10, 50, 20, 30] {
1570 tree.push(value);
1571 }
1572
1573 assert_eq!(Some(&50), tree.peek());
1574 assert_eq!(Some(&40), tree.second_best());
1575 assert_eq!(vec![50, 40, 30, 20, 10], drain_winner_tree(&mut tree));
1576 }
1577
1578 #[test]
1579 fn test_winner_tree_replays_winner_after_mutation() {
1580 let mut tree = WinnerTree::with_capacity(4);
1581 for value in [7, 3, 9, 5] {
1582 tree.push(value);
1583 }
1584
1585 *tree.winner_mut().unwrap() = 1;
1586 tree.replay_winner();
1587
1588 assert_eq!(Some(&7), tree.peek());
1589 assert_eq!(Some(&5), tree.second_best());
1590 assert_eq!(vec![7, 5, 3, 1], drain_winner_tree(&mut tree));
1591 }
1592
1593 #[test]
1594 fn test_winner_tree_mutated_winner_can_stay_winner() {
1595 let mut tree = WinnerTree::with_capacity(3);
1596 for value in [1, 2, 9] {
1597 tree.push(value);
1598 }
1599
1600 *tree.winner_mut().unwrap() = 8;
1601 tree.replay_winner();
1602
1603 assert_eq!(Some(&8), tree.peek());
1604 assert_eq!(vec![8, 2, 1], drain_winner_tree(&mut tree));
1605 }
1606
1607 #[test]
1608 fn test_winner_tree_remove_and_reinsert() {
1609 let mut tree = WinnerTree::with_capacity(3);
1610 for value in [5, 9, 7] {
1611 tree.push(value);
1612 }
1613
1614 assert_eq!(Some(9), tree.pop());
1616 tree.push(8);
1617 assert_eq!(Some(&8), tree.peek());
1618 assert_eq!(vec![8, 7, 5], drain_winner_tree(&mut tree));
1619
1620 tree.push(4);
1622 tree.push(6);
1623 assert_eq!(Some(&6), tree.peek());
1624 assert_eq!(vec![6, 4], drain_winner_tree(&mut tree));
1625 }
1626
1627 fn assert_winner_tree_matches_oracle(
1632 seed: u64,
1633 value_range: u32,
1634 capacity: usize,
1635 num_ops: usize,
1636 ) {
1637 use rand::rngs::StdRng;
1638 use rand::{Rng, SeedableRng};
1639
1640 let mut rng = StdRng::seed_from_u64(seed);
1641 let mut tree = WinnerTree::<u32>::with_capacity(capacity);
1642 let mut oracle = BinaryHeap::<u32>::new();
1643 let mut next_value = 0_u32;
1644
1645 for _ in 0..num_ops {
1646 match rng.random_range(0..3) {
1647 0 if tree.len() < capacity => {
1648 let pushed_value = next_value % value_range;
1649 next_value += 1;
1650 tree.push(pushed_value);
1651 oracle.push(pushed_value);
1652 }
1653 1 => {
1654 assert_eq!(oracle.pop(), tree.pop());
1655 }
1656 _ => {
1657 let new_value = rng.random_range(0..value_range);
1658 if let Some(winner) = tree.winner_mut() {
1659 *winner = new_value;
1660 tree.replay_winner();
1661
1662 oracle.pop();
1663 oracle.push(new_value);
1664 }
1665 }
1666 }
1667
1668 assert_eq!(oracle.peek(), tree.peek());
1669 assert_eq!(oracle.len(), tree.len());
1670 let oracle_second_best = {
1671 let mut rest = oracle.clone();
1672 rest.pop();
1673 rest.peek().copied()
1674 };
1675 assert_eq!(oracle_second_best, tree.second_best().copied());
1676 }
1677
1678 let mut oracle_values = Vec::with_capacity(oracle.len());
1680 while let Some(value) = oracle.pop() {
1681 oracle_values.push(value);
1682 }
1683 assert_eq!(oracle_values, drain_winner_tree(&mut tree));
1684 }
1685
1686 #[test]
1687 fn test_winner_tree_matches_binary_heap_oracle() {
1688 for seed in [0x5eed, 0xdead_beef, 42] {
1689 assert_winner_tree_matches_oracle(seed, 1000, 13, 2000);
1690 }
1691 }
1692
1693 #[test]
1694 fn test_winner_tree_matches_oracle_with_duplicate_heavy_values() {
1695 assert_winner_tree_matches_oracle(0xc0ffee, 3, 8, 2000);
1698 }
1699
1700 #[test]
1701 fn test_winner_tree_matches_oracle_with_tiny_capacities() {
1702 for capacity in 1..=3 {
1703 assert_winner_tree_matches_oracle(0xbeef, 100, capacity, 500);
1704 }
1705 }
1706
1707 fn create_test_record_batch(
1709 primary_keys: &[&[u8]],
1710 timestamps: &[i64],
1711 sequences: &[u64],
1712 op_types: &[OpType],
1713 field_values: &[i64],
1714 ) -> RecordBatch {
1715 let schema = Arc::new(Schema::new(vec![
1716 Field::new("field1", DataType::Int64, false),
1717 Field::new(
1718 "timestamp",
1719 DataType::Timestamp(TimeUnit::Millisecond, None),
1720 false,
1721 ),
1722 Field::new(
1723 "__primary_key",
1724 DataType::Dictionary(Box::new(DataType::UInt32), Box::new(DataType::Binary)),
1725 false,
1726 ),
1727 Field::new("__sequence", DataType::UInt64, false),
1728 Field::new("__op_type", DataType::UInt8, false),
1729 ]));
1730
1731 let field1 = Arc::new(Int64Array::from_iter_values(field_values.iter().copied()));
1732 let timestamp = Arc::new(TimestampMillisecondArray::from_iter_values(
1733 timestamps.iter().copied(),
1734 ));
1735
1736 let mut builder = BinaryDictionaryBuilder::<UInt32Type>::new();
1738 for &key in primary_keys {
1739 builder.append(key).unwrap();
1740 }
1741 let primary_key = Arc::new(builder.finish());
1742
1743 let sequence = Arc::new(UInt64Array::from_iter_values(sequences.iter().copied()));
1744 let op_type = Arc::new(UInt8Array::from_iter_values(
1745 op_types.iter().map(|&v| v as u8),
1746 ));
1747
1748 RecordBatch::try_new(
1749 schema,
1750 vec![field1, timestamp, primary_key, sequence, op_type],
1751 )
1752 .unwrap()
1753 }
1754
1755 fn new_test_iter(batches: Vec<RecordBatch>) -> BoxedRecordBatchIterator {
1756 Box::new(batches.into_iter().map(Ok))
1757 }
1758
1759 fn boundary_test_batches() -> (RecordBatch, RecordBatch, RecordBatch) {
1760 let first = create_test_record_batch(
1761 &[b"k1", b"k1"],
1762 &[1000, 2000],
1763 &[1, 2],
1764 &[OpType::Put, OpType::Put],
1765 &[10, 12],
1766 );
1767 let second = create_test_record_batch(
1768 &[b"k1", b"k1", b"k1"],
1769 &[1500, 2000, 2500],
1770 &[1, 1, 1],
1771 &[OpType::Put, OpType::Put, OpType::Put],
1772 &[11, 13, 14],
1773 );
1774 let pending = create_test_record_batch(
1775 &[b"k1", b"k1", b"k1"],
1776 &[1000, 1500, 2000],
1777 &[1, 1, 2],
1778 &[OpType::Put, OpType::Put, OpType::Put],
1779 &[10, 11, 12],
1780 );
1781 (first, second, pending)
1782 }
1783
1784 fn test_source_error() -> crate::error::Error {
1785 UnexpectedSnafu {
1786 reason: "test source failed".to_string(),
1787 }
1788 .build()
1789 }
1790
1791 #[test]
1792 fn test_row_cursor_last_row() {
1793 let batch = create_test_record_batch(
1794 &[b"k1", b"k1"],
1795 &[1000, 2000],
1796 &[21, 22],
1797 &[OpType::Put, OpType::Put],
1798 &[11, 12],
1799 );
1800 let mut cursor = RowCursor::new(SortColumns::try_new(&batch).unwrap());
1801
1802 assert!(!cursor.is_last_row());
1803 cursor.advance();
1804 assert!(cursor.is_last_row());
1805 cursor.advance();
1806 assert!(!cursor.is_last_row());
1807 }
1808
1809 fn assert_record_batches_eq(expected: &[RecordBatch], actual: &[RecordBatch]) {
1811 for (exp, act) in expected.iter().zip(actual.iter()) {
1812 assert_eq!(exp, act,);
1813 }
1814 }
1815
1816 fn collect_merge_iterator_batches(iter: FlatMergeIterator) -> Vec<RecordBatch> {
1818 iter.map(|result| result.unwrap()).collect()
1819 }
1820
1821 #[test]
1822 fn test_merge_iterator_empty() {
1823 let schema = Arc::new(Schema::new(vec![
1824 Field::new("field1", DataType::Int64, false),
1825 Field::new(
1826 "timestamp",
1827 DataType::Timestamp(TimeUnit::Millisecond, None),
1828 false,
1829 ),
1830 Field::new(
1831 "__primary_key",
1832 DataType::Dictionary(Box::new(DataType::UInt32), Box::new(DataType::Binary)),
1833 false,
1834 ),
1835 Field::new("__sequence", DataType::UInt64, false),
1836 Field::new("__op_type", DataType::UInt8, false),
1837 ]));
1838
1839 let mut merge_iter = FlatMergeIterator::new(schema, vec![], 1024).unwrap();
1840 assert!(merge_iter.next_batch().unwrap().is_none());
1841 }
1842
1843 #[test]
1844 fn test_merge_iterator_single_batch() {
1845 let batch = create_test_record_batch(
1846 &[b"k1", b"k1"],
1847 &[1000, 2000],
1848 &[21, 22],
1849 &[OpType::Put, OpType::Put],
1850 &[11, 12],
1851 );
1852
1853 let schema = batch.schema();
1854 let iter = Box::new(new_test_iter(vec![batch.clone()]));
1855
1856 let merge_iter = FlatMergeIterator::new(schema, vec![iter], 1024).unwrap();
1857 let result = collect_merge_iterator_batches(merge_iter);
1858
1859 assert_eq!(result.len(), 1);
1860 assert_record_batches_eq(&[batch], &result);
1861 }
1862
1863 #[test]
1864 fn test_merge_iterator_non_overlapping() {
1865 let batch1 = create_test_record_batch(
1866 &[b"k1", b"k1"],
1867 &[1000, 2000],
1868 &[21, 22],
1869 &[OpType::Put, OpType::Put],
1870 &[11, 12],
1871 );
1872 let batch2 = create_test_record_batch(
1873 &[b"k1", b"k1"],
1874 &[4000, 5000],
1875 &[24, 25],
1876 &[OpType::Put, OpType::Put],
1877 &[14, 15],
1878 );
1879 let batch3 = create_test_record_batch(
1880 &[b"k2", b"k2"],
1881 &[2000, 3000],
1882 &[22, 23],
1883 &[OpType::Delete, OpType::Put],
1884 &[12, 13],
1885 );
1886
1887 let schema = batch1.schema();
1888 let iter1 = Box::new(new_test_iter(vec![batch1.clone(), batch3.clone()]));
1889 let iter2 = Box::new(new_test_iter(vec![batch2.clone()]));
1890
1891 let merge_iter = FlatMergeIterator::new(schema, vec![iter1, iter2], 1024).unwrap();
1892 let result = collect_merge_iterator_batches(merge_iter);
1893
1894 let expected = vec![batch1, batch2, batch3];
1896 assert_record_batches_eq(&expected, &result);
1897 }
1898
1899 #[test]
1900 fn test_merge_iterator_overlapping_timestamps() {
1901 let batch1 = create_test_record_batch(
1903 &[b"k1", b"k1"],
1904 &[1000, 2000],
1905 &[21, 22],
1906 &[OpType::Put, OpType::Put],
1907 &[11, 12],
1908 );
1909 let batch2 = create_test_record_batch(
1910 &[b"k1", b"k1"],
1911 &[1500, 2500],
1912 &[31, 32],
1913 &[OpType::Put, OpType::Put],
1914 &[15, 25],
1915 );
1916
1917 let schema = batch1.schema();
1918 let iter1 = Box::new(new_test_iter(vec![batch1]));
1919 let iter2 = Box::new(new_test_iter(vec![batch2]));
1920
1921 let merge_iter = FlatMergeIterator::new(schema, vec![iter1, iter2], 1024).unwrap();
1922 let result = collect_merge_iterator_batches(merge_iter);
1923
1924 let expected = vec![
1925 create_test_record_batch(
1926 &[b"k1", b"k1"],
1927 &[1000, 1500],
1928 &[21, 31],
1929 &[OpType::Put, OpType::Put],
1930 &[11, 15],
1931 ),
1932 create_test_record_batch(&[b"k1"], &[2000], &[22], &[OpType::Put], &[12]),
1933 create_test_record_batch(&[b"k1"], &[2500], &[32], &[OpType::Put], &[25]),
1934 ];
1935 assert_record_batches_eq(&expected, &result);
1936 }
1937
1938 #[test]
1939 fn test_merge_iterator_duplicate_keys_sequences() {
1940 let batch1 = create_test_record_batch(
1942 &[b"k1", b"k1"],
1943 &[1000, 1000],
1944 &[20, 10],
1945 &[OpType::Put, OpType::Put],
1946 &[1, 2],
1947 );
1948 let batch2 = create_test_record_batch(
1949 &[b"k1"],
1950 &[1000],
1951 &[15], &[OpType::Put],
1953 &[3],
1954 );
1955
1956 let schema = batch1.schema();
1957 let iter1 = Box::new(new_test_iter(vec![batch1]));
1958 let iter2 = Box::new(new_test_iter(vec![batch2]));
1959
1960 let merge_iter = FlatMergeIterator::new(schema, vec![iter1, iter2], 1024).unwrap();
1961 let result = collect_merge_iterator_batches(merge_iter);
1962
1963 let expected = vec![
1965 create_test_record_batch(
1966 &[b"k1", b"k1"],
1967 &[1000, 1000],
1968 &[20, 15],
1969 &[OpType::Put, OpType::Put],
1970 &[1, 3],
1971 ),
1972 create_test_record_batch(&[b"k1"], &[1000], &[10], &[OpType::Put], &[2]),
1973 ];
1974 assert_record_batches_eq(&expected, &result);
1975 }
1976
1977 #[test]
1978 fn test_merge_iterator_empty_primary_keys() {
1979 let batch1 = create_test_record_batch(
1982 &[b"", b""],
1983 &[1000, 3000],
1984 &[21, 23],
1985 &[OpType::Put, OpType::Put],
1986 &[11, 13],
1987 );
1988 let batch2 = create_test_record_batch(
1989 &[b"", b""],
1990 &[2000, 4000],
1991 &[22, 24],
1992 &[OpType::Put, OpType::Put],
1993 &[12, 14],
1994 );
1995
1996 let schema = batch1.schema();
1997 let iter1 = Box::new(new_test_iter(vec![batch1]));
1998 let iter2 = Box::new(new_test_iter(vec![batch2]));
1999
2000 let merge_iter = FlatMergeIterator::new(schema, vec![iter1, iter2], 1024).unwrap();
2001 let result = collect_merge_iterator_batches(merge_iter);
2002
2003 let num_rows: usize = result.iter().map(|batch| batch.num_rows()).sum();
2004 assert_eq!(4, num_rows);
2005 let mut timestamps = Vec::new();
2006 for batch in &result {
2007 let pk_idx = primary_key_column_index(batch.num_columns());
2008 let expected_keys = vec![b"".as_slice(); batch.num_rows()];
2011 assert_primary_key_dictionary(batch.column(pk_idx).as_ref(), &expected_keys, &[b""]);
2012 let timestamp = batch
2013 .column(time_index_column_index(batch.num_columns()))
2014 .as_any()
2015 .downcast_ref::<TimestampMillisecondArray>()
2016 .unwrap();
2017 timestamps.extend(timestamp.values().iter().copied());
2018 }
2019 assert_eq!(vec![1000, 2000, 3000, 4000], timestamps);
2021 }
2022
2023 fn create_test_record_batch_with_dict_tag(
2026 tags: &[&str],
2027 primary_keys: &[&[u8]],
2028 timestamps: &[i64],
2029 sequences: &[u64],
2030 op_types: &[OpType],
2031 field_values: &[i64],
2032 ) -> RecordBatch {
2033 let schema = Arc::new(Schema::new(vec![
2034 Field::new(
2035 "tag0",
2036 DataType::Dictionary(Box::new(DataType::UInt32), Box::new(DataType::Utf8)),
2037 true,
2038 ),
2039 Field::new("field1", DataType::Int64, false),
2040 Field::new(
2041 "timestamp",
2042 DataType::Timestamp(TimeUnit::Millisecond, None),
2043 false,
2044 ),
2045 Field::new(
2046 "__primary_key",
2047 DataType::Dictionary(Box::new(DataType::UInt32), Box::new(DataType::Binary)),
2048 false,
2049 ),
2050 Field::new("__sequence", DataType::UInt64, false),
2051 Field::new("__op_type", DataType::UInt8, false),
2052 ]));
2053
2054 let mut tag_builder = StringDictionaryBuilder::<UInt32Type>::new();
2055 for tag in tags {
2056 tag_builder.append(tag).unwrap();
2057 }
2058 let tag = Arc::new(tag_builder.finish());
2059
2060 let field1 = Arc::new(Int64Array::from_iter_values(field_values.iter().copied()));
2061 let timestamp = Arc::new(TimestampMillisecondArray::from_iter_values(
2062 timestamps.iter().copied(),
2063 ));
2064 let mut pk_builder = BinaryDictionaryBuilder::<UInt32Type>::new();
2065 for key in primary_keys {
2066 pk_builder.append(key).unwrap();
2067 }
2068 let primary_key = Arc::new(pk_builder.finish());
2069 let sequence = Arc::new(UInt64Array::from_iter_values(sequences.iter().copied()));
2070 let op_type = Arc::new(UInt8Array::from_iter_values(
2071 op_types.iter().map(|&v| v as u8),
2072 ));
2073
2074 RecordBatch::try_new(
2075 schema,
2076 vec![tag, field1, timestamp, primary_key, sequence, op_type],
2077 )
2078 .unwrap()
2079 }
2080
2081 #[test]
2082 fn test_merge_iterator_dictionary_encoded_tag_column() {
2083 let batch1 = create_test_record_batch_with_dict_tag(
2086 &["us-west", "eu-west"],
2087 &[b"k1", b"k2"],
2088 &[1000, 2000],
2089 &[21, 22],
2090 &[OpType::Put, OpType::Put],
2091 &[11, 12],
2092 );
2093 let batch2 = create_test_record_batch_with_dict_tag(
2094 &["us-east", "eu-west"],
2095 &[b"k1", b"k2"],
2096 &[1500, 2500],
2097 &[23, 24],
2098 &[OpType::Put, OpType::Put],
2099 &[13, 14],
2100 );
2101
2102 let schema = batch1.schema();
2103 let iter1 = Box::new(new_test_iter(vec![batch1]));
2104 let iter2 = Box::new(new_test_iter(vec![batch2]));
2105
2106 let merge_iter = FlatMergeIterator::new(schema, vec![iter1, iter2], 1024).unwrap();
2107 let result = collect_merge_iterator_batches(merge_iter);
2108
2109 let num_rows: usize = result.iter().map(|batch| batch.num_rows()).sum();
2112 assert_eq!(4, num_rows);
2113 let mut tags = Vec::new();
2114 let mut timestamps = Vec::new();
2115 for batch in &result {
2116 let tag = batch
2117 .column(0)
2118 .as_any()
2119 .downcast_ref::<DictionaryArray<UInt32Type>>()
2120 .unwrap();
2121 let tag_values = tag.values().as_any().downcast_ref::<StringArray>().unwrap();
2122 tags.extend(
2123 tag.keys()
2124 .iter()
2125 .map(|key| tag_values.value(key.unwrap() as usize)),
2126 );
2127 let timestamp = batch
2128 .column(time_index_column_index(batch.num_columns()))
2129 .as_any()
2130 .downcast_ref::<TimestampMillisecondArray>()
2131 .unwrap();
2132 timestamps.extend(timestamp.values().iter().copied());
2133 }
2134 assert_eq!(vec!["us-west", "us-east", "eu-west", "eu-west"], tags);
2135 assert_eq!(vec![1000, 1500, 2000, 2500], timestamps);
2136 }
2137
2138 #[test]
2139 fn test_merge_iterator_retry_after_row_boundary_error_removes_source() {
2140 let (first, second, pending) = boundary_test_batches();
2141 let schema = first.schema();
2142 let first_source = Box::new(vec![Ok(first), Err(test_source_error())].into_iter())
2143 as BoxedRecordBatchIterator;
2144 let second_source = new_test_iter(vec![second.clone()]);
2145 let mut merge =
2146 FlatMergeIterator::new(schema, vec![first_source, second_source], 1024).unwrap();
2147
2148 assert!(merge.next_batch().is_err());
2149 assert_eq!(pending, merge.next_batch().unwrap().unwrap());
2150 assert_eq!(second.slice(1, 2), merge.next_batch().unwrap().unwrap());
2151 assert!(merge.next_batch().unwrap().is_none());
2152 }
2153
2154 #[tokio::test]
2155 async fn test_merge_reader_retry_after_row_boundary_error_removes_source() {
2156 let (first, second, pending) = boundary_test_batches();
2157 let schema = first.schema();
2158 let first_source = Box::pin(futures::stream::iter(vec![
2159 Ok(first),
2160 Err(test_source_error()),
2161 ])) as BoxedRecordBatchStream;
2162 let second_source =
2163 Box::pin(futures::stream::iter(vec![Ok(second.clone())])) as BoxedRecordBatchStream;
2164 let mut merge = FlatMergeReader::new(schema, vec![first_source, second_source], 1024, None)
2165 .await
2166 .unwrap();
2167
2168 assert!(merge.next_batch().await.is_err());
2169 assert_eq!(pending, merge.next_batch().await.unwrap().unwrap());
2170 assert_eq!(
2171 second.slice(1, 2),
2172 merge.next_batch().await.unwrap().unwrap()
2173 );
2174 assert!(merge.next_batch().await.unwrap().is_none());
2175 }
2176
2177 #[tokio::test]
2178 async fn test_merge_reader_cancelled_row_boundary_fetch_removes_source() {
2179 let (first, second, pending) = boundary_test_batches();
2180 let schema = first.schema();
2181 let fetch_pending = Arc::new(AtomicBool::new(false));
2182 let fetch_pending_on_poll = Arc::clone(&fetch_pending);
2183 let mut first_batch = Some(first);
2184 let first_source = Box::pin(futures::stream::poll_fn(move |_cx| {
2185 if let Some(batch) = first_batch.take() {
2186 Poll::Ready(Some(Ok(batch)))
2187 } else {
2188 fetch_pending_on_poll.store(true, AtomicOrdering::Relaxed);
2189 Poll::Pending
2190 }
2191 })) as BoxedRecordBatchStream;
2192 let second_source =
2193 Box::pin(futures::stream::iter(vec![Ok(second.clone())])) as BoxedRecordBatchStream;
2194 let mut merge = FlatMergeReader::new(schema, vec![first_source, second_source], 1024, None)
2195 .await
2196 .unwrap();
2197
2198 assert!(Box::pin(merge.next_batch()).now_or_never().is_none());
2199 assert!(fetch_pending.load(AtomicOrdering::Relaxed));
2200 assert_eq!(pending, merge.next_batch().await.unwrap().unwrap());
2201 assert_eq!(
2202 second.slice(1, 2),
2203 merge.next_batch().await.unwrap().unwrap()
2204 );
2205 assert!(merge.next_batch().await.unwrap().is_none());
2206 }
2207
2208 #[test]
2209 fn test_batch_builder_basic() {
2210 let schema = Arc::new(Schema::new(vec![
2211 Field::new("field1", DataType::Int64, false),
2212 Field::new(
2213 "timestamp",
2214 DataType::Timestamp(TimeUnit::Millisecond, None),
2215 false,
2216 ),
2217 ]));
2218
2219 let mut builder = BatchBuilder::new(schema.clone(), 2, 1024);
2220 assert!(builder.is_empty());
2221
2222 let batch = RecordBatch::try_new(
2223 schema,
2224 vec![
2225 Arc::new(Int64Array::from(vec![1, 2])),
2226 Arc::new(TimestampMillisecondArray::from(vec![1000, 2000])),
2227 ],
2228 )
2229 .unwrap();
2230
2231 builder.push_batch(0, batch);
2232 builder.push_row(0);
2233 builder.push_row(0);
2234
2235 assert!(!builder.is_empty());
2236 assert_eq!(builder.len(), 2);
2237
2238 let result_batch = builder.build_record_batch().unwrap().unwrap();
2239 assert_eq!(result_batch.num_rows(), 2);
2240 }
2241
2242 #[test]
2243 fn test_batch_builder_generic_three_column_schema() {
2244 let schema = Arc::new(Schema::new(vec![
2245 Field::new("field1", DataType::Int64, false),
2246 Field::new("field2", DataType::Int64, false),
2247 Field::new("field3", DataType::Int64, false),
2248 ]));
2249 let batch = RecordBatch::try_new(
2250 Arc::clone(&schema),
2251 vec![
2252 Arc::new(Int64Array::from(vec![1, 2])),
2253 Arc::new(Int64Array::from(vec![3, 4])),
2254 Arc::new(Int64Array::from(vec![5, 6])),
2255 ],
2256 )
2257 .unwrap();
2258 let mut builder = BatchBuilder::new(schema, 1, 2);
2259 builder.push_batch(0, batch.clone());
2260 builder.push_row(0);
2261 builder.push_row(0);
2262
2263 let output_batch = builder.build_record_batch().unwrap().unwrap();
2264
2265 assert_eq!(batch, output_batch);
2266 }
2267
2268 #[test]
2269 fn test_merge_iterator_rejects_batch_without_internal_columns() {
2270 let schema = Arc::new(Schema::new(vec![
2274 Field::new("field1", DataType::Int64, false),
2275 Field::new("field2", DataType::Int64, false),
2276 ]));
2277 let batch = RecordBatch::try_new(
2278 Arc::clone(&schema),
2279 vec![
2280 Arc::new(Int64Array::from(vec![1, 2])),
2281 Arc::new(Int64Array::from(vec![3, 4])),
2282 ],
2283 )
2284 .unwrap();
2285 let iter = Box::new(new_test_iter(vec![batch]));
2286
2287 let result = FlatMergeIterator::new(schema, vec![iter], 1024);
2288
2289 assert!(matches!(
2290 result,
2291 Err(crate::error::Error::InvalidRecordBatch { .. })
2292 ));
2293 }
2294
2295 fn assert_primary_key_dictionary(
2296 array: &dyn Array,
2297 expected_decoded: &[&[u8]],
2298 expected_values: &[&[u8]],
2299 ) {
2300 let dictionary = array.as_any().downcast_ref::<PrimaryKeyArray>().unwrap();
2301 let values = dictionary
2302 .values()
2303 .as_any()
2304 .downcast_ref::<BinaryArray>()
2305 .unwrap();
2306 let decoded: Vec<_> = dictionary
2307 .keys()
2308 .iter()
2309 .map(|key| values.value(key.unwrap() as usize))
2310 .collect();
2311 let dictionary_values: Vec<_> = values.iter().map(Option::unwrap).collect();
2312
2313 assert_eq!(expected_decoded, decoded);
2314 assert_eq!(expected_values, dictionary_values);
2315 }
2316
2317 #[test]
2318 fn test_interleave_primary_key_deduplicates_separate_dictionaries() {
2319 let batch0 = create_test_record_batch(
2320 &[b"k1", b"k2"],
2321 &[1000, 2000],
2322 &[1, 1],
2323 &[OpType::Put, OpType::Put],
2324 &[10, 20],
2325 );
2326 let batch1 = create_test_record_batch(
2327 &[b"k1", b"k2"],
2328 &[1000, 2000],
2329 &[1, 1],
2330 &[OpType::Put, OpType::Put],
2331 &[11, 21],
2332 );
2333 let pk_idx = primary_key_column_index(batch0.num_columns());
2334 let arrays: Vec<_> = [&batch0, &batch1]
2335 .into_iter()
2336 .map(|batch| batch.column(pk_idx).as_ref())
2337 .collect();
2338
2339 let output = interleave_primary_key(&arrays, &[(0, 0), (1, 0), (0, 1), (1, 1)]).unwrap();
2340
2341 assert_primary_key_dictionary(
2342 output.as_ref(),
2343 &[b"k1", b"k1", b"k2", b"k2"],
2344 &[b"k1", b"k2"],
2345 );
2346 }
2347
2348 #[test]
2349 fn test_interleave_primary_key_rejects_null_dictionary_value() {
2350 let primary_key = PrimaryKeyArray::try_new(
2351 UInt32Array::from(vec![0]),
2352 Arc::new(BinaryArray::from(vec![None::<&[u8]>])),
2353 )
2354 .unwrap();
2355
2356 let error = interleave_primary_key(&[&primary_key], &[(0, 0)]).unwrap_err();
2357
2358 assert!(error.to_string().contains("null dictionary value"));
2359 }
2360
2361 #[test]
2362 fn test_batch_builder_primary_key_has_no_state_between_builds() {
2363 let long_k1 = vec![b'a'; 4096];
2364 let long_k2 = vec![b'b'; 8192];
2365 let batch0 =
2366 create_test_record_batch(&[long_k1.as_slice()], &[1000], &[1], &[OpType::Put], &[10]);
2367 let batch1 =
2368 create_test_record_batch(&[long_k1.as_slice()], &[1000], &[1], &[OpType::Put], &[11]);
2369 let mut builder = BatchBuilder::new(batch0.schema(), 2, 4);
2370 builder.push_batch(0, batch0);
2371 builder.push_batch(1, batch1);
2372 builder.push_row(0);
2373 builder.push_row(1);
2374
2375 let first = builder.build_record_batch().unwrap().unwrap();
2376 let pk_idx = primary_key_column_index(first.num_columns());
2377 assert_primary_key_dictionary(
2378 first.column(pk_idx).as_ref(),
2379 &[long_k1.as_slice(), long_k1.as_slice()],
2380 &[long_k1.as_slice()],
2381 );
2382
2383 let batch0 =
2384 create_test_record_batch(&[long_k2.as_slice()], &[2000], &[2], &[OpType::Put], &[20]);
2385 let batch1 =
2386 create_test_record_batch(&[long_k2.as_slice()], &[2000], &[2], &[OpType::Put], &[21]);
2387 builder.push_batch(0, batch0);
2388 builder.push_batch(1, batch1);
2389 builder.push_row(0);
2390 builder.push_row(1);
2391
2392 let second = builder.build_record_batch().unwrap().unwrap();
2393 assert_primary_key_dictionary(
2394 second.column(pk_idx).as_ref(),
2395 &[long_k2.as_slice(), long_k2.as_slice()],
2396 &[long_k2.as_slice()],
2397 );
2398 }
2399
2400 #[test]
2401 fn test_row_cursor_comparison() {
2402 let batch1 = create_test_record_batch(
2404 &[b"k1", b"k1"],
2405 &[1000, 2000],
2406 &[22, 21],
2407 &[OpType::Put, OpType::Put],
2408 &[11, 12],
2409 );
2410 let batch2 = create_test_record_batch(
2411 &[b"k1", b"k1"],
2412 &[1000, 2000],
2413 &[23, 20], &[OpType::Put, OpType::Put],
2415 &[11, 12],
2416 );
2417
2418 let columns1 = SortColumns::try_new(&batch1).unwrap();
2419 let columns2 = SortColumns::try_new(&batch2).unwrap();
2420
2421 let cursor1 = RowCursor::new(columns1);
2422 let cursor2 = RowCursor::new(columns2);
2423
2424 assert!(cursor2 < cursor1);
2427 }
2428
2429 #[test]
2430 fn test_row_cursor_caches_current_primary_key() {
2431 let batch1 = create_test_record_batch(&[b"k1"], &[1000], &[1], &[OpType::Put], &[11]);
2432 let batch2 = create_test_record_batch(&[b"k2"], &[1000], &[1], &[OpType::Put], &[12]);
2433 let cursor1 = RowCursor::new(SortColumns::try_new(&batch1).unwrap());
2434 let cursor2 = RowCursor::new(SortColumns::try_new(&batch2).unwrap());
2435
2436 for _ in 0..5 {
2437 assert_eq!(Ordering::Less, cursor1.cmp(&cursor2));
2438 }
2439
2440 assert_eq!(1, cursor1.columns.primary_key_lookups());
2441 assert_eq!(1, cursor2.columns.primary_key_lookups());
2442 }
2443}