1pub(crate) mod chunk_reader;
18pub mod context;
19pub mod part;
20pub mod part_reader;
21mod row_group_reader;
22
23use std::collections::{BTreeMap, HashSet};
24use std::sync::atomic::{AtomicBool, AtomicI64, AtomicU64, AtomicUsize, Ordering};
25use std::sync::{Arc, LazyLock, Mutex, RwLock};
26use std::time::Instant;
27
28fn env_usize(name: &str, default: usize) -> usize {
30 std::env::var(name)
31 .ok()
32 .and_then(|v| v.parse().ok())
33 .unwrap_or(default)
34}
35
36use common_time::Timestamp;
37use datatypes::arrow::datatypes::SchemaRef;
38use mito_codec::key_values::KeyValue;
39use rayon::prelude::*;
40use serde::{Deserialize, Serialize};
41use serde_with::{DisplayFromStr, serde_as};
42use store_api::metadata::RegionMetadataRef;
43use store_api::mito_engine_options::FloatFieldEncoding;
44use store_api::storage::{ColumnId, FileId, RegionId, SequenceNumber, SequenceRange};
45use tokio::sync::Semaphore;
46
47use crate::compaction::{
48 Json2RewritePlans, collect_json2_rewrite_plans, rewrite_json2_batch, rewrite_json2_schema,
49};
50use crate::error::{Result, UnsupportedOperationSnafu};
51use crate::flush::WriteBufferManagerRef;
52use crate::memtable::bulk::context::BulkIterContext;
53use crate::memtable::bulk::part::{
54 BulkPart, BulkPartEncodeMetrics, BulkPartEncoder, MultiBulkPart, UnorderedPart,
55 should_prune_bulk_part,
56};
57use crate::memtable::bulk::part_reader::BulkPartBatchIter;
58use crate::memtable::stats::WriteMetrics;
59use crate::memtable::{
60 AllocTracker, BoxedBatchIterator, BoxedRecordBatchIterator, EncodedBulkPart, EncodedRange,
61 IterBuilder, KeyValues, MemScanMetrics, Memtable, MemtableBuilder, MemtableId, MemtableRange,
62 MemtableRangeContext, MemtableRanges, MemtableRef, MemtableStats, RangesOptions,
63 group_by_sequence,
64};
65use crate::read::flat_dedup::{FlatDedupIterator, FlatLastNonNull, FlatLastRow};
66use crate::read::flat_merge::FlatMergeIterator;
67use crate::region::options::MergeMode;
68use crate::sst::parquet::DEFAULT_ROW_GROUP_SIZE;
69use crate::sst::parquet::flat_format::field_column_start;
70
71const DEFAULT_MERGE_THRESHOLD: usize = 16;
73
74static MERGE_THRESHOLD: LazyLock<usize> =
76 LazyLock::new(|| env_usize("GREPTIME_BULK_MERGE_THRESHOLD", DEFAULT_MERGE_THRESHOLD));
77
78const DEFAULT_MAX_MERGE_GROUPS: usize = 32;
80
81static MAX_MERGE_GROUPS: LazyLock<usize> =
83 LazyLock::new(|| env_usize("GREPTIME_BULK_MAX_MERGE_GROUPS", DEFAULT_MAX_MERGE_GROUPS));
84
85pub(crate) static ENCODE_ROW_THRESHOLD: LazyLock<usize> = LazyLock::new(|| {
88 env_usize(
89 "GREPTIME_BULK_ENCODE_ROW_THRESHOLD",
90 10 * DEFAULT_ROW_GROUP_SIZE,
91 )
92});
93
94const DEFAULT_ENCODE_BYTES_THRESHOLD: usize = 64 * 1024 * 1024;
96
97const MAX_ENCODE_BYTES_THRESHOLD: usize = 512 * 1024 * 1024;
99
100const ENCODE_BYTES_THRESHOLD_DIVISOR: usize = 32;
102
103static ENCODE_BYTES_THRESHOLD_OVERRIDE: LazyLock<Option<usize>> = LazyLock::new(|| {
105 std::env::var("GREPTIME_BULK_ENCODE_BYTES_THRESHOLD")
106 .ok()
107 .and_then(|v| v.parse().ok())
108});
109
110fn adaptive_encode_bytes_threshold(global_write_buffer_bytes: usize) -> usize {
113 (global_write_buffer_bytes / ENCODE_BYTES_THRESHOLD_DIVISOR)
114 .clamp(DEFAULT_ENCODE_BYTES_THRESHOLD, MAX_ENCODE_BYTES_THRESHOLD)
115}
116
117fn default_encode_bytes_threshold() -> usize {
119 ENCODE_BYTES_THRESHOLD_OVERRIDE.unwrap_or(DEFAULT_ENCODE_BYTES_THRESHOLD)
120}
121
122#[serde_as]
124#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
125#[serde(default)]
126pub struct BulkMemtableConfig {
127 #[serde_as(as = "DisplayFromStr")]
129 pub merge_threshold: usize,
130 #[serde_as(as = "DisplayFromStr")]
132 pub encode_row_threshold: usize,
133 #[serde_as(as = "DisplayFromStr")]
135 pub encode_bytes_threshold: usize,
136 #[serde_as(as = "DisplayFromStr")]
138 pub max_merge_groups: usize,
139}
140
141impl Default for BulkMemtableConfig {
142 fn default() -> Self {
143 Self {
144 merge_threshold: *MERGE_THRESHOLD,
145 encode_row_threshold: *ENCODE_ROW_THRESHOLD,
146 encode_bytes_threshold: default_encode_bytes_threshold(),
147 max_merge_groups: *MAX_MERGE_GROUPS,
148 }
149 .sanitize()
150 }
151}
152
153impl BulkMemtableConfig {
154 pub(crate) fn default_for_write_buffer_size(global_write_buffer_bytes: usize) -> Self {
156 Self {
157 encode_bytes_threshold: ENCODE_BYTES_THRESHOLD_OVERRIDE
158 .unwrap_or_else(|| adaptive_encode_bytes_threshold(global_write_buffer_bytes)),
159 ..Default::default()
160 }
161 }
162
163 fn sanitize(mut self) -> Self {
164 if self.merge_threshold == 0 {
165 self.merge_threshold = DEFAULT_MERGE_THRESHOLD;
166 }
167 self
168 }
169}
170
171enum MergedPart {
173 Multi(MultiBulkPart),
175 Encoded(EncodedBulkPart),
177}
178
179struct CollectedParts {
181 groups: Vec<Vec<PartToMerge>>,
183}
184
185#[derive(Default)]
187struct BulkParts {
188 unordered_part: UnorderedPart,
190 parts: Vec<BulkPartWrapper>,
192}
193
194impl BulkParts {
195 fn num_parts(&self) -> usize {
197 let unordered_count = if self.unordered_part.is_empty() { 0 } else { 1 };
198 self.parts.len() + unordered_count
199 }
200
201 fn is_empty(&self) -> bool {
203 self.unordered_part.is_empty() && self.parts.is_empty()
204 }
205
206 fn may_merge_parts(&self, merge_threshold: usize) -> bool {
209 let merge_threshold = merge_threshold.max(2);
210 self.parts.len() >= merge_threshold
211 && self
212 .parts
213 .iter()
214 .filter(|wrapper| !wrapper.merging)
215 .take(merge_threshold)
216 .count()
217 == merge_threshold
218 }
219
220 fn is_merge_candidate_by_size(
221 is_encoded: bool,
222 estimated_size: usize,
223 encode_bytes_threshold: usize,
224 ) -> bool {
225 !is_encoded || estimated_size <= encode_bytes_threshold
226 }
227
228 fn should_compact_unordered_part(&self, encode_bytes_threshold: usize) -> bool {
230 self.unordered_part.should_compact()
231 || self.unordered_part.estimated_bytes() > encode_bytes_threshold
232 }
233
234 fn seal_unordered(&mut self, metadata: &RegionMetadataRef) -> Result<()> {
236 if let Some(part) = self.unordered_part.to_bulk_part(metadata)? {
237 self.parts.push(BulkPartWrapper {
238 part: PartToMerge::Bulk {
239 part,
240 file_id: FileId::random(),
241 },
242 merging: false,
243 });
244 self.unordered_part.clear();
245 }
246 Ok(())
247 }
248
249 fn pick_merge_groups(
252 &self,
253 merge_threshold: usize,
254 max_merge_groups: usize,
255 encode_bytes_threshold: usize,
256 ) -> Vec<Vec<usize>> {
257 if self.parts.len() < merge_threshold.max(2) || max_merge_groups == 0 {
258 return Vec::new();
259 }
260 let intervals = self
263 .parts
264 .iter()
265 .enumerate()
266 .map(|(index, wrapper)| {
267 let part = &wrapper.part;
268 (Some(index), (part.min_sequence(), part.max_sequence()))
269 })
270 .chain(
271 self.unordered_part
272 .sequence_bounds()
273 .map(|bounds| (None, bounds)),
274 )
275 .collect();
276 let components = group_by_sequence(intervals, |(_, bounds)| *bounds);
277 let mut groups = Vec::new();
278 let mut pending = Vec::new();
279 for component in components {
280 if groups.len() >= max_merge_groups {
281 break;
282 }
283 let Some(component) = component
284 .into_iter()
285 .map(|(index, _)| index)
286 .collect::<Option<Vec<_>>>()
287 else {
288 pending.clear();
289 continue;
290 };
291 let busy = component.iter().any(|&i| self.parts[i].merging);
292 let oversized = component.len() == 1 && {
293 let part = &self.parts[component[0]].part;
294 !Self::is_merge_candidate_by_size(
295 part.is_encoded(),
296 part.estimated_size(),
297 encode_bytes_threshold,
298 )
299 };
300 if busy || oversized {
301 pending.clear();
302 continue;
303 }
304 pending.extend(component);
305 if pending.len() >= merge_threshold.max(2) {
306 groups.push(std::mem::take(&mut pending));
307 }
308 }
309 groups
310 }
311
312 fn collect_parts_to_merge(
313 &mut self,
314 merge_threshold: usize,
315 max_merge_groups: usize,
316 encode_bytes_threshold: usize,
317 ) -> CollectedParts {
318 let groups = self
319 .pick_merge_groups(merge_threshold, max_merge_groups, encode_bytes_threshold)
320 .into_iter()
321 .map(|group| {
322 group
323 .into_iter()
324 .map(|i| {
325 let wrapper = &mut self.parts[i];
326 wrapper.merging = true;
327 wrapper.part.clone()
328 })
329 .collect()
330 })
331 .collect();
332 CollectedParts { groups }
333 }
334
335 fn install_merged_parts<I>(
338 &mut self,
339 merged_parts: I,
340 merged_file_ids: &HashSet<FileId>,
341 ) -> usize
342 where
343 I: IntoIterator<Item = MergedPart>,
344 {
345 let mut total_output_rows = 0;
346
347 for merged_part in merged_parts {
348 match merged_part {
349 MergedPart::Encoded(encoded_part) => {
350 total_output_rows += encoded_part.metadata().num_rows;
351 self.parts.push(BulkPartWrapper {
352 part: PartToMerge::Encoded {
353 part: encoded_part,
354 file_id: FileId::random(),
355 },
356 merging: false,
357 });
358 }
359 MergedPart::Multi(multi_part) => {
360 total_output_rows += multi_part.num_rows();
361 self.parts.push(BulkPartWrapper {
362 part: PartToMerge::Multi {
363 part: multi_part,
364 file_id: FileId::random(),
365 },
366 merging: false,
367 });
368 }
369 }
370 }
371
372 self.parts
373 .retain(|wrapper| !merged_file_ids.contains(&wrapper.file_id()));
374
375 total_output_rows
376 }
377
378 fn reset_merging_flags(&mut self, file_ids: &HashSet<FileId>) {
381 for wrapper in &mut self.parts {
382 if file_ids.contains(&wrapper.file_id()) {
383 wrapper.merging = false;
384 }
385 }
386 }
387}
388
389struct MergingFlagsGuard<'a> {
392 bulk_parts: &'a RwLock<BulkParts>,
393 file_ids: &'a HashSet<FileId>,
394 success: bool,
395}
396
397impl<'a> MergingFlagsGuard<'a> {
398 fn new(bulk_parts: &'a RwLock<BulkParts>, file_ids: &'a HashSet<FileId>) -> Self {
400 Self {
401 bulk_parts,
402 file_ids,
403 success: false,
404 }
405 }
406
407 fn mark_success(&mut self) {
410 self.success = true;
411 }
412}
413
414impl<'a> Drop for MergingFlagsGuard<'a> {
415 fn drop(&mut self) {
416 if !self.success
417 && let Ok(mut parts) = self.bulk_parts.write()
418 {
419 parts.reset_merging_flags(self.file_ids);
420 }
421 }
422}
423
424pub struct BulkMemtable {
426 id: MemtableId,
427 config: BulkMemtableConfig,
429 parts: Arc<RwLock<BulkParts>>,
430 metadata: RegionMetadataRef,
431 alloc_tracker: AllocTracker,
432 max_timestamp: AtomicI64,
433 min_timestamp: AtomicI64,
434 max_sequence: AtomicU64,
435 min_sequence: AtomicU64,
436 num_rows: AtomicUsize,
437 compactor: Arc<Mutex<MemtableCompactor>>,
439 compact_dispatcher: Option<Arc<CompactDispatcher>>,
441 compacting: Arc<AtomicBool>,
443 append_mode: bool,
445 merge_mode: MergeMode,
447 row_group_size: usize,
449 float_field_encoding: FloatFieldEncoding,
450}
451
452impl std::fmt::Debug for BulkMemtable {
453 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
454 f.debug_struct("BulkMemtable")
455 .field("id", &self.id)
456 .field("num_rows", &self.num_rows.load(Ordering::Relaxed))
457 .field("min_timestamp", &self.min_timestamp.load(Ordering::Relaxed))
458 .field("max_timestamp", &self.max_timestamp.load(Ordering::Relaxed))
459 .field("max_sequence", &self.max_sequence.load(Ordering::Relaxed))
460 .finish()
461 }
462}
463
464impl Memtable for BulkMemtable {
465 fn id(&self) -> MemtableId {
466 self.id
467 }
468
469 fn write(&self, _kvs: &KeyValues) -> Result<()> {
470 UnsupportedOperationSnafu {
471 err_msg: "write() is not supported for bulk memtable",
472 }
473 .fail()
474 }
475
476 fn write_one(&self, _key_value: KeyValue) -> Result<()> {
477 UnsupportedOperationSnafu {
478 err_msg: "write_one() is not supported for bulk memtable",
479 }
480 .fail()
481 }
482
483 fn write_bulk(&self, fragment: BulkPart) -> Result<()> {
484 let local_metrics = WriteMetrics {
485 key_bytes: 0,
486 value_bytes: fragment.estimated_size(),
487 min_ts: fragment.min_timestamp,
488 max_ts: fragment.max_timestamp,
489 num_rows: fragment.num_rows(),
490 max_sequence: fragment.sequence,
491 min_sequence: fragment.min_sequence,
492 };
493
494 {
495 let mut bulk_parts = self.parts.write().unwrap();
496
497 let fragment_size = fragment.estimated_size();
498 if bulk_parts.unordered_part.should_accept(fragment.num_rows())
500 && fragment_size <= self.config.encode_bytes_threshold
501 {
502 bulk_parts.unordered_part.push(fragment);
503
504 if bulk_parts.should_compact_unordered_part(self.config.encode_bytes_threshold) {
506 bulk_parts.seal_unordered(&self.metadata)?;
507 }
508 } else {
509 bulk_parts.parts.push(BulkPartWrapper {
510 part: PartToMerge::Bulk {
511 part: fragment,
512 file_id: FileId::random(),
513 },
514 merging: false,
515 });
516 }
517
518 self.update_stats(local_metrics);
523 }
524
525 self.schedule_compact();
526
527 Ok(())
528 }
529
530 fn ranges(
531 &self,
532 projection: Option<&[ColumnId]>,
533 options: RangesOptions,
534 ) -> Result<MemtableRanges> {
535 let predicate = options.predicate;
536 let sequence = options.sequence;
537 let mut ranges = BTreeMap::new();
538 let mut range_id = 0;
539
540 let context = Arc::new(BulkIterContext::new_with_pre_filter_mode(
542 self.metadata.clone(),
543 projection,
544 predicate.predicate().cloned(),
545 options.for_flush,
546 options.pre_filter_mode,
547 options.batch_size,
548 )?);
549
550 {
552 let bulk_parts = self.parts.read().unwrap();
553
554 if !bulk_parts.unordered_part.is_empty()
556 && let Some(unordered_bulk_part) =
557 bulk_parts.unordered_part.to_bulk_part(&self.metadata)?
558 {
559 let part_stats = unordered_bulk_part.to_memtable_stats(&self.metadata);
560 let range = MemtableRange::new(
561 Arc::new(MemtableRangeContext::new(
562 self.id,
563 Box::new(BulkRangeIterBuilder {
564 part: unordered_bulk_part,
565 context: context.clone(),
566 sequence,
567 }),
568 predicate.clone(),
569 )),
570 part_stats,
571 );
572 ranges.insert(range_id, range);
573 range_id += 1;
574 }
575
576 for part_wrapper in bulk_parts.parts.iter() {
578 if part_wrapper.part.num_rows() == 0 {
580 continue;
581 }
582
583 let part_stats = part_wrapper.part.to_memtable_stats(&self.metadata);
584 let iter_builder: Box<dyn IterBuilder> = match &part_wrapper.part {
585 PartToMerge::Bulk { part, .. } => Box::new(BulkRangeIterBuilder {
586 part: part.clone(),
587 context: context.clone(),
588 sequence,
589 }),
590 PartToMerge::Multi { part, .. } => Box::new(MultiBulkRangeIterBuilder {
591 part: part.clone(),
592 context: context.clone(),
593 sequence,
594 }),
595 PartToMerge::Encoded { part, file_id } => {
596 Box::new(EncodedBulkRangeIterBuilder {
597 file_id: *file_id,
598 part: part.clone(),
599 context: context.clone(),
600 sequence,
601 })
602 }
603 };
604
605 let range = MemtableRange::new(
606 Arc::new(MemtableRangeContext::new(
607 self.id,
608 iter_builder,
609 predicate.clone(),
610 )),
611 part_stats,
612 );
613 ranges.insert(range_id, range);
614 range_id += 1;
615 }
616 }
617
618 Ok(MemtableRanges { ranges })
619 }
620
621 fn is_empty(&self) -> bool {
622 let bulk_parts = self.parts.read().unwrap();
623 bulk_parts.is_empty()
624 }
625
626 fn freeze(&self) -> Result<()> {
627 self.alloc_tracker.done_allocating();
628 Ok(())
629 }
630
631 fn stats(&self) -> MemtableStats {
632 let estimated_bytes = self.alloc_tracker.bytes_allocated();
633
634 if estimated_bytes == 0 || self.num_rows.load(Ordering::Relaxed) == 0 {
635 return MemtableStats {
636 estimated_bytes,
637 time_range: None,
638 num_rows: 0,
639 num_ranges: 0,
640 max_sequence: 0,
641 min_sequence: 0,
642 series_count: 0,
643 };
644 }
645
646 let ts_type = self
647 .metadata
648 .time_index_column()
649 .column_schema
650 .data_type
651 .clone()
652 .as_timestamp()
653 .expect("Timestamp column must have timestamp type");
654 let max_timestamp = ts_type.create_timestamp(self.max_timestamp.load(Ordering::Relaxed));
655 let min_timestamp = ts_type.create_timestamp(self.min_timestamp.load(Ordering::Relaxed));
656
657 let num_ranges = self.parts.read().unwrap().num_parts();
658
659 MemtableStats {
660 estimated_bytes,
661 time_range: Some((min_timestamp, max_timestamp)),
662 num_rows: self.num_rows.load(Ordering::Relaxed),
663 num_ranges,
664 max_sequence: self.max_sequence.load(Ordering::Relaxed),
665 min_sequence: self.min_sequence.load(Ordering::Relaxed),
666 series_count: self.estimated_series_count(),
667 }
668 }
669
670 fn min_sequence(&self) -> SequenceNumber {
671 if self.alloc_tracker.bytes_allocated() == 0 || self.num_rows.load(Ordering::Relaxed) == 0 {
672 return 0;
673 }
674 self.min_sequence.load(Ordering::Relaxed)
675 }
676
677 fn fork(&self, id: MemtableId, metadata: &RegionMetadataRef) -> MemtableRef {
678 self.fork_inner(id, metadata)
679 }
680
681 fn compact(&self, for_flush: bool) -> Result<()> {
682 let mut compactor = self.compactor.lock().unwrap();
683
684 if for_flush {
685 return Ok(());
686 }
687
688 compactor.merge_parts(
689 &self.parts,
690 &self.metadata,
691 !self.append_mode,
692 self.merge_mode,
693 )
694 }
695}
696
697impl BulkMemtable {
698 fn fork_inner(&self, id: MemtableId, metadata: &RegionMetadataRef) -> Arc<BulkMemtable> {
699 Arc::new(Self {
700 id,
701 config: self.config.clone(),
702 parts: Arc::new(RwLock::new(BulkParts::default())),
703 metadata: metadata.clone(),
704 alloc_tracker: AllocTracker::new(self.alloc_tracker.write_buffer_manager()),
705 max_timestamp: AtomicI64::new(i64::MIN),
706 min_timestamp: AtomicI64::new(i64::MAX),
707 max_sequence: AtomicU64::new(0),
708 min_sequence: AtomicU64::new(u64::MAX),
709 num_rows: AtomicUsize::new(0),
710 compactor: Arc::new(Mutex::new(MemtableCompactor::new(
711 metadata.region_id,
712 id,
713 self.config.clone(),
714 self.row_group_size,
715 self.float_field_encoding,
716 ))),
717 compact_dispatcher: self.compact_dispatcher.clone(),
718 compacting: Arc::new(AtomicBool::new(false)),
719 append_mode: self.append_mode,
720 merge_mode: self.merge_mode,
721 row_group_size: self.row_group_size,
722 float_field_encoding: self.float_field_encoding,
723 })
724 }
725
726 pub fn new(
728 id: MemtableId,
729 config: BulkMemtableConfig,
730 metadata: RegionMetadataRef,
731 write_buffer_manager: Option<WriteBufferManagerRef>,
732 compact_dispatcher: Option<Arc<CompactDispatcher>>,
733 append_mode: bool,
734 merge_mode: MergeMode,
735 ) -> Self {
736 Self::new_with_row_group_size(
737 id,
738 config,
739 metadata,
740 write_buffer_manager,
741 compact_dispatcher,
742 append_mode,
743 merge_mode,
744 DEFAULT_ROW_GROUP_SIZE,
745 )
746 }
747
748 #[allow(clippy::too_many_arguments)]
750 pub fn new_with_row_group_size(
751 id: MemtableId,
752 config: BulkMemtableConfig,
753 metadata: RegionMetadataRef,
754 write_buffer_manager: Option<WriteBufferManagerRef>,
755 compact_dispatcher: Option<Arc<CompactDispatcher>>,
756 append_mode: bool,
757 merge_mode: MergeMode,
758 row_group_size: usize,
759 ) -> Self {
760 Self::new_with_row_group_size_and_encoding(
761 id,
762 config,
763 metadata,
764 write_buffer_manager,
765 compact_dispatcher,
766 append_mode,
767 merge_mode,
768 row_group_size,
769 FloatFieldEncoding::default(),
770 )
771 }
772
773 #[allow(clippy::too_many_arguments)]
775 fn new_with_row_group_size_and_encoding(
776 id: MemtableId,
777 config: BulkMemtableConfig,
778 metadata: RegionMetadataRef,
779 write_buffer_manager: Option<WriteBufferManagerRef>,
780 compact_dispatcher: Option<Arc<CompactDispatcher>>,
781 append_mode: bool,
782 merge_mode: MergeMode,
783 row_group_size: usize,
784 float_field_encoding: FloatFieldEncoding,
785 ) -> Self {
786 let config = config.sanitize();
787 let region_id = metadata.region_id;
788 Self {
789 id,
790 config: config.clone(),
791 parts: Arc::new(RwLock::new(BulkParts::default())),
792 metadata,
793 alloc_tracker: AllocTracker::new(write_buffer_manager),
794 max_timestamp: AtomicI64::new(i64::MIN),
795 min_timestamp: AtomicI64::new(i64::MAX),
796 max_sequence: AtomicU64::new(0),
797 min_sequence: AtomicU64::new(u64::MAX),
798 num_rows: AtomicUsize::new(0),
799 compactor: Arc::new(Mutex::new(MemtableCompactor::new(
800 region_id,
801 id,
802 config,
803 row_group_size,
804 float_field_encoding,
805 ))),
806 compact_dispatcher,
807 compacting: Arc::new(AtomicBool::new(false)),
808 append_mode,
809 merge_mode,
810 row_group_size,
811 float_field_encoding,
812 }
813 }
814
815 #[cfg(test)]
817 pub fn set_unordered_part_threshold(&self, threshold: usize) {
818 self.parts
819 .write()
820 .unwrap()
821 .unordered_part
822 .set_threshold(threshold);
823 }
824
825 #[cfg(test)]
827 pub fn set_unordered_part_compact_threshold(&self, compact_threshold: usize) {
828 self.parts
829 .write()
830 .unwrap()
831 .unordered_part
832 .set_compact_threshold(compact_threshold);
833 }
834
835 fn update_stats(&self, stats: WriteMetrics) {
839 self.alloc_tracker
840 .on_allocation(stats.key_bytes + stats.value_bytes);
841
842 self.max_timestamp
843 .fetch_max(stats.max_ts, Ordering::Relaxed);
844 self.min_timestamp
845 .fetch_min(stats.min_ts, Ordering::Relaxed);
846 self.max_sequence
847 .fetch_max(stats.max_sequence, Ordering::Relaxed);
848 self.min_sequence
849 .fetch_min(stats.min_sequence, Ordering::Relaxed);
850 self.num_rows.fetch_add(stats.num_rows, Ordering::Relaxed);
851 }
852
853 fn estimated_series_count(&self) -> usize {
855 let bulk_parts = self.parts.read().unwrap();
856 bulk_parts
857 .parts
858 .iter()
859 .map(|part_wrapper| part_wrapper.part.series_count())
860 .sum()
861 }
862
863 fn schedule_compact(&self) {
865 if self.config.max_merge_groups == 0 || self.compacting.load(Ordering::Relaxed) {
866 return;
867 }
868 if !self
869 .parts
870 .read()
871 .unwrap()
872 .may_merge_parts(self.config.merge_threshold)
873 {
874 return;
875 }
876 let Some(guard) = CompactTaskGuard::try_new(self.compacting.clone()) else {
877 return;
878 };
879 if let Some(dispatcher) = &self.compact_dispatcher {
880 let task = MemCompactTask {
881 metadata: self.metadata.clone(),
882 parts: self.parts.clone(),
883 _guard: guard,
884 compactor: self.compactor.clone(),
885 append_mode: self.append_mode,
886 merge_mode: self.merge_mode,
887 };
888
889 dispatcher.dispatch_compact(task);
890 } else {
891 if let Err(e) = self.compact(false) {
893 common_telemetry::error!(e; "Failed to compact table");
894 }
895 }
896 }
897}
898
899pub struct BulkRangeIterBuilder {
901 pub part: BulkPart,
902 pub context: Arc<BulkIterContext>,
903 pub sequence: Option<SequenceRange>,
904}
905
906struct MultiBulkRangeIterBuilder {
908 part: MultiBulkPart,
909 context: Arc<BulkIterContext>,
910 sequence: Option<SequenceRange>,
911}
912
913impl IterBuilder for BulkRangeIterBuilder {
914 fn build(&self, _metrics: Option<MemScanMetrics>) -> Result<BoxedBatchIterator> {
915 UnsupportedOperationSnafu {
916 err_msg: "BatchIterator is not supported for bulk memtable",
917 }
918 .fail()
919 }
920
921 fn is_record_batch(&self) -> bool {
922 true
923 }
924
925 fn build_record_batch(
926 &self,
927 _time_range: Option<(Timestamp, Timestamp)>,
928 metrics: Option<MemScanMetrics>,
929 ) -> Result<BoxedRecordBatchIterator> {
930 let metadata = self.context.read_format().metadata();
931 if should_prune_bulk_part(&self.part.batch, &self.context, metadata) {
932 return Ok(Box::new(std::iter::empty()));
933 }
934
935 let series_count = self.part.estimated_series_count();
936 let iter = BulkPartBatchIter::from_single(
937 self.part.batch.clone(),
938 self.context.clone(),
939 self.sequence,
940 series_count,
941 metrics,
942 );
943
944 Ok(Box::new(iter))
945 }
946
947 fn record_batch_schema_hint(&self) -> Option<SchemaRef> {
948 Some(self.part.schema())
949 }
950
951 fn encoded_range(&self) -> Option<EncodedRange> {
952 None
953 }
954}
955
956impl IterBuilder for MultiBulkRangeIterBuilder {
957 fn build(&self, _metrics: Option<MemScanMetrics>) -> Result<BoxedBatchIterator> {
958 UnsupportedOperationSnafu {
959 err_msg: "BatchIterator is not supported for multi bulk memtable",
960 }
961 .fail()
962 }
963
964 fn is_record_batch(&self) -> bool {
965 true
966 }
967
968 fn build_record_batch(
969 &self,
970 _time_range: Option<(Timestamp, Timestamp)>,
971 metrics: Option<MemScanMetrics>,
972 ) -> Result<BoxedRecordBatchIterator> {
973 match self
974 .part
975 .read(self.context.clone(), self.sequence, metrics)?
976 {
977 Some(iter) => Ok(iter),
978 None => Ok(Box::new(std::iter::empty())),
980 }
981 }
982
983 fn record_batch_schema_hint(&self) -> Option<SchemaRef> {
984 self.part.schemas().next()
985 }
986
987 fn encoded_range(&self) -> Option<EncodedRange> {
988 None
989 }
990}
991
992struct EncodedBulkRangeIterBuilder {
994 file_id: FileId,
995 part: EncodedBulkPart,
996 context: Arc<BulkIterContext>,
997 sequence: Option<SequenceRange>,
998}
999
1000impl IterBuilder for EncodedBulkRangeIterBuilder {
1001 fn build(&self, _metrics: Option<MemScanMetrics>) -> Result<BoxedBatchIterator> {
1002 UnsupportedOperationSnafu {
1003 err_msg: "BatchIterator is not supported for encoded bulk memtable",
1004 }
1005 .fail()
1006 }
1007
1008 fn is_record_batch(&self) -> bool {
1009 true
1010 }
1011
1012 fn build_record_batch(
1013 &self,
1014 _time_range: Option<(Timestamp, Timestamp)>,
1015 metrics: Option<MemScanMetrics>,
1016 ) -> Result<BoxedRecordBatchIterator> {
1017 if let Some(iter) = self
1018 .part
1019 .read(self.context.clone(), self.sequence, metrics)?
1020 {
1021 Ok(iter)
1022 } else {
1023 Ok(Box::new(std::iter::empty()))
1025 }
1026 }
1027
1028 fn record_batch_schema_hint(&self) -> Option<SchemaRef> {
1029 Some(self.part.schema())
1030 }
1031
1032 fn encoded_range(&self) -> Option<EncodedRange> {
1033 Some(EncodedRange {
1034 data: self.part.data().clone(),
1035 sst_info: self.part.to_sst_info(self.file_id),
1036 })
1037 }
1038}
1039
1040struct BulkPartWrapper {
1041 part: PartToMerge,
1043 merging: bool,
1045}
1046
1047impl BulkPartWrapper {
1048 fn file_id(&self) -> FileId {
1050 self.part.file_id()
1051 }
1052}
1053
1054#[derive(Clone)]
1056enum PartToMerge {
1057 Bulk { part: BulkPart, file_id: FileId },
1059 Multi {
1061 part: MultiBulkPart,
1062 file_id: FileId,
1063 },
1064 Encoded {
1066 part: EncodedBulkPart,
1067 file_id: FileId,
1068 },
1069}
1070
1071impl PartToMerge {
1072 fn file_id(&self) -> FileId {
1074 match self {
1075 PartToMerge::Bulk { file_id, .. } => *file_id,
1076 PartToMerge::Multi { file_id, .. } => *file_id,
1077 PartToMerge::Encoded { file_id, .. } => *file_id,
1078 }
1079 }
1080
1081 fn min_timestamp(&self) -> i64 {
1083 match self {
1084 PartToMerge::Bulk { part, .. } => part.min_timestamp,
1085 PartToMerge::Multi { part, .. } => part.min_timestamp(),
1086 PartToMerge::Encoded { part, .. } => part.metadata().min_timestamp,
1087 }
1088 }
1089
1090 fn max_timestamp(&self) -> i64 {
1092 match self {
1093 PartToMerge::Bulk { part, .. } => part.max_timestamp,
1094 PartToMerge::Multi { part, .. } => part.max_timestamp(),
1095 PartToMerge::Encoded { part, .. } => part.metadata().max_timestamp,
1096 }
1097 }
1098
1099 fn num_rows(&self) -> usize {
1101 match self {
1102 PartToMerge::Bulk { part, .. } => part.num_rows(),
1103 PartToMerge::Multi { part, .. } => part.num_rows(),
1104 PartToMerge::Encoded { part, .. } => part.metadata().num_rows,
1105 }
1106 }
1107
1108 fn min_sequence(&self) -> SequenceNumber {
1110 match self {
1111 PartToMerge::Bulk { part, .. } => part.min_sequence,
1112 PartToMerge::Multi { part, .. } => part.min_sequence(),
1113 PartToMerge::Encoded { part, .. } => part.metadata().min_sequence,
1114 }
1115 }
1116
1117 fn max_sequence(&self) -> u64 {
1119 match self {
1120 PartToMerge::Bulk { part, .. } => part.sequence,
1121 PartToMerge::Multi { part, .. } => part.max_sequence(),
1122 PartToMerge::Encoded { part, .. } => part.metadata().max_sequence,
1123 }
1124 }
1125
1126 fn series_count(&self) -> usize {
1128 match self {
1129 PartToMerge::Bulk { part, .. } => part.estimated_series_count(),
1130 PartToMerge::Multi { part, .. } => part.series_count(),
1131 PartToMerge::Encoded { part, .. } => part.metadata().num_series as usize,
1132 }
1133 }
1134
1135 fn is_encoded(&self) -> bool {
1137 matches!(self, PartToMerge::Encoded { .. })
1138 }
1139
1140 fn estimated_size(&self) -> usize {
1142 match self {
1143 PartToMerge::Bulk { part, .. } => part.estimated_size(),
1144 PartToMerge::Multi { part, .. } => part.estimated_size(),
1145 PartToMerge::Encoded { part, .. } => part.size_bytes(),
1146 }
1147 }
1148
1149 fn batch_size_statistic(&self) -> Option<(u64, u64)> {
1151 match self {
1152 PartToMerge::Bulk { part, .. } => {
1153 Some((part.num_rows() as u64, part.estimated_size() as u64))
1154 }
1155 PartToMerge::Multi { part, .. } => {
1156 Some((part.num_rows() as u64, part.estimated_size() as u64))
1157 }
1158 PartToMerge::Encoded { part, .. } => {
1159 crate::batch_size::largest_row_group_statistic(&part.metadata().parquet_metadata)
1160 }
1161 }
1162 }
1163
1164 fn to_memtable_stats(&self, region_metadata: &RegionMetadataRef) -> MemtableStats {
1166 match self {
1167 PartToMerge::Bulk { part, .. } => part.to_memtable_stats(region_metadata),
1168 PartToMerge::Multi { part, .. } => part.to_memtable_stats(region_metadata),
1169 PartToMerge::Encoded { part, .. } => part.to_memtable_stats(),
1170 }
1171 }
1172
1173 fn arrow_schema(&self) -> SchemaRef {
1175 match self {
1176 PartToMerge::Bulk { part, .. } => part.schema(),
1177 PartToMerge::Multi { part, .. } => part
1180 .schemas()
1181 .next()
1182 .expect("MultiBulkPart must contain at least one record batch"),
1183 PartToMerge::Encoded { part, .. } => part.schema(),
1184 }
1185 }
1186
1187 fn create_iterator(
1189 self,
1190 context: Arc<BulkIterContext>,
1191 plans: Arc<Json2RewritePlans>,
1192 ) -> Result<Option<BoxedRecordBatchIterator>> {
1193 let iter = match self {
1194 PartToMerge::Bulk { part, .. } => {
1195 let series_count = part.estimated_series_count();
1196 let iter = BulkPartBatchIter::from_single(
1197 part.batch,
1198 context,
1199 None, series_count,
1201 None, );
1203 Some(Box::new(iter) as BoxedRecordBatchIterator)
1204 }
1205 PartToMerge::Multi { part, .. } => part.read(context, None, None)?,
1206 PartToMerge::Encoded { part, .. } => part.read(context, None, None)?,
1207 };
1208 if plans.is_empty() {
1209 Ok(iter)
1210 } else {
1211 Ok(iter.map(|x| {
1212 Box::new(x.map(move |batch| rewrite_json2_batch(batch?, &plans)))
1213 as BoxedRecordBatchIterator
1214 }))
1215 }
1216 }
1217}
1218
1219struct MemtableCompactor {
1220 region_id: RegionId,
1221 memtable_id: MemtableId,
1222 config: BulkMemtableConfig,
1224 row_group_size: usize,
1226 float_field_encoding: FloatFieldEncoding,
1227}
1228
1229impl MemtableCompactor {
1230 fn new(
1232 region_id: RegionId,
1233 memtable_id: MemtableId,
1234 config: BulkMemtableConfig,
1235 row_group_size: usize,
1236 float_field_encoding: FloatFieldEncoding,
1237 ) -> Self {
1238 Self {
1239 region_id,
1240 memtable_id,
1241 config,
1242 row_group_size,
1243 float_field_encoding,
1244 }
1245 }
1246
1247 fn merge_parts(
1249 &mut self,
1250 bulk_parts: &RwLock<BulkParts>,
1251 metadata: &RegionMetadataRef,
1252 dedup: bool,
1253 merge_mode: MergeMode,
1254 ) -> Result<()> {
1255 let start = Instant::now();
1256
1257 let collected = {
1258 let mut parts = bulk_parts.write().unwrap();
1259 parts.collect_parts_to_merge(
1260 self.config.merge_threshold,
1261 self.config.max_merge_groups,
1262 self.config.encode_bytes_threshold,
1263 )
1264 };
1265
1266 if collected.groups.is_empty() {
1267 return Ok(());
1268 }
1269
1270 let merged_file_ids: HashSet<FileId> = collected
1272 .groups
1273 .iter()
1274 .flatten()
1275 .map(|part| part.file_id())
1276 .collect();
1277 let mut guard = MergingFlagsGuard::new(bulk_parts, &merged_file_ids);
1278
1279 let num_groups = collected.groups.len();
1280 let num_parts: usize = collected.groups.iter().map(|g| g.len()).sum();
1281
1282 let encode_row_threshold = self.config.encode_row_threshold;
1283 let encode_bytes_threshold = self.config.encode_bytes_threshold;
1284 let row_group_size = self.row_group_size;
1285 let float_field_encoding = self.float_field_encoding;
1286
1287 let merged_parts = collected
1289 .groups
1290 .into_par_iter()
1291 .map(|group| {
1292 Self::merge_parts_group(
1293 group,
1294 metadata,
1295 dedup,
1296 merge_mode,
1297 encode_row_threshold,
1298 encode_bytes_threshold,
1299 row_group_size,
1300 float_field_encoding,
1301 )
1302 })
1303 .collect::<Result<Vec<Option<MergedPart>>>>()?;
1304
1305 let total_output_rows = {
1307 let mut parts = bulk_parts.write().unwrap();
1308 parts.install_merged_parts(merged_parts.into_iter().flatten(), &merged_file_ids)
1309 };
1310
1311 guard.mark_success();
1312
1313 common_telemetry::debug!(
1314 "BulkMemtable {} {} concurrent compact {} groups, {} parts, {} rows, cost: {:?}",
1315 self.region_id,
1316 self.memtable_id,
1317 num_groups,
1318 num_parts,
1319 total_output_rows,
1320 start.elapsed()
1321 );
1322
1323 Ok(())
1324 }
1325
1326 #[allow(clippy::too_many_arguments)]
1328 fn merge_parts_group(
1329 parts_to_merge: Vec<PartToMerge>,
1330 metadata: &RegionMetadataRef,
1331 dedup: bool,
1332 merge_mode: MergeMode,
1333 encode_row_threshold: usize,
1334 encode_bytes_threshold: usize,
1335 row_group_size: usize,
1336 float_field_encoding: FloatFieldEncoding,
1337 ) -> Result<Option<MergedPart>> {
1338 if parts_to_merge.is_empty() {
1339 return Ok(None);
1340 }
1341
1342 let min_timestamp = parts_to_merge
1344 .iter()
1345 .map(|p| p.min_timestamp())
1346 .min()
1347 .unwrap_or(i64::MAX);
1348 let max_timestamp = parts_to_merge
1349 .iter()
1350 .map(|p| p.max_timestamp())
1351 .max()
1352 .unwrap_or(i64::MIN);
1353 let min_sequence = parts_to_merge
1354 .iter()
1355 .map(PartToMerge::min_sequence)
1356 .min()
1357 .unwrap_or(0);
1358 let max_sequence = parts_to_merge
1359 .iter()
1360 .map(|p| p.max_sequence())
1361 .max()
1362 .unwrap_or(0);
1363
1364 let estimated_total_rows: usize = parts_to_merge.iter().map(|p| p.num_rows()).sum();
1366 let estimated_total_bytes: usize = parts_to_merge.iter().map(|p| p.estimated_size()).sum();
1367 let estimated_series_count = parts_to_merge
1368 .iter()
1369 .map(|p| p.series_count())
1370 .max()
1371 .unwrap_or(0);
1372
1373 let batch_size = crate::batch_size::estimate_batch_size(
1374 parts_to_merge
1375 .iter()
1376 .filter_map(PartToMerge::batch_size_statistic),
1377 );
1378 let context = Arc::new(BulkIterContext::new(
1379 metadata.clone(),
1380 None, None, true,
1383 batch_size,
1384 )?);
1385
1386 let schemas = parts_to_merge
1387 .iter()
1388 .map(|part| (part.arrow_schema(), part.num_rows() as u64))
1389 .collect::<Vec<_>>();
1390 let plans = Arc::new(collect_json2_rewrite_plans(metadata, &schemas)?);
1391
1392 debug_assert!(parts_to_merge.windows(2).all(|w| rewrite_json2_schema(
1393 &w[0].arrow_schema(),
1394 &plans
1395 ) == rewrite_json2_schema(
1396 &w[1].arrow_schema(),
1397 &plans
1398 )));
1399 let schema = rewrite_json2_schema(&parts_to_merge[0].arrow_schema(), &plans);
1403
1404 let iterators: Vec<BoxedRecordBatchIterator> = parts_to_merge
1405 .into_iter()
1406 .map(|part| part.create_iterator(context.clone(), plans.clone()))
1407 .collect::<Result<Vec<_>>>()?
1408 .into_iter()
1409 .flatten()
1410 .collect();
1411
1412 if iterators.is_empty() {
1413 return Ok(None);
1414 }
1415
1416 let merged_iter = FlatMergeIterator::new(schema.clone(), iterators, batch_size)?;
1417
1418 let boxed_iter: BoxedRecordBatchIterator = if dedup {
1419 match merge_mode {
1420 MergeMode::LastRow => {
1421 let dedup_iter = FlatDedupIterator::new(merged_iter, FlatLastRow::new(false));
1422 Box::new(dedup_iter)
1423 }
1424 MergeMode::LastNonNull => {
1425 let field_column_start = field_column_start(metadata, schema.fields().len());
1426
1427 let dedup_iter = FlatDedupIterator::new(
1428 merged_iter,
1429 FlatLastNonNull::new(field_column_start, false),
1430 );
1431 Box::new(dedup_iter)
1432 }
1433 }
1434 } else {
1435 Box::new(merged_iter)
1436 };
1437
1438 if estimated_total_rows > encode_row_threshold
1440 || estimated_total_bytes > encode_bytes_threshold
1441 {
1442 let encoder = BulkPartEncoder::new_with_float_field_encoding(
1443 metadata.clone(),
1444 row_group_size,
1445 float_field_encoding,
1446 )?;
1447 let mut metrics = BulkPartEncodeMetrics::default();
1448 let encoded_part = encoder.encode_record_batch_iter(
1449 boxed_iter,
1450 schema,
1451 min_timestamp,
1452 max_timestamp,
1453 min_sequence..=max_sequence,
1454 &mut metrics,
1455 )?;
1456
1457 common_telemetry::trace!("merge_parts_group metrics: {:?}", metrics);
1458
1459 Ok(encoded_part.map(MergedPart::Encoded))
1460 } else {
1461 let mut batches = Vec::new();
1463 let mut actual_total_rows = 0;
1464
1465 for batch_result in boxed_iter {
1466 let batch = batch_result?;
1467 actual_total_rows += batch.num_rows();
1468 batches.push(batch);
1469 }
1470
1471 if actual_total_rows == 0 {
1472 return Ok(None);
1473 }
1474
1475 let multi_part = MultiBulkPart::new(
1476 batches,
1477 min_timestamp,
1478 max_timestamp,
1479 min_sequence,
1480 max_sequence,
1481 estimated_series_count,
1482 metadata,
1483 );
1484
1485 common_telemetry::trace!(
1486 "merge_parts_group created MultiBulkPart: rows={}, batches={}",
1487 actual_total_rows,
1488 multi_part.num_batches()
1489 );
1490
1491 Ok(Some(MergedPart::Multi(multi_part)))
1492 }
1493 }
1494}
1495
1496struct CompactTaskGuard(Arc<AtomicBool>);
1499
1500impl CompactTaskGuard {
1501 fn try_new(compacting: Arc<AtomicBool>) -> Option<Self> {
1502 compacting
1504 .compare_exchange(false, true, Ordering::Relaxed, Ordering::Relaxed)
1505 .ok()
1506 .map(|_| Self(compacting))
1507 }
1508}
1509
1510impl Drop for CompactTaskGuard {
1511 fn drop(&mut self) {
1512 self.0.store(false, Ordering::Relaxed);
1513 }
1514}
1515
1516struct MemCompactTask {
1518 metadata: RegionMetadataRef,
1519 parts: Arc<RwLock<BulkParts>>,
1520 _guard: CompactTaskGuard,
1521 compactor: Arc<Mutex<MemtableCompactor>>,
1523 append_mode: bool,
1525 merge_mode: MergeMode,
1527}
1528
1529impl MemCompactTask {
1530 fn compact(&self) -> Result<()> {
1531 self.compactor.lock().unwrap().merge_parts(
1532 &self.parts,
1533 &self.metadata,
1534 !self.append_mode,
1535 self.merge_mode,
1536 )
1537 }
1538}
1539
1540#[derive(Debug)]
1542pub struct CompactDispatcher {
1543 semaphore: Arc<Semaphore>,
1544}
1545
1546impl CompactDispatcher {
1547 pub fn new(permits: usize) -> Self {
1549 Self {
1550 semaphore: Arc::new(Semaphore::new(permits)),
1551 }
1552 }
1553
1554 fn dispatch_compact(&self, task: MemCompactTask) {
1556 let semaphore = self.semaphore.clone();
1557 common_runtime::spawn_global(async move {
1558 let Ok(permit) = semaphore.acquire_owned().await else {
1559 return;
1560 };
1561
1562 common_runtime::spawn_blocking_global(move || {
1563 let _permit = permit;
1567 if let Err(e) = task.compact() {
1568 common_telemetry::error!(e; "Failed to compact memtable, region: {}", task.metadata.region_id);
1569 }
1570 });
1571 });
1572 }
1573}
1574
1575#[derive(Debug)]
1577pub struct BulkMemtableBuilder {
1578 config: BulkMemtableConfig,
1580 write_buffer_manager: Option<WriteBufferManagerRef>,
1581 compact_dispatcher: Option<Arc<CompactDispatcher>>,
1582 append_mode: bool,
1583 merge_mode: MergeMode,
1584 row_group_size: usize,
1586 float_field_encoding: FloatFieldEncoding,
1587}
1588
1589impl Default for BulkMemtableBuilder {
1590 fn default() -> Self {
1591 Self {
1592 config: BulkMemtableConfig::default(),
1593 write_buffer_manager: None,
1594 compact_dispatcher: None,
1595 append_mode: false,
1596 merge_mode: MergeMode::default(),
1597 row_group_size: DEFAULT_ROW_GROUP_SIZE,
1598 float_field_encoding: FloatFieldEncoding::default(),
1599 }
1600 }
1601}
1602
1603impl BulkMemtableBuilder {
1604 pub fn new(
1606 write_buffer_manager: Option<WriteBufferManagerRef>,
1607 append_mode: bool,
1608 merge_mode: MergeMode,
1609 ) -> Self {
1610 Self {
1611 write_buffer_manager,
1612 append_mode,
1613 merge_mode,
1614 ..Default::default()
1615 }
1616 }
1617
1618 pub fn with_config(mut self, config: BulkMemtableConfig) -> Self {
1620 self.config = config;
1621 self
1622 }
1623
1624 pub fn with_row_group_size(mut self, row_group_size: usize) -> Self {
1626 self.row_group_size = row_group_size;
1627 self
1628 }
1629
1630 pub(super) fn with_float_field_encoding(mut self, encoding: FloatFieldEncoding) -> Self {
1631 self.float_field_encoding = encoding;
1632 self
1633 }
1634
1635 pub fn with_compact_dispatcher(mut self, compact_dispatcher: Arc<CompactDispatcher>) -> Self {
1637 self.compact_dispatcher = Some(compact_dispatcher);
1638 self
1639 }
1640
1641 #[cfg(test)]
1642 pub(crate) fn config(&self) -> &BulkMemtableConfig {
1643 &self.config
1644 }
1645}
1646
1647impl MemtableBuilder for BulkMemtableBuilder {
1648 fn build(&self, id: MemtableId, metadata: &RegionMetadataRef) -> MemtableRef {
1649 Arc::new(BulkMemtable::new_with_row_group_size_and_encoding(
1650 id,
1651 self.config.clone(),
1652 metadata.clone(),
1653 self.write_buffer_manager.clone(),
1654 self.compact_dispatcher.clone(),
1655 self.append_mode,
1656 self.merge_mode,
1657 self.row_group_size,
1658 self.float_field_encoding,
1659 ))
1660 }
1661
1662 fn use_bulk_insert(&self, _metadata: &RegionMetadataRef) -> bool {
1663 true
1664 }
1665}
1666
1667#[cfg(test)]
1668mod tests {
1669 use api::helper::encode_json_value;
1670 use api::v1::value::ValueData;
1671 use api::v1::{Mutation, Row, Rows, SemanticType};
1672 use common_error::ext::WhateverResult;
1673 use datatypes::arrow::array::AsArray;
1674 use datatypes::arrow::datatypes::{DataType as ArrowDataType, UInt64Type};
1675 use datatypes::data_type::ConcreteDataType;
1676 use datatypes::extension::json::{
1677 JSON2_REMAINDER_FIELD_NAME, Json2ExtensionType, Json2PhysicalLayout, JsonMetadata,
1678 };
1679 use datatypes::json::value::JsonValue;
1680 use datatypes::json::{JsonSettings, JsonTypeHint};
1681 use datatypes::schema::ColumnSchema;
1682 use datatypes::types::json_type::{JsonNativeType, JsonObjectType};
1683 use mito_codec::row_converter::build_primary_key_codec;
1684 use parquet::basic::{Compression, Encoding, Type as PhysicalType};
1685 use serde_json::json;
1686 use store_api::metadata::{ColumnMetadata, RegionMetadataBuilder, RegionMetadataRef};
1687 use store_api::storage::consts::{OP_TYPE_COLUMN_NAME, SEQUENCE_COLUMN_NAME};
1688
1689 use super::*;
1690 use crate::memtable::bulk::part::BulkPartConverter;
1691 use crate::read::scan_region::PredicateGroup;
1692 use crate::sst::parquet::flat_format::sequence_column_index;
1693 use crate::sst::{FlatSchemaOptions, to_flat_sst_arrow_schema};
1694 use crate::test_util::memtable_util::{
1695 build_key_values_with_ts_seq_values, metadata_for_test, region_metadata_to_row_schema,
1696 };
1697
1698 fn create_bulk_part_with_converter(
1699 k0: &str,
1700 k1: u32,
1701 timestamps: Vec<i64>,
1702 values: Vec<Option<f64>>,
1703 sequence: u64,
1704 ) -> Result<BulkPart> {
1705 let metadata = metadata_for_test();
1706 let capacity = 100;
1707 let primary_key_codec = build_primary_key_codec(&metadata);
1708 let schema = to_flat_sst_arrow_schema(
1709 &metadata,
1710 &FlatSchemaOptions::from_encoding(metadata.primary_key_encoding),
1711 );
1712
1713 let mut converter =
1714 BulkPartConverter::new(&metadata, schema, capacity, primary_key_codec, true);
1715
1716 let key_values = build_key_values_with_ts_seq_values(
1717 &metadata,
1718 k0.to_string(),
1719 k1,
1720 timestamps.into_iter(),
1721 values.into_iter(),
1722 sequence,
1723 );
1724
1725 converter.append_key_values(&key_values)?;
1726 converter.convert()
1727 }
1728
1729 #[test]
1730 fn test_adaptive_encode_bytes_threshold() {
1731 assert_eq!(
1733 DEFAULT_ENCODE_BYTES_THRESHOLD,
1734 adaptive_encode_bytes_threshold(1024 * 1024 * 1024) );
1736 assert_eq!(
1738 256 * 1024 * 1024,
1739 adaptive_encode_bytes_threshold(8 * 1024 * 1024 * 1024) );
1741 assert_eq!(
1743 MAX_ENCODE_BYTES_THRESHOLD,
1744 adaptive_encode_bytes_threshold(64 * 1024 * 1024 * 1024) );
1746 }
1747
1748 #[test]
1749 fn test_bulk_memtable_sanitizes_zero_merge_threshold() {
1750 let metadata = metadata_for_test();
1751 let config = BulkMemtableConfig {
1752 merge_threshold: 0,
1753 ..Default::default()
1754 };
1755
1756 let memtable =
1757 BulkMemtable::new(999, config, metadata, None, None, false, MergeMode::LastRow);
1758
1759 assert_eq!(DEFAULT_MERGE_THRESHOLD, memtable.config.merge_threshold);
1760 assert_eq!(
1761 DEFAULT_MERGE_THRESHOLD,
1762 memtable.compactor.lock().unwrap().config.merge_threshold
1763 );
1764 }
1765
1766 #[test]
1767 fn test_min_sequence_survives_mixed_parts_compaction_and_fork() {
1768 let metadata = metadata_for_test();
1769 let config = BulkMemtableConfig {
1770 merge_threshold: 3,
1771 encode_row_threshold: 1,
1772 encode_bytes_threshold: 1,
1773 ..Default::default()
1774 };
1775 let memtable = BulkMemtable::new(
1776 1,
1777 config,
1778 metadata.clone(),
1779 None,
1780 None,
1781 false,
1782 MergeMode::LastNonNull,
1783 );
1784 memtable.set_unordered_part_threshold(0);
1785 for (sequence, expected_min, expected_ranges) in [(100, 100, 1), (10, 10, 2), (200, 10, 1)]
1786 {
1787 let part = create_bulk_part_with_converter(
1788 "a",
1789 1,
1790 vec![2000, 1000],
1791 vec![Some(1.0), Some(2.0)],
1792 sequence,
1793 )
1794 .unwrap();
1795 assert_eq!(sequence + 1, part.sequence);
1798 assert_eq!(sequence, part.min_sequence);
1799 let sequences = part
1800 .batch
1801 .column(sequence_column_index(part.batch.num_columns()))
1802 .as_primitive::<UInt64Type>();
1803 assert_eq!(&[sequence + 1, sequence], sequences.values().as_ref());
1804 memtable.write_bulk(part).unwrap();
1805 assert_eq!(expected_min, memtable.stats().min_sequence);
1806 assert_eq!(expected_min, memtable.min_sequence());
1807 assert_eq!(expected_ranges, memtable.stats().num_ranges);
1808 assert!(!memtable.compacting.load(Ordering::Relaxed));
1809 }
1810 assert_eq!(10, memtable.stats().min_sequence);
1813 assert_eq!(201, memtable.stats().max_sequence);
1814 let ranges = memtable.ranges(None, RangesOptions::default()).unwrap();
1815 let merged = ranges.ranges.values().next().unwrap();
1816 assert!(merged.encoded().is_some());
1817 assert_eq!(10, merged.stats().min_sequence);
1818 let fork = memtable.fork(2, &metadata);
1819 assert!(fork.is_empty());
1820 assert_eq!(0, fork.min_sequence());
1821 fork.write_bulk(
1822 create_bulk_part_with_converter("a", 1, vec![1000], vec![Some(1.0)], 50).unwrap(),
1823 )
1824 .unwrap();
1825 assert_eq!(50, fork.stats().min_sequence);
1826 assert_eq!(50, fork.min_sequence());
1827 }
1828
1829 #[test]
1830 fn test_bulk_memtable_write_read() {
1831 let metadata = metadata_for_test();
1832 let memtable = BulkMemtable::new(
1833 999,
1834 BulkMemtableConfig::default(),
1835 metadata.clone(),
1836 None,
1837 None,
1838 false,
1839 MergeMode::LastRow,
1840 );
1841 memtable.set_unordered_part_threshold(0);
1843
1844 let test_data = [
1845 (
1846 "key_a",
1847 1u32,
1848 vec![1000i64, 2000i64],
1849 vec![Some(10.5), Some(20.5)],
1850 100u64,
1851 ),
1852 (
1853 "key_b",
1854 2u32,
1855 vec![1500i64, 2500i64],
1856 vec![Some(15.5), Some(25.5)],
1857 200u64,
1858 ),
1859 ("key_c", 3u32, vec![3000i64], vec![Some(30.5)], 300u64),
1860 ];
1861
1862 for (k0, k1, timestamps, values, seq) in test_data.iter() {
1863 let part =
1864 create_bulk_part_with_converter(k0, *k1, timestamps.clone(), values.clone(), *seq)
1865 .unwrap();
1866 memtable.write_bulk(part).unwrap();
1867 }
1868
1869 let stats = memtable.stats();
1870 assert_eq!(5, stats.num_rows);
1871 assert_eq!(3, stats.num_ranges);
1872 assert_eq!(300, stats.max_sequence);
1873 assert_eq!(100, stats.min_sequence);
1874
1875 let (min_ts, max_ts) = stats.time_range.unwrap();
1876 assert_eq!(1000, min_ts.value());
1877 assert_eq!(3000, max_ts.value());
1878
1879 let predicate_group = PredicateGroup::new(&metadata, &[]).unwrap();
1880 let ranges = memtable
1881 .ranges(
1882 None,
1883 RangesOptions::default().with_predicate(predicate_group),
1884 )
1885 .unwrap();
1886
1887 assert_eq!(3, ranges.ranges.len());
1888 let total_rows: usize = ranges.ranges.values().map(|r| r.stats().num_rows()).sum();
1889 assert_eq!(5, total_rows);
1890
1891 for range in ranges.ranges.values() {
1892 assert!(range.num_rows() > 0);
1893 assert!(range.is_record_batch());
1894
1895 let record_batch_iter = range.build_record_batch_iter(None, None).unwrap();
1896
1897 let mut total_rows = 0;
1898 for batch_result in record_batch_iter {
1899 let batch = batch_result.unwrap();
1900 total_rows += batch.num_rows();
1901 assert!(batch.num_rows() > 0);
1902 assert_eq!(8, batch.num_columns());
1903 }
1904 assert_eq!(total_rows, range.num_rows());
1905 }
1906 }
1907
1908 #[test]
1909 fn test_bulk_memtable_compact_parts_with_json2() {
1910 let metadata = mock_metadata_with_json2(JsonSettings::default());
1911
1912 let config = BulkMemtableConfig {
1913 merge_threshold: 2,
1914 encode_row_threshold: 1,
1915 encode_bytes_threshold: 1,
1916 ..Default::default()
1917 };
1918 let memtable = BulkMemtable::new(
1919 999,
1920 config,
1921 metadata.clone(),
1922 None,
1923 None,
1924 true,
1925 MergeMode::LastRow,
1926 );
1927 memtable.set_unordered_part_threshold(0);
1928
1929 let part1 = mock_bulk_part_with_json2(&metadata, vec![1000, 2000], 100).unwrap();
1930 let part2 = mock_bulk_part_with_json2(&metadata, vec![3000, 4000], 200).unwrap();
1931
1932 memtable.write_bulk(part1).unwrap();
1933 memtable.write_bulk(part2).unwrap();
1934 memtable.compact(false).unwrap();
1935
1936 let stats = memtable.stats();
1937 assert_eq!(4, stats.num_rows);
1938 assert_eq!(201, stats.max_sequence);
1939
1940 let predicate_group = PredicateGroup::new(&metadata, &[]).unwrap();
1941 let opts = RangesOptions::default().with_predicate(predicate_group);
1942 let ranges = memtable.ranges(None, opts).unwrap();
1943
1944 assert_eq!(1, ranges.ranges.len());
1945 let total_rows: usize = ranges.ranges.values().map(|r| r.stats().num_rows()).sum();
1946 assert_eq!(4, total_rows);
1947 }
1948
1949 #[test]
1950 fn test_bulk_memtable_merge_bounds_json2_paths() -> WhateverResult<()> {
1951 let metadata = mock_metadata_with_json2(JsonSettings::try_new(vec![], Some(1))?);
1952 let first = mock_bulk_part_with_json2_values(
1953 &metadata,
1954 vec![1000, 2000],
1955 vec![json!({"a": 1}), json!({"a": 2})],
1956 100,
1957 )?;
1958 let second = mock_bulk_part_with_json2_values(
1959 &metadata,
1960 vec![3000, 4000],
1961 vec![json!({"a": 3, "b": 3}), json!({"a": 4, "b": 4})],
1962 200,
1963 )?;
1964 let parts = vec![
1965 PartToMerge::Bulk {
1966 part: first,
1967 file_id: FileId::random(),
1968 },
1969 PartToMerge::Bulk {
1970 part: second,
1971 file_id: FileId::random(),
1972 },
1973 ];
1974
1975 let merged = MemtableCompactor::merge_parts_group(
1976 parts,
1977 &metadata,
1978 false,
1979 MergeMode::LastRow,
1980 usize::MAX,
1981 usize::MAX,
1982 DEFAULT_ROW_GROUP_SIZE,
1983 FloatFieldEncoding::default(),
1984 )?
1985 .unwrap();
1986 let MergedPart::Multi(part) = merged else {
1987 unreachable!()
1988 };
1989 let schema = part.schemas().next().unwrap();
1990 let ArrowDataType::Struct(fields) = schema.field(0).data_type() else {
1991 unreachable!()
1992 };
1993 assert_eq!(
1994 vec![JSON2_REMAINDER_FIELD_NAME, "a"],
1995 fields.iter().map(|x| x.name().as_str()).collect::<Vec<_>>()
1996 );
1997 Ok(())
1998 }
1999
2000 #[test]
2001 fn test_unordered_parts_align_json2_layouts() -> WhateverResult<()> {
2002 let metadata = mock_metadata_with_json2(JsonSettings::try_new(vec![], Some(2))?);
2003 let memtable = BulkMemtable::new(
2004 42,
2005 BulkMemtableConfig::default(),
2006 metadata.clone(),
2007 None,
2008 None,
2009 false,
2010 MergeMode::LastRow,
2011 );
2012 memtable.write_bulk(mock_bulk_part_with_json2_values(
2013 &metadata,
2014 vec![1000, 2000],
2015 vec![json!({"a": 1}), json!({"a": 2})],
2016 100,
2017 )?)?;
2018 memtable.write_bulk(mock_bulk_part_with_json2_values(
2019 &metadata,
2020 vec![3000, 4000],
2021 vec![json!({"b": 3}), json!({"b": 4})],
2022 200,
2023 )?)?;
2024
2025 let predicate = PredicateGroup::new(&metadata, &[])?;
2026 let ranges = memtable.ranges(None, RangesOptions::default().with_predicate(predicate))?;
2027 let range = ranges.ranges.values().next().unwrap();
2028 let batch = range.build_record_batch_iter(None, None)?.next().unwrap()?;
2029 let schema = batch.schema();
2030 let ArrowDataType::Struct(fields) = schema.field(0).data_type() else {
2031 unreachable!()
2032 };
2033 assert_eq!(
2034 vec![JSON2_REMAINDER_FIELD_NAME],
2035 fields.iter().map(|x| x.name().as_str()).collect::<Vec<_>>()
2036 );
2037 assert_eq!(4, batch.num_rows());
2039 let json = datatypes::vectors::json::array::JsonArray::from(batch.column(0));
2040 let restored = json.project_to_v2(schema.field(0), &ArrowDataType::Binary)?;
2041 let restored = datatypes::vectors::json::array::JsonArray::from(&restored);
2042 assert_eq!(json!({"a": 1}), restored.try_get_value(0)?);
2043 assert_eq!(json!({"a": 2}), restored.try_get_value(1)?);
2044 assert_eq!(json!({"b": 3}), restored.try_get_value(2)?);
2045 assert_eq!(json!({"b": 4}), restored.try_get_value(3)?);
2046 Ok(())
2047 }
2048
2049 #[test]
2050 fn test_bulk_part_converter_uses_json2_v2_layout() -> WhateverResult<()> {
2051 let settings = JsonSettings::try_new(
2052 vec![JsonTypeHint {
2053 path: vec!["id".to_string()],
2054 data_type: ConcreteDataType::int64_datatype(),
2055 inverted_index: false,
2056 skipping_index: None,
2057 fulltext_index: None,
2058 }],
2059 Some(0),
2060 )?;
2061 let metadata = mock_metadata_with_json2(settings);
2062 let part = mock_bulk_part_with_json2(&metadata, vec![1000, 2000], 100)?;
2063 let schema = part.batch.schema();
2064 let field = schema.field(0);
2065 let layout = Json2PhysicalLayout::try_from_root(field)?;
2066
2067 assert!(layout.is_version_2());
2068 let ArrowDataType::Struct(fields) = field.data_type() else {
2069 unreachable!()
2070 };
2071 assert_eq!(
2072 vec![JSON2_REMAINDER_FIELD_NAME, "id"],
2073 fields.iter().map(|x| x.name().as_str()).collect::<Vec<_>>()
2074 );
2075 Ok(())
2076 }
2077
2078 fn mock_metadata_with_json2(settings: JsonSettings) -> RegionMetadataRef {
2079 let col_meta_1 = ColumnMetadata {
2080 column_schema: ColumnSchema::new(
2081 "ts",
2082 ConcreteDataType::timestamp_millisecond_datatype(),
2083 false,
2084 ),
2085 semantic_type: SemanticType::Timestamp,
2086 column_id: 0,
2087 };
2088
2089 let data_type = ConcreteDataType::json2(JsonNativeType::Object(JsonObjectType::new()));
2090 let mut col_schema = ColumnSchema::new("data", data_type, true);
2091 col_schema.with_extension_type(&Json2ExtensionType::new(Arc::new(JsonMetadata::new(
2092 settings,
2093 ))));
2094
2095 let col_meta_2 = ColumnMetadata {
2096 column_schema: col_schema,
2097 semantic_type: SemanticType::Field,
2098 column_id: 1,
2099 };
2100 let mut builder = RegionMetadataBuilder::new(RegionId::new(123, 789));
2101 builder
2102 .push_column_metadata(col_meta_1)
2103 .push_column_metadata(col_meta_2)
2104 .primary_key(vec![]);
2105 Arc::new(builder.build().unwrap())
2106 }
2107
2108 fn mock_bulk_part_with_json2(
2109 metadata: &RegionMetadataRef,
2110 timestamps: Vec<i64>,
2111 sequence: u64,
2112 ) -> Result<BulkPart> {
2113 let values = timestamps
2114 .iter()
2115 .map(|ts| {
2116 json!({
2117 "id": ts,
2118 "payload": {
2119 "message": format!("row-{ts}"),
2120 },
2121 })
2122 })
2123 .collect();
2124 mock_bulk_part_with_json2_values(metadata, timestamps, values, sequence)
2125 }
2126
2127 fn mock_bulk_part_with_json2_values(
2128 metadata: &RegionMetadataRef,
2129 timestamps: Vec<i64>,
2130 values: Vec<serde_json::Value>,
2131 sequence: u64,
2132 ) -> Result<BulkPart> {
2133 let capacity = timestamps.len();
2134 debug_assert_eq!(capacity, values.len());
2135 let primary_key_codec = build_primary_key_codec(metadata);
2136 let json_type = JsonNativeType::Object(JsonObjectType::from([
2137 ("id".to_string(), JsonNativeType::i64()),
2138 (
2139 "payload".to_string(),
2140 JsonNativeType::Object(JsonObjectType::from([(
2141 "message".to_string(),
2142 JsonNativeType::String,
2143 )])),
2144 ),
2145 ]));
2146 let mut options = FlatSchemaOptions::from_encoding(metadata.primary_key_encoding);
2147 options
2148 .concretized_json_types
2149 .insert("data".to_string(), json_type.as_arrow_type());
2150 let schema = to_flat_sst_arrow_schema(metadata, &options);
2151
2152 let mut converter =
2153 BulkPartConverter::new(metadata, schema, capacity, primary_key_codec, true);
2154
2155 let rows = timestamps
2156 .into_iter()
2157 .zip(values)
2158 .map(|(ts, value)| {
2159 let val1 = api::v1::Value {
2160 value_data: Some(ValueData::TimestampMillisecondValue(ts)),
2161 };
2162 let value_data = ValueData::JsonValue(encode_json_value(JsonValue::from(value)));
2163 let val2 = api::v1::Value {
2164 value_data: Some(value_data),
2165 };
2166 Row {
2167 values: vec![val1, val2],
2168 }
2169 })
2170 .collect();
2171
2172 let mutation = Mutation {
2173 op_type: 1,
2174 sequence,
2175 rows: Some(Rows {
2176 schema: region_metadata_to_row_schema(metadata),
2177 rows,
2178 }),
2179 write_hint: None,
2180 };
2181 let key_values = KeyValues::new(metadata.as_ref(), mutation).unwrap();
2182
2183 converter.append_key_values(&key_values)?;
2184 converter.convert()
2185 }
2186
2187 #[test]
2188 fn test_bulk_memtable_ranges_with_projection() {
2189 let metadata = metadata_for_test();
2190 let memtable = BulkMemtable::new(
2191 111,
2192 BulkMemtableConfig::default(),
2193 metadata.clone(),
2194 None,
2195 None,
2196 false,
2197 MergeMode::LastRow,
2198 );
2199
2200 let bulk_part = create_bulk_part_with_converter(
2201 "projection_test",
2202 5,
2203 vec![5000, 6000, 7000],
2204 vec![Some(50.0), Some(60.0), Some(70.0)],
2205 500,
2206 )
2207 .unwrap();
2208
2209 memtable.write_bulk(bulk_part).unwrap();
2210
2211 let projection = vec![4u32];
2212 let predicate_group = PredicateGroup::new(&metadata, &[]).unwrap();
2213 let ranges = memtable
2214 .ranges(
2215 Some(&projection),
2216 RangesOptions::default().with_predicate(predicate_group),
2217 )
2218 .unwrap();
2219
2220 assert_eq!(1, ranges.ranges.len());
2221 let range = ranges.ranges.get(&0).unwrap();
2222
2223 assert!(range.is_record_batch());
2224 let record_batch_iter = range.build_record_batch_iter(None, None).unwrap();
2225
2226 let mut total_rows = 0;
2227 for batch_result in record_batch_iter {
2228 let batch = batch_result.unwrap();
2229 assert!(batch.num_rows() > 0);
2230 assert_eq!(5, batch.num_columns());
2231 total_rows += batch.num_rows();
2232 }
2233 assert_eq!(3, total_rows);
2234 }
2235
2236 #[test]
2237 fn test_bulk_memtable_unsupported_operations() {
2238 let metadata = metadata_for_test();
2239 let memtable = BulkMemtable::new(
2240 111,
2241 BulkMemtableConfig::default(),
2242 metadata.clone(),
2243 None,
2244 None,
2245 false,
2246 MergeMode::LastRow,
2247 );
2248
2249 let key_values = build_key_values_with_ts_seq_values(
2250 &metadata,
2251 "test".to_string(),
2252 1,
2253 vec![1000].into_iter(),
2254 vec![Some(1.0)].into_iter(),
2255 1,
2256 );
2257
2258 let err = memtable.write(&key_values).unwrap_err();
2259 assert!(err.to_string().contains("not supported"));
2260
2261 let kv = key_values.iter().next().unwrap();
2262 let err = memtable.write_one(kv).unwrap_err();
2263 assert!(err.to_string().contains("not supported"));
2264 }
2265
2266 #[test]
2267 fn test_bulk_memtable_freeze() {
2268 let metadata = metadata_for_test();
2269 let memtable = BulkMemtable::new(
2270 222,
2271 BulkMemtableConfig::default(),
2272 metadata.clone(),
2273 None,
2274 None,
2275 false,
2276 MergeMode::LastRow,
2277 );
2278
2279 let bulk_part = create_bulk_part_with_converter(
2280 "freeze_test",
2281 10,
2282 vec![10000],
2283 vec![Some(100.0)],
2284 1000,
2285 )
2286 .unwrap();
2287
2288 memtable.write_bulk(bulk_part).unwrap();
2289 memtable.freeze().unwrap();
2290
2291 let stats_after_freeze = memtable.stats();
2292 assert_eq!(1, stats_after_freeze.num_rows);
2293 }
2294
2295 #[test]
2296 fn test_bulk_memtable_fork() {
2297 let metadata = metadata_for_test();
2298 let original_memtable = BulkMemtable::new_with_row_group_size_and_encoding(
2299 333,
2300 BulkMemtableConfig::default(),
2301 metadata.clone(),
2302 None,
2303 None,
2304 false,
2305 MergeMode::LastRow,
2306 DEFAULT_ROW_GROUP_SIZE,
2307 FloatFieldEncoding::ByteStreamSplit,
2308 );
2309
2310 let bulk_part =
2311 create_bulk_part_with_converter("fork_test", 15, vec![15000], vec![Some(150.0)], 1500)
2312 .unwrap();
2313
2314 original_memtable.write_bulk(bulk_part).unwrap();
2315
2316 let forked_memtable = original_memtable.fork_inner(444, &metadata);
2317
2318 assert_eq!(forked_memtable.id(), 444);
2319 assert_eq!(
2320 FloatFieldEncoding::ByteStreamSplit,
2321 original_memtable.float_field_encoding
2322 );
2323 assert_eq!(
2324 original_memtable.float_field_encoding,
2325 forked_memtable.float_field_encoding
2326 );
2327 assert_eq!(
2328 original_memtable.float_field_encoding,
2329 forked_memtable
2330 .compactor
2331 .lock()
2332 .unwrap()
2333 .float_field_encoding
2334 );
2335 assert!(forked_memtable.is_empty());
2336 assert_eq!(0, forked_memtable.stats().num_rows);
2337
2338 assert!(!original_memtable.is_empty());
2339 assert_eq!(1, original_memtable.stats().num_rows);
2340 }
2341
2342 #[test]
2343 fn test_bulk_memtable_ranges_multiple_parts() {
2344 let metadata = metadata_for_test();
2345 let memtable = BulkMemtable::new(
2346 777,
2347 BulkMemtableConfig::default(),
2348 metadata.clone(),
2349 None,
2350 None,
2351 false,
2352 MergeMode::LastRow,
2353 );
2354 memtable.set_unordered_part_threshold(0);
2356
2357 let parts_data = vec![
2358 (
2359 "part1",
2360 1u32,
2361 vec![1000i64, 1100i64],
2362 vec![Some(10.0), Some(11.0)],
2363 100u64,
2364 ),
2365 (
2366 "part2",
2367 2u32,
2368 vec![2000i64, 2100i64],
2369 vec![Some(20.0), Some(21.0)],
2370 200u64,
2371 ),
2372 ("part3", 3u32, vec![3000i64], vec![Some(30.0)], 300u64),
2373 ];
2374
2375 for (k0, k1, timestamps, values, seq) in parts_data {
2376 let part = create_bulk_part_with_converter(k0, k1, timestamps, values, seq).unwrap();
2377 memtable.write_bulk(part).unwrap();
2378 }
2379
2380 let predicate_group = PredicateGroup::new(&metadata, &[]).unwrap();
2381 let ranges = memtable
2382 .ranges(
2383 None,
2384 RangesOptions::default().with_predicate(predicate_group),
2385 )
2386 .unwrap();
2387
2388 assert_eq!(3, ranges.ranges.len());
2389 let total_rows: usize = ranges.ranges.values().map(|r| r.stats().num_rows()).sum();
2390 assert_eq!(5, total_rows);
2391 assert_eq!(3, ranges.ranges.len());
2392
2393 for (range_id, range) in ranges.ranges.iter() {
2394 assert!(*range_id < 3);
2395 assert!(range.num_rows() > 0);
2396 assert!(range.is_record_batch());
2397 }
2398 }
2399
2400 #[test]
2401 fn test_bulk_memtable_ranges_with_sequence_filter() {
2402 let metadata = metadata_for_test();
2403 let memtable = BulkMemtable::new(
2404 888,
2405 BulkMemtableConfig::default(),
2406 metadata.clone(),
2407 None,
2408 None,
2409 false,
2410 MergeMode::LastRow,
2411 );
2412
2413 let part = create_bulk_part_with_converter(
2414 "seq_test",
2415 1,
2416 vec![1000, 2000, 3000],
2417 vec![Some(10.0), Some(20.0), Some(30.0)],
2418 500,
2419 )
2420 .unwrap();
2421
2422 memtable.write_bulk(part).unwrap();
2423
2424 let predicate_group = PredicateGroup::new(&metadata, &[]).unwrap();
2425 let sequence_filter = Some(SequenceRange::LtEq { max: 400 }); let ranges = memtable
2427 .ranges(
2428 None,
2429 RangesOptions::default()
2430 .with_predicate(predicate_group)
2431 .with_sequence(sequence_filter),
2432 )
2433 .unwrap();
2434
2435 assert_eq!(1, ranges.ranges.len());
2436 let range = ranges.ranges.get(&0).unwrap();
2437
2438 let mut record_batch_iter = range.build_record_batch_iter(None, None).unwrap();
2439 assert!(record_batch_iter.next().is_none());
2440 }
2441
2442 #[test]
2443 fn test_bulk_memtable_ranges_with_encoded_parts() {
2444 let metadata = metadata_for_test();
2445 let config = BulkMemtableConfig {
2446 merge_threshold: 8,
2447 ..Default::default()
2448 };
2449 let memtable = BulkMemtable::new(
2450 999,
2451 config,
2452 metadata.clone(),
2453 None,
2454 None,
2455 false,
2456 MergeMode::LastRow,
2457 );
2458 memtable.set_unordered_part_threshold(0);
2460
2461 for i in 0..10 {
2463 let part = create_bulk_part_with_converter(
2464 &format!("key_{}", i),
2465 i,
2466 vec![1000 + i as i64 * 100],
2467 vec![Some(i as f64 * 10.0)],
2468 100 + i as u64,
2469 )
2470 .unwrap();
2471 memtable.write_bulk(part).unwrap();
2472 }
2473
2474 memtable.compact(false).unwrap();
2475
2476 let predicate_group = PredicateGroup::new(&metadata, &[]).unwrap();
2477 let ranges = memtable
2478 .ranges(
2479 None,
2480 RangesOptions::default().with_predicate(predicate_group),
2481 )
2482 .unwrap();
2483
2484 assert_eq!(3, ranges.ranges.len());
2486 let total_rows: usize = ranges.ranges.values().map(|r| r.stats().num_rows()).sum();
2487 assert_eq!(10, total_rows);
2488
2489 for range in ranges.ranges.values() {
2490 assert!(range.num_rows() > 0);
2491 assert!(range.is_record_batch());
2492
2493 let record_batch_iter = range.build_record_batch_iter(None, None).unwrap();
2494 let mut total_rows = 0;
2495 for batch_result in record_batch_iter {
2496 let batch = batch_result.unwrap();
2497 total_rows += batch.num_rows();
2498 assert!(batch.num_rows() > 0);
2499 }
2500 assert_eq!(total_rows, range.num_rows());
2501 }
2502 }
2503
2504 #[test]
2505 fn test_bulk_memtable_unordered_part() {
2506 let metadata = metadata_for_test();
2507 let memtable = BulkMemtable::new(
2508 1001,
2509 BulkMemtableConfig::default(),
2510 metadata.clone(),
2511 None,
2512 None,
2513 false,
2514 MergeMode::LastRow,
2515 );
2516
2517 memtable.set_unordered_part_threshold(5);
2520 memtable.set_unordered_part_compact_threshold(10);
2522
2523 for i in 0..3 {
2525 let part = create_bulk_part_with_converter(
2526 &format!("key_{}", i),
2527 i,
2528 vec![1000 + i as i64 * 100, 1100 + i as i64 * 100],
2529 vec![Some(i as f64 * 10.0), Some(i as f64 * 10.0 + 1.0)],
2530 100 + i as u64,
2531 )
2532 .unwrap();
2533 assert_eq!(2, part.num_rows());
2534 memtable.write_bulk(part).unwrap();
2535 }
2536
2537 let stats = memtable.stats();
2539 assert_eq!(6, stats.num_rows);
2540
2541 for i in 3..5 {
2544 let part = create_bulk_part_with_converter(
2545 &format!("key_{}", i),
2546 i,
2547 vec![1000 + i as i64 * 100, 1100 + i as i64 * 100],
2548 vec![Some(i as f64 * 10.0), Some(i as f64 * 10.0 + 1.0)],
2549 100 + i as u64,
2550 )
2551 .unwrap();
2552 memtable.write_bulk(part).unwrap();
2553 }
2554
2555 let stats = memtable.stats();
2557 assert_eq!(10, stats.num_rows);
2558
2559 let predicate_group = PredicateGroup::new(&metadata, &[]).unwrap();
2561 let ranges = memtable
2562 .ranges(
2563 None,
2564 RangesOptions::default().with_predicate(predicate_group),
2565 )
2566 .unwrap();
2567
2568 assert!(!ranges.ranges.is_empty());
2570 let total_rows: usize = ranges.ranges.values().map(|r| r.stats().num_rows()).sum();
2571 assert_eq!(10, total_rows);
2572
2573 let mut total_rows_read = 0;
2575 for range in ranges.ranges.values() {
2576 assert!(range.is_record_batch());
2577 let record_batch_iter = range.build_record_batch_iter(None, None).unwrap();
2578
2579 for batch_result in record_batch_iter {
2580 let batch = batch_result.unwrap();
2581 total_rows_read += batch.num_rows();
2582 }
2583 }
2584 assert_eq!(10, total_rows_read);
2585 }
2586
2587 #[test]
2588 fn test_bulk_memtable_unordered_part_mixed_sizes() {
2589 let metadata = metadata_for_test();
2590 let memtable = BulkMemtable::new(
2591 1002,
2592 BulkMemtableConfig::default(),
2593 metadata.clone(),
2594 None,
2595 None,
2596 false,
2597 MergeMode::LastRow,
2598 );
2599
2600 memtable.set_unordered_part_threshold(4);
2602 memtable.set_unordered_part_compact_threshold(8);
2603
2604 for i in 0..2 {
2606 let part = create_bulk_part_with_converter(
2607 &format!("small_{}", i),
2608 i,
2609 vec![1000 + i as i64, 2000 + i as i64, 3000 + i as i64],
2610 vec![Some(i as f64), Some(i as f64 + 1.0), Some(i as f64 + 2.0)],
2611 10 + i as u64,
2612 )
2613 .unwrap();
2614 assert_eq!(3, part.num_rows());
2615 memtable.write_bulk(part).unwrap();
2616 }
2617
2618 let large_part = create_bulk_part_with_converter(
2620 "large_key",
2621 100,
2622 vec![5000, 6000, 7000, 8000, 9000],
2623 vec![
2624 Some(100.0),
2625 Some(101.0),
2626 Some(102.0),
2627 Some(103.0),
2628 Some(104.0),
2629 ],
2630 50,
2631 )
2632 .unwrap();
2633 assert_eq!(5, large_part.num_rows());
2634 memtable.write_bulk(large_part).unwrap();
2635
2636 let part = create_bulk_part_with_converter(
2638 "small_2",
2639 2,
2640 vec![4000, 4100],
2641 vec![Some(20.0), Some(21.0)],
2642 30,
2643 )
2644 .unwrap();
2645 memtable.write_bulk(part).unwrap();
2646
2647 let stats = memtable.stats();
2648 assert_eq!(13, stats.num_rows); let predicate_group = PredicateGroup::new(&metadata, &[]).unwrap();
2652 let ranges = memtable
2653 .ranges(
2654 None,
2655 RangesOptions::default().with_predicate(predicate_group),
2656 )
2657 .unwrap();
2658
2659 let total_rows: usize = ranges.ranges.values().map(|r| r.stats().num_rows()).sum();
2660 assert_eq!(13, total_rows);
2661
2662 let mut total_rows_read = 0;
2663 for range in ranges.ranges.values() {
2664 let record_batch_iter = range.build_record_batch_iter(None, None).unwrap();
2665 for batch_result in record_batch_iter {
2666 let batch = batch_result.unwrap();
2667 total_rows_read += batch.num_rows();
2668 }
2669 }
2670 assert_eq!(13, total_rows_read);
2671 }
2672
2673 #[test]
2674 fn test_bulk_memtable_unordered_part_byte_threshold() {
2675 let metadata = metadata_for_test();
2676 let first =
2677 create_bulk_part_with_converter("first", 1, vec![1000], vec![Some(1.0)], 1).unwrap();
2678 let second =
2679 create_bulk_part_with_converter("second", 2, vec![2000], vec![Some(2.0)], 2).unwrap();
2680 let threshold = first.estimated_size().max(second.estimated_size());
2681 let memtable = BulkMemtable::new(
2682 1004,
2683 BulkMemtableConfig {
2684 encode_bytes_threshold: threshold,
2685 ..Default::default()
2686 },
2687 metadata.clone(),
2688 None,
2689 None,
2690 false,
2691 MergeMode::LastRow,
2692 );
2693 memtable.set_unordered_part_compact_threshold(usize::MAX);
2694
2695 memtable.write_bulk(first).unwrap();
2697 assert_eq!(1, memtable.parts.read().unwrap().unordered_part.num_parts());
2698
2699 memtable.write_bulk(second).unwrap();
2701 let parts = memtable.parts.read().unwrap();
2702 assert!(parts.unordered_part.is_empty());
2703 assert_eq!(1, parts.parts.len());
2704 drop(parts);
2705
2706 let oversized =
2707 create_bulk_part_with_converter("oversized", 3, vec![3000], vec![Some(3.0)], 3)
2708 .unwrap();
2709 let oversized_memtable = BulkMemtable::new(
2710 1005,
2711 BulkMemtableConfig {
2712 encode_bytes_threshold: oversized.estimated_size() - 1,
2713 ..Default::default()
2714 },
2715 metadata,
2716 None,
2717 None,
2718 false,
2719 MergeMode::LastRow,
2720 );
2721 oversized_memtable.write_bulk(oversized).unwrap();
2722 let parts = oversized_memtable.parts.read().unwrap();
2723 assert!(parts.unordered_part.is_empty());
2724 assert_eq!(1, parts.parts.len());
2725 }
2726
2727 #[test]
2728 fn test_bulk_memtable_unordered_part_with_ranges() {
2729 let metadata = metadata_for_test();
2730 let memtable = BulkMemtable::new(
2731 1003,
2732 BulkMemtableConfig::default(),
2733 metadata.clone(),
2734 None,
2735 None,
2736 false,
2737 MergeMode::LastRow,
2738 );
2739
2740 memtable.set_unordered_part_threshold(3);
2742 memtable.set_unordered_part_compact_threshold(100); for i in 0..3 {
2746 let part = create_bulk_part_with_converter(
2747 &format!("key_{}", i),
2748 i,
2749 vec![1000 + i as i64 * 100],
2750 vec![Some(i as f64 * 10.0)],
2751 100 + i as u64,
2752 )
2753 .unwrap();
2754 assert_eq!(1, part.num_rows());
2755 memtable.write_bulk(part).unwrap();
2756 }
2757
2758 let stats = memtable.stats();
2759 assert_eq!(3, stats.num_rows);
2760
2761 let predicate_group = PredicateGroup::new(&metadata, &[]).unwrap();
2763 let ranges = memtable
2764 .ranges(
2765 None,
2766 RangesOptions::default().with_predicate(predicate_group),
2767 )
2768 .unwrap();
2769
2770 assert_eq!(1, ranges.ranges.len());
2772 let total_rows: usize = ranges.ranges.values().map(|r| r.stats().num_rows()).sum();
2773 assert_eq!(3, total_rows);
2774
2775 let range = ranges.ranges.get(&0).unwrap();
2777 let record_batch_iter = range.build_record_batch_iter(None, None).unwrap();
2778
2779 let mut total_rows = 0;
2780 for batch_result in record_batch_iter {
2781 let batch = batch_result.unwrap();
2782 total_rows += batch.num_rows();
2783 assert!(batch.num_rows() > 0);
2785 }
2786 assert_eq!(3, total_rows);
2787 }
2788
2789 fn create_bulk_part_wrapper(part: BulkPart) -> BulkPartWrapper {
2791 BulkPartWrapper {
2792 part: PartToMerge::Bulk {
2793 part,
2794 file_id: FileId::random(),
2795 },
2796 merging: false,
2797 }
2798 }
2799
2800 #[test]
2801 fn test_may_merge_parts_below_threshold() {
2802 let mut bulk_parts = BulkParts::default();
2803
2804 for i in 0..DEFAULT_MERGE_THRESHOLD - 1 {
2806 let part = create_bulk_part_with_converter(
2807 &format!("key_{}", i),
2808 i as u32,
2809 vec![1000 + i as i64 * 100],
2810 vec![Some(i as f64 * 10.0)],
2811 100 + i as u64,
2812 )
2813 .unwrap();
2814 bulk_parts.parts.push(create_bulk_part_wrapper(part));
2815 if i == 0 {
2816 assert!(!bulk_parts.may_merge_parts(0));
2817 assert!(!bulk_parts.may_merge_parts(1));
2818 }
2819 }
2820
2821 assert!(!bulk_parts.may_merge_parts(DEFAULT_MERGE_THRESHOLD));
2823 }
2824
2825 #[test]
2826 fn test_may_merge_parts_at_threshold() {
2827 let mut bulk_parts = BulkParts::default();
2828 let merge_threshold = 8;
2829
2830 for i in 0..merge_threshold {
2832 let part = create_bulk_part_with_converter(
2833 &format!("key_{}", i),
2834 i as u32,
2835 vec![1000 + i as i64 * 100],
2836 vec![Some(i as f64 * 10.0)],
2837 100 + i as u64,
2838 )
2839 .unwrap();
2840 bulk_parts.parts.push(create_bulk_part_wrapper(part));
2841 }
2842
2843 assert!(bulk_parts.may_merge_parts(merge_threshold));
2845 }
2846
2847 #[test]
2848 fn test_raw_parts_do_not_use_encoded_merge_limit() {
2849 let mut bulk_parts = BulkParts::default();
2850 let merge_threshold = 4;
2851 for i in 0..merge_threshold {
2852 let part = create_bulk_part_with_converter(
2853 &format!("key_{}", i),
2854 i as u32,
2855 vec![1000 + i as i64],
2856 vec![Some(i as f64)],
2857 100 + i as u64,
2858 )
2859 .unwrap();
2860 bulk_parts.parts.push(create_bulk_part_wrapper(part));
2861 }
2862
2863 let max_size = bulk_parts.parts[0].part.estimated_size();
2864 assert!(bulk_parts.may_merge_parts(merge_threshold));
2865 assert_eq!(
2866 1,
2867 bulk_parts
2868 .collect_parts_to_merge(merge_threshold, 1, max_size)
2869 .groups
2870 .len()
2871 );
2872 }
2873
2874 #[test]
2875 fn test_may_merge_parts_with_merging_flag() {
2876 let mut bulk_parts = BulkParts::default();
2877 let merge_threshold = 8;
2878
2879 for i in 0..10 {
2881 let part = create_bulk_part_with_converter(
2882 &format!("key_{}", i),
2883 i as u32,
2884 vec![1000 + i as i64 * 100],
2885 vec![Some(i as f64 * 10.0)],
2886 100 + i as u64,
2887 )
2888 .unwrap();
2889 bulk_parts.parts.push(create_bulk_part_wrapper(part));
2890 }
2891
2892 assert!(bulk_parts.may_merge_parts(merge_threshold));
2894
2895 for wrapper in bulk_parts.parts.iter_mut().take(3) {
2897 wrapper.merging = true;
2898 }
2899
2900 assert!(!bulk_parts.may_merge_parts(merge_threshold));
2902 }
2903
2904 #[test]
2905 fn test_compact_task_guard() {
2906 let compacting = Arc::new(AtomicBool::new(false));
2907 let guard = CompactTaskGuard::try_new(compacting.clone()).unwrap();
2908 assert!(compacting.load(Ordering::Relaxed));
2909 assert!(CompactTaskGuard::try_new(compacting.clone()).is_none());
2910 drop(guard);
2911 assert!(!compacting.load(Ordering::Relaxed));
2912 assert!(CompactTaskGuard::try_new(compacting.clone()).is_some());
2913 assert!(!compacting.load(Ordering::Relaxed));
2914 }
2915
2916 #[test]
2917 fn test_compact_scheduling_disabled() {
2918 let memtable = BulkMemtable::new(
2919 0,
2920 BulkMemtableConfig {
2921 merge_threshold: 2,
2922 max_merge_groups: 0,
2923 ..Default::default()
2924 },
2925 metadata_for_test(),
2926 None,
2927 Some(Arc::new(CompactDispatcher::new(0))),
2928 false,
2929 MergeMode::LastRow,
2930 );
2931 let part =
2932 create_bulk_part_with_converter("key", 0, vec![1000], vec![Some(1.0)], 0).unwrap();
2933 memtable.parts.write().unwrap().parts = vec![
2934 create_bulk_part_wrapper(part.clone()),
2935 create_bulk_part_wrapper(part),
2936 ];
2937 memtable.schedule_compact();
2938 assert!(!memtable.compacting.load(Ordering::Relaxed));
2939 }
2940
2941 #[test]
2942 fn test_collect_parts_to_merge_sequence_components() {
2943 let make_part = |min, max| {
2944 let mut part =
2945 create_bulk_part_with_converter("key", 0, vec![1000], vec![Some(1.0)], max)
2946 .unwrap();
2947 part.min_sequence = min;
2948 part
2949 };
2950 let metadata = metadata_for_test();
2951 let encoder = BulkPartEncoder::new(metadata.clone(), DEFAULT_ROW_GROUP_SIZE).unwrap();
2952 let mut parts = BulkParts {
2954 parts: vec![
2955 create_bulk_part_wrapper(make_part(70, 70)),
2956 create_bulk_part_wrapper(make_part(10, 30)),
2957 BulkPartWrapper {
2958 part: PartToMerge::Multi {
2959 part: MultiBulkPart::from_bulk_part(make_part(35, 50), &metadata),
2960 file_id: FileId::random(),
2961 },
2962 merging: false,
2963 },
2964 create_bulk_part_wrapper(make_part(60, 60)),
2965 BulkPartWrapper {
2966 part: PartToMerge::Encoded {
2967 part: encoder.encode_part(&make_part(20, 40)).unwrap().unwrap(),
2968 file_id: FileId::random(),
2969 },
2970 merging: false,
2971 },
2972 ],
2973 ..Default::default()
2974 };
2975 assert!(parts.may_merge_parts(parts.parts.len()));
2978 assert_eq!(
2979 vec![vec![1, 4, 2], vec![3, 0]],
2980 parts.pick_merge_groups(2, 2, 1)
2981 );
2982 parts.unordered_part.push(make_part(0, 100));
2984 assert!(parts.may_merge_parts(2));
2985 assert!(parts.pick_merge_groups(2, 2, 1).is_empty());
2986 parts.unordered_part.clear();
2987
2988 parts.unordered_part.push(make_part(50, 55));
2990 assert_eq!(vec![vec![3, 0]], parts.pick_merge_groups(2, 2, 1));
2991 parts.unordered_part.clear();
2992
2993 parts.unordered_part.push(make_part(65, 65));
2995 assert_eq!(vec![vec![1, 4, 2]], parts.pick_merge_groups(2, 2, 1));
2996 parts.unordered_part.clear();
2997
2998 parts.parts[4].merging = true;
2999 let selected = parts.collect_parts_to_merge(2, 2, 1);
3000 assert_eq!(1, selected.groups.len());
3001 assert_eq!(
3002 vec![60, 70],
3003 selected.groups[0]
3004 .iter()
3005 .map(PartToMerge::max_sequence)
3006 .collect::<Vec<_>>()
3007 );
3008 assert!(!parts.parts[1].merging);
3009 assert!(!parts.parts[2].merging);
3010 }
3011
3012 #[test]
3013 fn test_encoded_merge_candidate_size_limit() {
3014 assert!(BulkParts::is_merge_candidate_by_size(false, usize::MAX, 8));
3015 assert!(BulkParts::is_merge_candidate_by_size(true, 8, 8));
3016 assert!(!BulkParts::is_merge_candidate_by_size(true, 9, 8));
3017 }
3018
3019 #[test]
3020 fn test_bulk_part_encoder_preserves_byte_stream_split() -> WhateverResult<()> {
3021 let metadata = metadata_for_test();
3022 let bulk_part = create_bulk_part_with_converter(
3023 "byte_stream_split",
3024 0,
3025 vec![1000, 2000],
3026 vec![Some(10.0), Some(20.0)],
3027 100,
3028 )?;
3029 let encoder = BulkPartEncoder::new_with_float_field_encoding(
3030 metadata,
3031 DEFAULT_ROW_GROUP_SIZE,
3032 FloatFieldEncoding::ByteStreamSplit,
3033 )?;
3034 let encoded_part = encoder.encode_part(&bulk_part)?.unwrap();
3035 let field_column = encoded_part
3036 .metadata()
3037 .parquet_metadata
3038 .row_groups()
3039 .iter()
3040 .flat_map(|row_group| row_group.columns())
3041 .find(|column| {
3042 column.column_path().string() == "v1"
3043 && column.column_type() == PhysicalType::DOUBLE
3044 })
3045 .expect("Float64 field should be present in encoded bulk part");
3046
3047 assert!(
3048 field_column
3049 .encodings()
3050 .any(|encoding| encoding == Encoding::BYTE_STREAM_SPLIT)
3051 );
3052 Ok(())
3053 }
3054
3055 #[test]
3056 fn test_bulk_part_encoder_matches_sst_writer_internal_encodings() -> WhateverResult<()> {
3057 let metadata = metadata_for_test();
3058 let bulk_part = create_bulk_part_with_converter(
3059 "key",
3060 0,
3061 vec![1000, 2000, 3000],
3062 vec![Some(1.0), Some(2.0), Some(3.0)],
3063 100,
3064 )?;
3065 let encoder = BulkPartEncoder::new(metadata, DEFAULT_ROW_GROUP_SIZE)?;
3066 let encoded_part = encoder.encode_part(&bulk_part)?.unwrap();
3067 let columns = encoded_part
3068 .metadata()
3069 .parquet_metadata
3070 .row_group(0)
3071 .columns();
3072
3073 for name in ["ts", SEQUENCE_COLUMN_NAME] {
3074 let column = columns
3075 .iter()
3076 .find(|column| column.column_path().string() == name)
3077 .unwrap();
3078 assert!(
3079 column
3080 .encodings()
3081 .any(|encoding| encoding == Encoding::DELTA_BINARY_PACKED),
3082 "{name}"
3083 );
3084 assert!(column.dictionary_page_offset().is_none(), "{name}");
3085 }
3086 let op_type = columns
3087 .iter()
3088 .find(|column| column.column_path().string() == OP_TYPE_COLUMN_NAME)
3089 .unwrap();
3090 assert_eq!(Compression::UNCOMPRESSED, op_type.compression());
3091 Ok(())
3092 }
3093
3094 #[test]
3095 fn test_encoded_part_batch_size_uses_largest_uncompressed_row_group() {
3096 const NUM_ROWS: usize = 14;
3097 const ROW_GROUP_SIZE: usize = 4;
3098
3099 let metadata = metadata_for_test();
3100 let timestamps = (0..NUM_ROWS as i64).collect::<Vec<_>>();
3101 let field_values = (0..NUM_ROWS)
3102 .map(|value| Some(value as f64))
3103 .collect::<Vec<_>>();
3104 let bulk_part =
3105 create_bulk_part_with_converter("key", 0, timestamps, field_values, 0).unwrap();
3106 let encoder = BulkPartEncoder::new(metadata, ROW_GROUP_SIZE).unwrap();
3107 let encoded_part = encoder.encode_part(&bulk_part).unwrap().unwrap();
3108 let max_row_group = encoded_part
3109 .metadata()
3110 .parquet_metadata
3111 .row_groups()
3112 .iter()
3113 .max_by_key(|row_group| {
3114 row_group
3115 .columns()
3116 .iter()
3117 .map(|column| column.uncompressed_size() as u64)
3118 .sum::<u64>()
3119 })
3120 .unwrap();
3121 let max_uncompressed_size = max_row_group
3122 .columns()
3123 .iter()
3124 .map(|column| column.uncompressed_size() as u64)
3125 .sum();
3126 let expected = (max_row_group.num_rows() as u64, max_uncompressed_size);
3127 let part = PartToMerge::Encoded {
3128 part: encoded_part,
3129 file_id: FileId::random(),
3130 };
3131
3132 assert_eq!(Some(expected), part.batch_size_statistic());
3133 }
3134
3135 #[test]
3136 fn test_bulk_memtable_ranges_with_multi_bulk_part() {
3137 let metadata = metadata_for_test();
3138 let merge_threshold = 8;
3139 let config = BulkMemtableConfig {
3140 merge_threshold,
3141 ..Default::default()
3142 };
3143 let memtable = BulkMemtable::new(
3144 2005,
3145 config,
3146 metadata.clone(),
3147 None,
3148 None,
3149 false,
3150 MergeMode::LastRow,
3151 );
3152 memtable.set_unordered_part_threshold(0);
3154
3155 for i in 0..merge_threshold {
3159 let part = create_bulk_part_with_converter(
3160 &format!("key_{}", i),
3161 i as u32,
3162 vec![1000 + i as i64 * 100, 2000 + i as i64 * 100],
3163 vec![Some(i as f64 * 10.0), Some(i as f64 * 10.0 + 1.0)],
3164 100 + i as u64,
3165 )
3166 .unwrap();
3167 memtable.write_bulk(part).unwrap();
3168 }
3169
3170 memtable.compact(false).unwrap();
3172
3173 let predicate_group = PredicateGroup::new(&metadata, &[]).unwrap();
3175 let ranges = memtable
3176 .ranges(
3177 None,
3178 RangesOptions::default().with_predicate(predicate_group),
3179 )
3180 .unwrap();
3181
3182 assert_eq!(1, ranges.ranges.len());
3183 let expected_rows = merge_threshold * 2; let total_rows: usize = ranges.ranges.values().map(|r| r.stats().num_rows()).sum();
3185 assert_eq!(expected_rows, total_rows);
3186
3187 let mut total_rows_read = 0;
3189 for range in ranges.ranges.values() {
3190 assert!(range.is_record_batch());
3191 let record_batch_iter = range.build_record_batch_iter(None, None).unwrap();
3192
3193 for batch_result in record_batch_iter {
3194 let batch = batch_result.unwrap();
3195 total_rows_read += batch.num_rows();
3196 }
3197 }
3198 assert_eq!(expected_rows, total_rows_read);
3199 }
3200
3201 #[test]
3202 fn test_multi_bulk_range_iter_builder_all_pruned() {
3203 let metadata = metadata_for_test();
3204 let merge_threshold = 8;
3205 let config = BulkMemtableConfig {
3206 merge_threshold,
3207 ..Default::default()
3208 };
3209 let memtable = BulkMemtable::new(
3210 2006,
3211 config,
3212 metadata.clone(),
3213 None,
3214 None,
3215 false,
3216 MergeMode::LastRow,
3217 );
3218 memtable.set_unordered_part_threshold(0);
3219
3220 for i in 0..merge_threshold {
3222 let part = create_bulk_part_with_converter(
3223 &format!("key_{}", i),
3224 i as u32,
3225 vec![1000 + i as i64 * 100, 2000 + i as i64 * 100],
3226 vec![Some(i as f64 * 10.0), Some(i as f64 * 10.0 + 1.0)],
3227 100 + i as u64,
3228 )
3229 .unwrap();
3230 memtable.write_bulk(part).unwrap();
3231 }
3232 memtable.compact(false).unwrap();
3233
3234 let filter = datafusion_expr::col("k0").eq(datafusion_expr::lit("nonexistent"));
3236 let predicate_group = PredicateGroup::new(&metadata, &[filter]).unwrap();
3237 let ranges = memtable
3238 .ranges(
3239 None,
3240 RangesOptions::default().with_predicate(predicate_group),
3241 )
3242 .unwrap();
3243
3244 for range in ranges.ranges.values() {
3247 assert!(range.is_record_batch());
3248 let record_batch_iter = range.build_record_batch_iter(None, None).unwrap();
3249 let total_rows: usize = record_batch_iter.map(|r| r.unwrap().num_rows()).sum();
3250 assert_eq!(0, total_rows);
3251 }
3252 }
3253}