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 }],
2057 Some(0),
2058 )?;
2059 let metadata = mock_metadata_with_json2(settings);
2060 let part = mock_bulk_part_with_json2(&metadata, vec![1000, 2000], 100)?;
2061 let schema = part.batch.schema();
2062 let field = schema.field(0);
2063 let layout = Json2PhysicalLayout::try_from_root(field)?;
2064
2065 assert!(layout.is_version_2());
2066 let ArrowDataType::Struct(fields) = field.data_type() else {
2067 unreachable!()
2068 };
2069 assert_eq!(
2070 vec![JSON2_REMAINDER_FIELD_NAME, "id"],
2071 fields.iter().map(|x| x.name().as_str()).collect::<Vec<_>>()
2072 );
2073 Ok(())
2074 }
2075
2076 fn mock_metadata_with_json2(settings: JsonSettings) -> RegionMetadataRef {
2077 let col_meta_1 = ColumnMetadata {
2078 column_schema: ColumnSchema::new(
2079 "ts",
2080 ConcreteDataType::timestamp_millisecond_datatype(),
2081 false,
2082 ),
2083 semantic_type: SemanticType::Timestamp,
2084 column_id: 0,
2085 };
2086
2087 let data_type = ConcreteDataType::json2(JsonNativeType::Object(JsonObjectType::new()));
2088 let mut col_schema = ColumnSchema::new("data", data_type, true);
2089 col_schema.with_extension_type(&Json2ExtensionType::new(Arc::new(JsonMetadata::new(
2090 settings,
2091 ))));
2092
2093 let col_meta_2 = ColumnMetadata {
2094 column_schema: col_schema,
2095 semantic_type: SemanticType::Field,
2096 column_id: 1,
2097 };
2098 let mut builder = RegionMetadataBuilder::new(RegionId::new(123, 789));
2099 builder
2100 .push_column_metadata(col_meta_1)
2101 .push_column_metadata(col_meta_2)
2102 .primary_key(vec![]);
2103 Arc::new(builder.build().unwrap())
2104 }
2105
2106 fn mock_bulk_part_with_json2(
2107 metadata: &RegionMetadataRef,
2108 timestamps: Vec<i64>,
2109 sequence: u64,
2110 ) -> Result<BulkPart> {
2111 let values = timestamps
2112 .iter()
2113 .map(|ts| {
2114 json!({
2115 "id": ts,
2116 "payload": {
2117 "message": format!("row-{ts}"),
2118 },
2119 })
2120 })
2121 .collect();
2122 mock_bulk_part_with_json2_values(metadata, timestamps, values, sequence)
2123 }
2124
2125 fn mock_bulk_part_with_json2_values(
2126 metadata: &RegionMetadataRef,
2127 timestamps: Vec<i64>,
2128 values: Vec<serde_json::Value>,
2129 sequence: u64,
2130 ) -> Result<BulkPart> {
2131 let capacity = timestamps.len();
2132 debug_assert_eq!(capacity, values.len());
2133 let primary_key_codec = build_primary_key_codec(metadata);
2134 let json_type = JsonNativeType::Object(JsonObjectType::from([
2135 ("id".to_string(), JsonNativeType::i64()),
2136 (
2137 "payload".to_string(),
2138 JsonNativeType::Object(JsonObjectType::from([(
2139 "message".to_string(),
2140 JsonNativeType::String,
2141 )])),
2142 ),
2143 ]));
2144 let mut options = FlatSchemaOptions::from_encoding(metadata.primary_key_encoding);
2145 options
2146 .concretized_json_types
2147 .insert("data".to_string(), json_type.as_arrow_type());
2148 let schema = to_flat_sst_arrow_schema(metadata, &options);
2149
2150 let mut converter =
2151 BulkPartConverter::new(metadata, schema, capacity, primary_key_codec, true);
2152
2153 let rows = timestamps
2154 .into_iter()
2155 .zip(values)
2156 .map(|(ts, value)| {
2157 let val1 = api::v1::Value {
2158 value_data: Some(ValueData::TimestampMillisecondValue(ts)),
2159 };
2160 let value_data = ValueData::JsonValue(encode_json_value(JsonValue::from(value)));
2161 let val2 = api::v1::Value {
2162 value_data: Some(value_data),
2163 };
2164 Row {
2165 values: vec![val1, val2],
2166 }
2167 })
2168 .collect();
2169
2170 let mutation = Mutation {
2171 op_type: 1,
2172 sequence,
2173 rows: Some(Rows {
2174 schema: region_metadata_to_row_schema(metadata),
2175 rows,
2176 }),
2177 write_hint: None,
2178 };
2179 let key_values = KeyValues::new(metadata.as_ref(), mutation).unwrap();
2180
2181 converter.append_key_values(&key_values)?;
2182 converter.convert()
2183 }
2184
2185 #[test]
2186 fn test_bulk_memtable_ranges_with_projection() {
2187 let metadata = metadata_for_test();
2188 let memtable = BulkMemtable::new(
2189 111,
2190 BulkMemtableConfig::default(),
2191 metadata.clone(),
2192 None,
2193 None,
2194 false,
2195 MergeMode::LastRow,
2196 );
2197
2198 let bulk_part = create_bulk_part_with_converter(
2199 "projection_test",
2200 5,
2201 vec![5000, 6000, 7000],
2202 vec![Some(50.0), Some(60.0), Some(70.0)],
2203 500,
2204 )
2205 .unwrap();
2206
2207 memtable.write_bulk(bulk_part).unwrap();
2208
2209 let projection = vec![4u32];
2210 let predicate_group = PredicateGroup::new(&metadata, &[]).unwrap();
2211 let ranges = memtable
2212 .ranges(
2213 Some(&projection),
2214 RangesOptions::default().with_predicate(predicate_group),
2215 )
2216 .unwrap();
2217
2218 assert_eq!(1, ranges.ranges.len());
2219 let range = ranges.ranges.get(&0).unwrap();
2220
2221 assert!(range.is_record_batch());
2222 let record_batch_iter = range.build_record_batch_iter(None, None).unwrap();
2223
2224 let mut total_rows = 0;
2225 for batch_result in record_batch_iter {
2226 let batch = batch_result.unwrap();
2227 assert!(batch.num_rows() > 0);
2228 assert_eq!(5, batch.num_columns());
2229 total_rows += batch.num_rows();
2230 }
2231 assert_eq!(3, total_rows);
2232 }
2233
2234 #[test]
2235 fn test_bulk_memtable_unsupported_operations() {
2236 let metadata = metadata_for_test();
2237 let memtable = BulkMemtable::new(
2238 111,
2239 BulkMemtableConfig::default(),
2240 metadata.clone(),
2241 None,
2242 None,
2243 false,
2244 MergeMode::LastRow,
2245 );
2246
2247 let key_values = build_key_values_with_ts_seq_values(
2248 &metadata,
2249 "test".to_string(),
2250 1,
2251 vec![1000].into_iter(),
2252 vec![Some(1.0)].into_iter(),
2253 1,
2254 );
2255
2256 let err = memtable.write(&key_values).unwrap_err();
2257 assert!(err.to_string().contains("not supported"));
2258
2259 let kv = key_values.iter().next().unwrap();
2260 let err = memtable.write_one(kv).unwrap_err();
2261 assert!(err.to_string().contains("not supported"));
2262 }
2263
2264 #[test]
2265 fn test_bulk_memtable_freeze() {
2266 let metadata = metadata_for_test();
2267 let memtable = BulkMemtable::new(
2268 222,
2269 BulkMemtableConfig::default(),
2270 metadata.clone(),
2271 None,
2272 None,
2273 false,
2274 MergeMode::LastRow,
2275 );
2276
2277 let bulk_part = create_bulk_part_with_converter(
2278 "freeze_test",
2279 10,
2280 vec![10000],
2281 vec![Some(100.0)],
2282 1000,
2283 )
2284 .unwrap();
2285
2286 memtable.write_bulk(bulk_part).unwrap();
2287 memtable.freeze().unwrap();
2288
2289 let stats_after_freeze = memtable.stats();
2290 assert_eq!(1, stats_after_freeze.num_rows);
2291 }
2292
2293 #[test]
2294 fn test_bulk_memtable_fork() {
2295 let metadata = metadata_for_test();
2296 let original_memtable = BulkMemtable::new_with_row_group_size_and_encoding(
2297 333,
2298 BulkMemtableConfig::default(),
2299 metadata.clone(),
2300 None,
2301 None,
2302 false,
2303 MergeMode::LastRow,
2304 DEFAULT_ROW_GROUP_SIZE,
2305 FloatFieldEncoding::ByteStreamSplit,
2306 );
2307
2308 let bulk_part =
2309 create_bulk_part_with_converter("fork_test", 15, vec![15000], vec![Some(150.0)], 1500)
2310 .unwrap();
2311
2312 original_memtable.write_bulk(bulk_part).unwrap();
2313
2314 let forked_memtable = original_memtable.fork_inner(444, &metadata);
2315
2316 assert_eq!(forked_memtable.id(), 444);
2317 assert_eq!(
2318 FloatFieldEncoding::ByteStreamSplit,
2319 original_memtable.float_field_encoding
2320 );
2321 assert_eq!(
2322 original_memtable.float_field_encoding,
2323 forked_memtable.float_field_encoding
2324 );
2325 assert_eq!(
2326 original_memtable.float_field_encoding,
2327 forked_memtable
2328 .compactor
2329 .lock()
2330 .unwrap()
2331 .float_field_encoding
2332 );
2333 assert!(forked_memtable.is_empty());
2334 assert_eq!(0, forked_memtable.stats().num_rows);
2335
2336 assert!(!original_memtable.is_empty());
2337 assert_eq!(1, original_memtable.stats().num_rows);
2338 }
2339
2340 #[test]
2341 fn test_bulk_memtable_ranges_multiple_parts() {
2342 let metadata = metadata_for_test();
2343 let memtable = BulkMemtable::new(
2344 777,
2345 BulkMemtableConfig::default(),
2346 metadata.clone(),
2347 None,
2348 None,
2349 false,
2350 MergeMode::LastRow,
2351 );
2352 memtable.set_unordered_part_threshold(0);
2354
2355 let parts_data = vec![
2356 (
2357 "part1",
2358 1u32,
2359 vec![1000i64, 1100i64],
2360 vec![Some(10.0), Some(11.0)],
2361 100u64,
2362 ),
2363 (
2364 "part2",
2365 2u32,
2366 vec![2000i64, 2100i64],
2367 vec![Some(20.0), Some(21.0)],
2368 200u64,
2369 ),
2370 ("part3", 3u32, vec![3000i64], vec![Some(30.0)], 300u64),
2371 ];
2372
2373 for (k0, k1, timestamps, values, seq) in parts_data {
2374 let part = create_bulk_part_with_converter(k0, k1, timestamps, values, seq).unwrap();
2375 memtable.write_bulk(part).unwrap();
2376 }
2377
2378 let predicate_group = PredicateGroup::new(&metadata, &[]).unwrap();
2379 let ranges = memtable
2380 .ranges(
2381 None,
2382 RangesOptions::default().with_predicate(predicate_group),
2383 )
2384 .unwrap();
2385
2386 assert_eq!(3, ranges.ranges.len());
2387 let total_rows: usize = ranges.ranges.values().map(|r| r.stats().num_rows()).sum();
2388 assert_eq!(5, total_rows);
2389 assert_eq!(3, ranges.ranges.len());
2390
2391 for (range_id, range) in ranges.ranges.iter() {
2392 assert!(*range_id < 3);
2393 assert!(range.num_rows() > 0);
2394 assert!(range.is_record_batch());
2395 }
2396 }
2397
2398 #[test]
2399 fn test_bulk_memtable_ranges_with_sequence_filter() {
2400 let metadata = metadata_for_test();
2401 let memtable = BulkMemtable::new(
2402 888,
2403 BulkMemtableConfig::default(),
2404 metadata.clone(),
2405 None,
2406 None,
2407 false,
2408 MergeMode::LastRow,
2409 );
2410
2411 let part = create_bulk_part_with_converter(
2412 "seq_test",
2413 1,
2414 vec![1000, 2000, 3000],
2415 vec![Some(10.0), Some(20.0), Some(30.0)],
2416 500,
2417 )
2418 .unwrap();
2419
2420 memtable.write_bulk(part).unwrap();
2421
2422 let predicate_group = PredicateGroup::new(&metadata, &[]).unwrap();
2423 let sequence_filter = Some(SequenceRange::LtEq { max: 400 }); let ranges = memtable
2425 .ranges(
2426 None,
2427 RangesOptions::default()
2428 .with_predicate(predicate_group)
2429 .with_sequence(sequence_filter),
2430 )
2431 .unwrap();
2432
2433 assert_eq!(1, ranges.ranges.len());
2434 let range = ranges.ranges.get(&0).unwrap();
2435
2436 let mut record_batch_iter = range.build_record_batch_iter(None, None).unwrap();
2437 assert!(record_batch_iter.next().is_none());
2438 }
2439
2440 #[test]
2441 fn test_bulk_memtable_ranges_with_encoded_parts() {
2442 let metadata = metadata_for_test();
2443 let config = BulkMemtableConfig {
2444 merge_threshold: 8,
2445 ..Default::default()
2446 };
2447 let memtable = BulkMemtable::new(
2448 999,
2449 config,
2450 metadata.clone(),
2451 None,
2452 None,
2453 false,
2454 MergeMode::LastRow,
2455 );
2456 memtable.set_unordered_part_threshold(0);
2458
2459 for i in 0..10 {
2461 let part = create_bulk_part_with_converter(
2462 &format!("key_{}", i),
2463 i,
2464 vec![1000 + i as i64 * 100],
2465 vec![Some(i as f64 * 10.0)],
2466 100 + i as u64,
2467 )
2468 .unwrap();
2469 memtable.write_bulk(part).unwrap();
2470 }
2471
2472 memtable.compact(false).unwrap();
2473
2474 let predicate_group = PredicateGroup::new(&metadata, &[]).unwrap();
2475 let ranges = memtable
2476 .ranges(
2477 None,
2478 RangesOptions::default().with_predicate(predicate_group),
2479 )
2480 .unwrap();
2481
2482 assert_eq!(3, ranges.ranges.len());
2484 let total_rows: usize = ranges.ranges.values().map(|r| r.stats().num_rows()).sum();
2485 assert_eq!(10, total_rows);
2486
2487 for range in ranges.ranges.values() {
2488 assert!(range.num_rows() > 0);
2489 assert!(range.is_record_batch());
2490
2491 let record_batch_iter = range.build_record_batch_iter(None, None).unwrap();
2492 let mut total_rows = 0;
2493 for batch_result in record_batch_iter {
2494 let batch = batch_result.unwrap();
2495 total_rows += batch.num_rows();
2496 assert!(batch.num_rows() > 0);
2497 }
2498 assert_eq!(total_rows, range.num_rows());
2499 }
2500 }
2501
2502 #[test]
2503 fn test_bulk_memtable_unordered_part() {
2504 let metadata = metadata_for_test();
2505 let memtable = BulkMemtable::new(
2506 1001,
2507 BulkMemtableConfig::default(),
2508 metadata.clone(),
2509 None,
2510 None,
2511 false,
2512 MergeMode::LastRow,
2513 );
2514
2515 memtable.set_unordered_part_threshold(5);
2518 memtable.set_unordered_part_compact_threshold(10);
2520
2521 for i in 0..3 {
2523 let part = create_bulk_part_with_converter(
2524 &format!("key_{}", i),
2525 i,
2526 vec![1000 + i as i64 * 100, 1100 + i as i64 * 100],
2527 vec![Some(i as f64 * 10.0), Some(i as f64 * 10.0 + 1.0)],
2528 100 + i as u64,
2529 )
2530 .unwrap();
2531 assert_eq!(2, part.num_rows());
2532 memtable.write_bulk(part).unwrap();
2533 }
2534
2535 let stats = memtable.stats();
2537 assert_eq!(6, stats.num_rows);
2538
2539 for i in 3..5 {
2542 let part = create_bulk_part_with_converter(
2543 &format!("key_{}", i),
2544 i,
2545 vec![1000 + i as i64 * 100, 1100 + i as i64 * 100],
2546 vec![Some(i as f64 * 10.0), Some(i as f64 * 10.0 + 1.0)],
2547 100 + i as u64,
2548 )
2549 .unwrap();
2550 memtable.write_bulk(part).unwrap();
2551 }
2552
2553 let stats = memtable.stats();
2555 assert_eq!(10, stats.num_rows);
2556
2557 let predicate_group = PredicateGroup::new(&metadata, &[]).unwrap();
2559 let ranges = memtable
2560 .ranges(
2561 None,
2562 RangesOptions::default().with_predicate(predicate_group),
2563 )
2564 .unwrap();
2565
2566 assert!(!ranges.ranges.is_empty());
2568 let total_rows: usize = ranges.ranges.values().map(|r| r.stats().num_rows()).sum();
2569 assert_eq!(10, total_rows);
2570
2571 let mut total_rows_read = 0;
2573 for range in ranges.ranges.values() {
2574 assert!(range.is_record_batch());
2575 let record_batch_iter = range.build_record_batch_iter(None, None).unwrap();
2576
2577 for batch_result in record_batch_iter {
2578 let batch = batch_result.unwrap();
2579 total_rows_read += batch.num_rows();
2580 }
2581 }
2582 assert_eq!(10, total_rows_read);
2583 }
2584
2585 #[test]
2586 fn test_bulk_memtable_unordered_part_mixed_sizes() {
2587 let metadata = metadata_for_test();
2588 let memtable = BulkMemtable::new(
2589 1002,
2590 BulkMemtableConfig::default(),
2591 metadata.clone(),
2592 None,
2593 None,
2594 false,
2595 MergeMode::LastRow,
2596 );
2597
2598 memtable.set_unordered_part_threshold(4);
2600 memtable.set_unordered_part_compact_threshold(8);
2601
2602 for i in 0..2 {
2604 let part = create_bulk_part_with_converter(
2605 &format!("small_{}", i),
2606 i,
2607 vec![1000 + i as i64, 2000 + i as i64, 3000 + i as i64],
2608 vec![Some(i as f64), Some(i as f64 + 1.0), Some(i as f64 + 2.0)],
2609 10 + i as u64,
2610 )
2611 .unwrap();
2612 assert_eq!(3, part.num_rows());
2613 memtable.write_bulk(part).unwrap();
2614 }
2615
2616 let large_part = create_bulk_part_with_converter(
2618 "large_key",
2619 100,
2620 vec![5000, 6000, 7000, 8000, 9000],
2621 vec![
2622 Some(100.0),
2623 Some(101.0),
2624 Some(102.0),
2625 Some(103.0),
2626 Some(104.0),
2627 ],
2628 50,
2629 )
2630 .unwrap();
2631 assert_eq!(5, large_part.num_rows());
2632 memtable.write_bulk(large_part).unwrap();
2633
2634 let part = create_bulk_part_with_converter(
2636 "small_2",
2637 2,
2638 vec![4000, 4100],
2639 vec![Some(20.0), Some(21.0)],
2640 30,
2641 )
2642 .unwrap();
2643 memtable.write_bulk(part).unwrap();
2644
2645 let stats = memtable.stats();
2646 assert_eq!(13, stats.num_rows); let predicate_group = PredicateGroup::new(&metadata, &[]).unwrap();
2650 let ranges = memtable
2651 .ranges(
2652 None,
2653 RangesOptions::default().with_predicate(predicate_group),
2654 )
2655 .unwrap();
2656
2657 let total_rows: usize = ranges.ranges.values().map(|r| r.stats().num_rows()).sum();
2658 assert_eq!(13, total_rows);
2659
2660 let mut total_rows_read = 0;
2661 for range in ranges.ranges.values() {
2662 let record_batch_iter = range.build_record_batch_iter(None, None).unwrap();
2663 for batch_result in record_batch_iter {
2664 let batch = batch_result.unwrap();
2665 total_rows_read += batch.num_rows();
2666 }
2667 }
2668 assert_eq!(13, total_rows_read);
2669 }
2670
2671 #[test]
2672 fn test_bulk_memtable_unordered_part_byte_threshold() {
2673 let metadata = metadata_for_test();
2674 let first =
2675 create_bulk_part_with_converter("first", 1, vec![1000], vec![Some(1.0)], 1).unwrap();
2676 let second =
2677 create_bulk_part_with_converter("second", 2, vec![2000], vec![Some(2.0)], 2).unwrap();
2678 let threshold = first.estimated_size().max(second.estimated_size());
2679 let memtable = BulkMemtable::new(
2680 1004,
2681 BulkMemtableConfig {
2682 encode_bytes_threshold: threshold,
2683 ..Default::default()
2684 },
2685 metadata.clone(),
2686 None,
2687 None,
2688 false,
2689 MergeMode::LastRow,
2690 );
2691 memtable.set_unordered_part_compact_threshold(usize::MAX);
2692
2693 memtable.write_bulk(first).unwrap();
2695 assert_eq!(1, memtable.parts.read().unwrap().unordered_part.num_parts());
2696
2697 memtable.write_bulk(second).unwrap();
2699 let parts = memtable.parts.read().unwrap();
2700 assert!(parts.unordered_part.is_empty());
2701 assert_eq!(1, parts.parts.len());
2702 drop(parts);
2703
2704 let oversized =
2705 create_bulk_part_with_converter("oversized", 3, vec![3000], vec![Some(3.0)], 3)
2706 .unwrap();
2707 let oversized_memtable = BulkMemtable::new(
2708 1005,
2709 BulkMemtableConfig {
2710 encode_bytes_threshold: oversized.estimated_size() - 1,
2711 ..Default::default()
2712 },
2713 metadata,
2714 None,
2715 None,
2716 false,
2717 MergeMode::LastRow,
2718 );
2719 oversized_memtable.write_bulk(oversized).unwrap();
2720 let parts = oversized_memtable.parts.read().unwrap();
2721 assert!(parts.unordered_part.is_empty());
2722 assert_eq!(1, parts.parts.len());
2723 }
2724
2725 #[test]
2726 fn test_bulk_memtable_unordered_part_with_ranges() {
2727 let metadata = metadata_for_test();
2728 let memtable = BulkMemtable::new(
2729 1003,
2730 BulkMemtableConfig::default(),
2731 metadata.clone(),
2732 None,
2733 None,
2734 false,
2735 MergeMode::LastRow,
2736 );
2737
2738 memtable.set_unordered_part_threshold(3);
2740 memtable.set_unordered_part_compact_threshold(100); for i in 0..3 {
2744 let part = create_bulk_part_with_converter(
2745 &format!("key_{}", i),
2746 i,
2747 vec![1000 + i as i64 * 100],
2748 vec![Some(i as f64 * 10.0)],
2749 100 + i as u64,
2750 )
2751 .unwrap();
2752 assert_eq!(1, part.num_rows());
2753 memtable.write_bulk(part).unwrap();
2754 }
2755
2756 let stats = memtable.stats();
2757 assert_eq!(3, stats.num_rows);
2758
2759 let predicate_group = PredicateGroup::new(&metadata, &[]).unwrap();
2761 let ranges = memtable
2762 .ranges(
2763 None,
2764 RangesOptions::default().with_predicate(predicate_group),
2765 )
2766 .unwrap();
2767
2768 assert_eq!(1, ranges.ranges.len());
2770 let total_rows: usize = ranges.ranges.values().map(|r| r.stats().num_rows()).sum();
2771 assert_eq!(3, total_rows);
2772
2773 let range = ranges.ranges.get(&0).unwrap();
2775 let record_batch_iter = range.build_record_batch_iter(None, None).unwrap();
2776
2777 let mut total_rows = 0;
2778 for batch_result in record_batch_iter {
2779 let batch = batch_result.unwrap();
2780 total_rows += batch.num_rows();
2781 assert!(batch.num_rows() > 0);
2783 }
2784 assert_eq!(3, total_rows);
2785 }
2786
2787 fn create_bulk_part_wrapper(part: BulkPart) -> BulkPartWrapper {
2789 BulkPartWrapper {
2790 part: PartToMerge::Bulk {
2791 part,
2792 file_id: FileId::random(),
2793 },
2794 merging: false,
2795 }
2796 }
2797
2798 #[test]
2799 fn test_may_merge_parts_below_threshold() {
2800 let mut bulk_parts = BulkParts::default();
2801
2802 for i in 0..DEFAULT_MERGE_THRESHOLD - 1 {
2804 let part = create_bulk_part_with_converter(
2805 &format!("key_{}", i),
2806 i as u32,
2807 vec![1000 + i as i64 * 100],
2808 vec![Some(i as f64 * 10.0)],
2809 100 + i as u64,
2810 )
2811 .unwrap();
2812 bulk_parts.parts.push(create_bulk_part_wrapper(part));
2813 if i == 0 {
2814 assert!(!bulk_parts.may_merge_parts(0));
2815 assert!(!bulk_parts.may_merge_parts(1));
2816 }
2817 }
2818
2819 assert!(!bulk_parts.may_merge_parts(DEFAULT_MERGE_THRESHOLD));
2821 }
2822
2823 #[test]
2824 fn test_may_merge_parts_at_threshold() {
2825 let mut bulk_parts = BulkParts::default();
2826 let merge_threshold = 8;
2827
2828 for i in 0..merge_threshold {
2830 let part = create_bulk_part_with_converter(
2831 &format!("key_{}", i),
2832 i as u32,
2833 vec![1000 + i as i64 * 100],
2834 vec![Some(i as f64 * 10.0)],
2835 100 + i as u64,
2836 )
2837 .unwrap();
2838 bulk_parts.parts.push(create_bulk_part_wrapper(part));
2839 }
2840
2841 assert!(bulk_parts.may_merge_parts(merge_threshold));
2843 }
2844
2845 #[test]
2846 fn test_raw_parts_do_not_use_encoded_merge_limit() {
2847 let mut bulk_parts = BulkParts::default();
2848 let merge_threshold = 4;
2849 for i in 0..merge_threshold {
2850 let part = create_bulk_part_with_converter(
2851 &format!("key_{}", i),
2852 i as u32,
2853 vec![1000 + i as i64],
2854 vec![Some(i as f64)],
2855 100 + i as u64,
2856 )
2857 .unwrap();
2858 bulk_parts.parts.push(create_bulk_part_wrapper(part));
2859 }
2860
2861 let max_size = bulk_parts.parts[0].part.estimated_size();
2862 assert!(bulk_parts.may_merge_parts(merge_threshold));
2863 assert_eq!(
2864 1,
2865 bulk_parts
2866 .collect_parts_to_merge(merge_threshold, 1, max_size)
2867 .groups
2868 .len()
2869 );
2870 }
2871
2872 #[test]
2873 fn test_may_merge_parts_with_merging_flag() {
2874 let mut bulk_parts = BulkParts::default();
2875 let merge_threshold = 8;
2876
2877 for i in 0..10 {
2879 let part = create_bulk_part_with_converter(
2880 &format!("key_{}", i),
2881 i as u32,
2882 vec![1000 + i as i64 * 100],
2883 vec![Some(i as f64 * 10.0)],
2884 100 + i as u64,
2885 )
2886 .unwrap();
2887 bulk_parts.parts.push(create_bulk_part_wrapper(part));
2888 }
2889
2890 assert!(bulk_parts.may_merge_parts(merge_threshold));
2892
2893 for wrapper in bulk_parts.parts.iter_mut().take(3) {
2895 wrapper.merging = true;
2896 }
2897
2898 assert!(!bulk_parts.may_merge_parts(merge_threshold));
2900 }
2901
2902 #[test]
2903 fn test_compact_task_guard() {
2904 let compacting = Arc::new(AtomicBool::new(false));
2905 let guard = CompactTaskGuard::try_new(compacting.clone()).unwrap();
2906 assert!(compacting.load(Ordering::Relaxed));
2907 assert!(CompactTaskGuard::try_new(compacting.clone()).is_none());
2908 drop(guard);
2909 assert!(!compacting.load(Ordering::Relaxed));
2910 assert!(CompactTaskGuard::try_new(compacting.clone()).is_some());
2911 assert!(!compacting.load(Ordering::Relaxed));
2912 }
2913
2914 #[test]
2915 fn test_compact_scheduling_disabled() {
2916 let memtable = BulkMemtable::new(
2917 0,
2918 BulkMemtableConfig {
2919 merge_threshold: 2,
2920 max_merge_groups: 0,
2921 ..Default::default()
2922 },
2923 metadata_for_test(),
2924 None,
2925 Some(Arc::new(CompactDispatcher::new(0))),
2926 false,
2927 MergeMode::LastRow,
2928 );
2929 let part =
2930 create_bulk_part_with_converter("key", 0, vec![1000], vec![Some(1.0)], 0).unwrap();
2931 memtable.parts.write().unwrap().parts = vec![
2932 create_bulk_part_wrapper(part.clone()),
2933 create_bulk_part_wrapper(part),
2934 ];
2935 memtable.schedule_compact();
2936 assert!(!memtable.compacting.load(Ordering::Relaxed));
2937 }
2938
2939 #[test]
2940 fn test_collect_parts_to_merge_sequence_components() {
2941 let make_part = |min, max| {
2942 let mut part =
2943 create_bulk_part_with_converter("key", 0, vec![1000], vec![Some(1.0)], max)
2944 .unwrap();
2945 part.min_sequence = min;
2946 part
2947 };
2948 let metadata = metadata_for_test();
2949 let encoder = BulkPartEncoder::new(metadata.clone(), DEFAULT_ROW_GROUP_SIZE).unwrap();
2950 let mut parts = BulkParts {
2952 parts: vec![
2953 create_bulk_part_wrapper(make_part(70, 70)),
2954 create_bulk_part_wrapper(make_part(10, 30)),
2955 BulkPartWrapper {
2956 part: PartToMerge::Multi {
2957 part: MultiBulkPart::from_bulk_part(make_part(35, 50), &metadata),
2958 file_id: FileId::random(),
2959 },
2960 merging: false,
2961 },
2962 create_bulk_part_wrapper(make_part(60, 60)),
2963 BulkPartWrapper {
2964 part: PartToMerge::Encoded {
2965 part: encoder.encode_part(&make_part(20, 40)).unwrap().unwrap(),
2966 file_id: FileId::random(),
2967 },
2968 merging: false,
2969 },
2970 ],
2971 ..Default::default()
2972 };
2973 assert!(parts.may_merge_parts(parts.parts.len()));
2976 assert_eq!(
2977 vec![vec![1, 4, 2], vec![3, 0]],
2978 parts.pick_merge_groups(2, 2, 1)
2979 );
2980 parts.unordered_part.push(make_part(0, 100));
2982 assert!(parts.may_merge_parts(2));
2983 assert!(parts.pick_merge_groups(2, 2, 1).is_empty());
2984 parts.unordered_part.clear();
2985
2986 parts.unordered_part.push(make_part(50, 55));
2988 assert_eq!(vec![vec![3, 0]], parts.pick_merge_groups(2, 2, 1));
2989 parts.unordered_part.clear();
2990
2991 parts.unordered_part.push(make_part(65, 65));
2993 assert_eq!(vec![vec![1, 4, 2]], parts.pick_merge_groups(2, 2, 1));
2994 parts.unordered_part.clear();
2995
2996 parts.parts[4].merging = true;
2997 let selected = parts.collect_parts_to_merge(2, 2, 1);
2998 assert_eq!(1, selected.groups.len());
2999 assert_eq!(
3000 vec![60, 70],
3001 selected.groups[0]
3002 .iter()
3003 .map(PartToMerge::max_sequence)
3004 .collect::<Vec<_>>()
3005 );
3006 assert!(!parts.parts[1].merging);
3007 assert!(!parts.parts[2].merging);
3008 }
3009
3010 #[test]
3011 fn test_encoded_merge_candidate_size_limit() {
3012 assert!(BulkParts::is_merge_candidate_by_size(false, usize::MAX, 8));
3013 assert!(BulkParts::is_merge_candidate_by_size(true, 8, 8));
3014 assert!(!BulkParts::is_merge_candidate_by_size(true, 9, 8));
3015 }
3016
3017 #[test]
3018 fn test_bulk_part_encoder_preserves_byte_stream_split() -> WhateverResult<()> {
3019 let metadata = metadata_for_test();
3020 let bulk_part = create_bulk_part_with_converter(
3021 "byte_stream_split",
3022 0,
3023 vec![1000, 2000],
3024 vec![Some(10.0), Some(20.0)],
3025 100,
3026 )?;
3027 let encoder = BulkPartEncoder::new_with_float_field_encoding(
3028 metadata,
3029 DEFAULT_ROW_GROUP_SIZE,
3030 FloatFieldEncoding::ByteStreamSplit,
3031 )?;
3032 let encoded_part = encoder.encode_part(&bulk_part)?.unwrap();
3033 let field_column = encoded_part
3034 .metadata()
3035 .parquet_metadata
3036 .row_groups()
3037 .iter()
3038 .flat_map(|row_group| row_group.columns())
3039 .find(|column| {
3040 column.column_path().string() == "v1"
3041 && column.column_type() == PhysicalType::DOUBLE
3042 })
3043 .expect("Float64 field should be present in encoded bulk part");
3044
3045 assert!(
3046 field_column
3047 .encodings()
3048 .any(|encoding| encoding == Encoding::BYTE_STREAM_SPLIT)
3049 );
3050 Ok(())
3051 }
3052
3053 #[test]
3054 fn test_bulk_part_encoder_matches_sst_writer_internal_encodings() -> WhateverResult<()> {
3055 let metadata = metadata_for_test();
3056 let bulk_part = create_bulk_part_with_converter(
3057 "key",
3058 0,
3059 vec![1000, 2000, 3000],
3060 vec![Some(1.0), Some(2.0), Some(3.0)],
3061 100,
3062 )?;
3063 let encoder = BulkPartEncoder::new(metadata, DEFAULT_ROW_GROUP_SIZE)?;
3064 let encoded_part = encoder.encode_part(&bulk_part)?.unwrap();
3065 let columns = encoded_part
3066 .metadata()
3067 .parquet_metadata
3068 .row_group(0)
3069 .columns();
3070
3071 for name in ["ts", SEQUENCE_COLUMN_NAME] {
3072 let column = columns
3073 .iter()
3074 .find(|column| column.column_path().string() == name)
3075 .unwrap();
3076 assert!(
3077 column
3078 .encodings()
3079 .any(|encoding| encoding == Encoding::DELTA_BINARY_PACKED),
3080 "{name}"
3081 );
3082 assert!(column.dictionary_page_offset().is_none(), "{name}");
3083 }
3084 let op_type = columns
3085 .iter()
3086 .find(|column| column.column_path().string() == OP_TYPE_COLUMN_NAME)
3087 .unwrap();
3088 assert_eq!(Compression::UNCOMPRESSED, op_type.compression());
3089 Ok(())
3090 }
3091
3092 #[test]
3093 fn test_encoded_part_batch_size_uses_largest_uncompressed_row_group() {
3094 const NUM_ROWS: usize = 14;
3095 const ROW_GROUP_SIZE: usize = 4;
3096
3097 let metadata = metadata_for_test();
3098 let timestamps = (0..NUM_ROWS as i64).collect::<Vec<_>>();
3099 let field_values = (0..NUM_ROWS)
3100 .map(|value| Some(value as f64))
3101 .collect::<Vec<_>>();
3102 let bulk_part =
3103 create_bulk_part_with_converter("key", 0, timestamps, field_values, 0).unwrap();
3104 let encoder = BulkPartEncoder::new(metadata, ROW_GROUP_SIZE).unwrap();
3105 let encoded_part = encoder.encode_part(&bulk_part).unwrap().unwrap();
3106 let max_row_group = encoded_part
3107 .metadata()
3108 .parquet_metadata
3109 .row_groups()
3110 .iter()
3111 .max_by_key(|row_group| {
3112 row_group
3113 .columns()
3114 .iter()
3115 .map(|column| column.uncompressed_size() as u64)
3116 .sum::<u64>()
3117 })
3118 .unwrap();
3119 let max_uncompressed_size = max_row_group
3120 .columns()
3121 .iter()
3122 .map(|column| column.uncompressed_size() as u64)
3123 .sum();
3124 let expected = (max_row_group.num_rows() as u64, max_uncompressed_size);
3125 let part = PartToMerge::Encoded {
3126 part: encoded_part,
3127 file_id: FileId::random(),
3128 };
3129
3130 assert_eq!(Some(expected), part.batch_size_statistic());
3131 }
3132
3133 #[test]
3134 fn test_bulk_memtable_ranges_with_multi_bulk_part() {
3135 let metadata = metadata_for_test();
3136 let merge_threshold = 8;
3137 let config = BulkMemtableConfig {
3138 merge_threshold,
3139 ..Default::default()
3140 };
3141 let memtable = BulkMemtable::new(
3142 2005,
3143 config,
3144 metadata.clone(),
3145 None,
3146 None,
3147 false,
3148 MergeMode::LastRow,
3149 );
3150 memtable.set_unordered_part_threshold(0);
3152
3153 for i in 0..merge_threshold {
3157 let part = create_bulk_part_with_converter(
3158 &format!("key_{}", i),
3159 i as u32,
3160 vec![1000 + i as i64 * 100, 2000 + i as i64 * 100],
3161 vec![Some(i as f64 * 10.0), Some(i as f64 * 10.0 + 1.0)],
3162 100 + i as u64,
3163 )
3164 .unwrap();
3165 memtable.write_bulk(part).unwrap();
3166 }
3167
3168 memtable.compact(false).unwrap();
3170
3171 let predicate_group = PredicateGroup::new(&metadata, &[]).unwrap();
3173 let ranges = memtable
3174 .ranges(
3175 None,
3176 RangesOptions::default().with_predicate(predicate_group),
3177 )
3178 .unwrap();
3179
3180 assert_eq!(1, ranges.ranges.len());
3181 let expected_rows = merge_threshold * 2; let total_rows: usize = ranges.ranges.values().map(|r| r.stats().num_rows()).sum();
3183 assert_eq!(expected_rows, total_rows);
3184
3185 let mut total_rows_read = 0;
3187 for range in ranges.ranges.values() {
3188 assert!(range.is_record_batch());
3189 let record_batch_iter = range.build_record_batch_iter(None, None).unwrap();
3190
3191 for batch_result in record_batch_iter {
3192 let batch = batch_result.unwrap();
3193 total_rows_read += batch.num_rows();
3194 }
3195 }
3196 assert_eq!(expected_rows, total_rows_read);
3197 }
3198
3199 #[test]
3200 fn test_multi_bulk_range_iter_builder_all_pruned() {
3201 let metadata = metadata_for_test();
3202 let merge_threshold = 8;
3203 let config = BulkMemtableConfig {
3204 merge_threshold,
3205 ..Default::default()
3206 };
3207 let memtable = BulkMemtable::new(
3208 2006,
3209 config,
3210 metadata.clone(),
3211 None,
3212 None,
3213 false,
3214 MergeMode::LastRow,
3215 );
3216 memtable.set_unordered_part_threshold(0);
3217
3218 for i in 0..merge_threshold {
3220 let part = create_bulk_part_with_converter(
3221 &format!("key_{}", i),
3222 i as u32,
3223 vec![1000 + i as i64 * 100, 2000 + i as i64 * 100],
3224 vec![Some(i as f64 * 10.0), Some(i as f64 * 10.0 + 1.0)],
3225 100 + i as u64,
3226 )
3227 .unwrap();
3228 memtable.write_bulk(part).unwrap();
3229 }
3230 memtable.compact(false).unwrap();
3231
3232 let filter = datafusion_expr::col("k0").eq(datafusion_expr::lit("nonexistent"));
3234 let predicate_group = PredicateGroup::new(&metadata, &[filter]).unwrap();
3235 let ranges = memtable
3236 .ranges(
3237 None,
3238 RangesOptions::default().with_predicate(predicate_group),
3239 )
3240 .unwrap();
3241
3242 for range in ranges.ranges.values() {
3245 assert!(range.is_record_batch());
3246 let record_batch_iter = range.build_record_batch_iter(None, None).unwrap();
3247 let total_rows: usize = record_batch_iter.map(|r| r.unwrap().num_rows()).sum();
3248 assert_eq!(0, total_rows);
3249 }
3250 }
3251}