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, .. } => part
1159 .metadata()
1160 .parquet_metadata
1161 .row_groups()
1162 .iter()
1163 .map(|row_group| {
1164 let uncompressed_bytes = row_group
1165 .columns()
1166 .iter()
1167 .map(|column| column.uncompressed_size() as u64)
1168 .sum();
1169 (row_group.num_rows() as u64, uncompressed_bytes)
1170 })
1171 .max_by_key(|(_, uncompressed_bytes)| *uncompressed_bytes),
1172 }
1173 }
1174
1175 fn to_memtable_stats(&self, region_metadata: &RegionMetadataRef) -> MemtableStats {
1177 match self {
1178 PartToMerge::Bulk { part, .. } => part.to_memtable_stats(region_metadata),
1179 PartToMerge::Multi { part, .. } => part.to_memtable_stats(region_metadata),
1180 PartToMerge::Encoded { part, .. } => part.to_memtable_stats(),
1181 }
1182 }
1183
1184 fn arrow_schema(&self) -> SchemaRef {
1186 match self {
1187 PartToMerge::Bulk { part, .. } => part.schema(),
1188 PartToMerge::Multi { part, .. } => part
1191 .schemas()
1192 .next()
1193 .expect("MultiBulkPart must contain at least one record batch"),
1194 PartToMerge::Encoded { part, .. } => part.schema(),
1195 }
1196 }
1197
1198 fn create_iterator(
1200 self,
1201 context: Arc<BulkIterContext>,
1202 plans: Arc<Json2RewritePlans>,
1203 ) -> Result<Option<BoxedRecordBatchIterator>> {
1204 let iter = match self {
1205 PartToMerge::Bulk { part, .. } => {
1206 let series_count = part.estimated_series_count();
1207 let iter = BulkPartBatchIter::from_single(
1208 part.batch,
1209 context,
1210 None, series_count,
1212 None, );
1214 Some(Box::new(iter) as BoxedRecordBatchIterator)
1215 }
1216 PartToMerge::Multi { part, .. } => part.read(context, None, None)?,
1217 PartToMerge::Encoded { part, .. } => part.read(context, None, None)?,
1218 };
1219 if plans.is_empty() {
1220 Ok(iter)
1221 } else {
1222 Ok(iter.map(|x| {
1223 Box::new(x.map(move |batch| rewrite_json2_batch(batch?, &plans)))
1224 as BoxedRecordBatchIterator
1225 }))
1226 }
1227 }
1228}
1229
1230struct MemtableCompactor {
1231 region_id: RegionId,
1232 memtable_id: MemtableId,
1233 config: BulkMemtableConfig,
1235 row_group_size: usize,
1237 float_field_encoding: FloatFieldEncoding,
1238}
1239
1240impl MemtableCompactor {
1241 fn new(
1243 region_id: RegionId,
1244 memtable_id: MemtableId,
1245 config: BulkMemtableConfig,
1246 row_group_size: usize,
1247 float_field_encoding: FloatFieldEncoding,
1248 ) -> Self {
1249 Self {
1250 region_id,
1251 memtable_id,
1252 config,
1253 row_group_size,
1254 float_field_encoding,
1255 }
1256 }
1257
1258 fn merge_parts(
1260 &mut self,
1261 bulk_parts: &RwLock<BulkParts>,
1262 metadata: &RegionMetadataRef,
1263 dedup: bool,
1264 merge_mode: MergeMode,
1265 ) -> Result<()> {
1266 let start = Instant::now();
1267
1268 let collected = {
1269 let mut parts = bulk_parts.write().unwrap();
1270 parts.collect_parts_to_merge(
1271 self.config.merge_threshold,
1272 self.config.max_merge_groups,
1273 self.config.encode_bytes_threshold,
1274 )
1275 };
1276
1277 if collected.groups.is_empty() {
1278 return Ok(());
1279 }
1280
1281 let merged_file_ids: HashSet<FileId> = collected
1283 .groups
1284 .iter()
1285 .flatten()
1286 .map(|part| part.file_id())
1287 .collect();
1288 let mut guard = MergingFlagsGuard::new(bulk_parts, &merged_file_ids);
1289
1290 let num_groups = collected.groups.len();
1291 let num_parts: usize = collected.groups.iter().map(|g| g.len()).sum();
1292
1293 let encode_row_threshold = self.config.encode_row_threshold;
1294 let encode_bytes_threshold = self.config.encode_bytes_threshold;
1295 let row_group_size = self.row_group_size;
1296 let float_field_encoding = self.float_field_encoding;
1297
1298 let merged_parts = collected
1300 .groups
1301 .into_par_iter()
1302 .map(|group| {
1303 Self::merge_parts_group(
1304 group,
1305 metadata,
1306 dedup,
1307 merge_mode,
1308 encode_row_threshold,
1309 encode_bytes_threshold,
1310 row_group_size,
1311 float_field_encoding,
1312 )
1313 })
1314 .collect::<Result<Vec<Option<MergedPart>>>>()?;
1315
1316 let total_output_rows = {
1318 let mut parts = bulk_parts.write().unwrap();
1319 parts.install_merged_parts(merged_parts.into_iter().flatten(), &merged_file_ids)
1320 };
1321
1322 guard.mark_success();
1323
1324 common_telemetry::debug!(
1325 "BulkMemtable {} {} concurrent compact {} groups, {} parts, {} rows, cost: {:?}",
1326 self.region_id,
1327 self.memtable_id,
1328 num_groups,
1329 num_parts,
1330 total_output_rows,
1331 start.elapsed()
1332 );
1333
1334 Ok(())
1335 }
1336
1337 #[allow(clippy::too_many_arguments)]
1339 fn merge_parts_group(
1340 parts_to_merge: Vec<PartToMerge>,
1341 metadata: &RegionMetadataRef,
1342 dedup: bool,
1343 merge_mode: MergeMode,
1344 encode_row_threshold: usize,
1345 encode_bytes_threshold: usize,
1346 row_group_size: usize,
1347 float_field_encoding: FloatFieldEncoding,
1348 ) -> Result<Option<MergedPart>> {
1349 if parts_to_merge.is_empty() {
1350 return Ok(None);
1351 }
1352
1353 let min_timestamp = parts_to_merge
1355 .iter()
1356 .map(|p| p.min_timestamp())
1357 .min()
1358 .unwrap_or(i64::MAX);
1359 let max_timestamp = parts_to_merge
1360 .iter()
1361 .map(|p| p.max_timestamp())
1362 .max()
1363 .unwrap_or(i64::MIN);
1364 let min_sequence = parts_to_merge
1365 .iter()
1366 .map(PartToMerge::min_sequence)
1367 .min()
1368 .unwrap_or(0);
1369 let max_sequence = parts_to_merge
1370 .iter()
1371 .map(|p| p.max_sequence())
1372 .max()
1373 .unwrap_or(0);
1374
1375 let estimated_total_rows: usize = parts_to_merge.iter().map(|p| p.num_rows()).sum();
1377 let estimated_total_bytes: usize = parts_to_merge.iter().map(|p| p.estimated_size()).sum();
1378 let estimated_series_count = parts_to_merge
1379 .iter()
1380 .map(|p| p.series_count())
1381 .max()
1382 .unwrap_or(0);
1383
1384 let batch_size = crate::batch_size::estimate_batch_size(
1385 parts_to_merge
1386 .iter()
1387 .filter_map(PartToMerge::batch_size_statistic),
1388 );
1389 let context = Arc::new(BulkIterContext::new(
1390 metadata.clone(),
1391 None, None, true,
1394 batch_size,
1395 )?);
1396
1397 let schemas = parts_to_merge
1398 .iter()
1399 .map(|part| (part.arrow_schema(), part.num_rows() as u64))
1400 .collect::<Vec<_>>();
1401 let plans = Arc::new(collect_json2_rewrite_plans(metadata, &schemas)?);
1402
1403 debug_assert!(parts_to_merge.windows(2).all(|w| rewrite_json2_schema(
1404 &w[0].arrow_schema(),
1405 &plans
1406 ) == rewrite_json2_schema(
1407 &w[1].arrow_schema(),
1408 &plans
1409 )));
1410 let schema = rewrite_json2_schema(&parts_to_merge[0].arrow_schema(), &plans);
1414
1415 let iterators: Vec<BoxedRecordBatchIterator> = parts_to_merge
1416 .into_iter()
1417 .map(|part| part.create_iterator(context.clone(), plans.clone()))
1418 .collect::<Result<Vec<_>>>()?
1419 .into_iter()
1420 .flatten()
1421 .collect();
1422
1423 if iterators.is_empty() {
1424 return Ok(None);
1425 }
1426
1427 let merged_iter = FlatMergeIterator::new(schema.clone(), iterators, batch_size)?;
1428
1429 let boxed_iter: BoxedRecordBatchIterator = if dedup {
1430 match merge_mode {
1431 MergeMode::LastRow => {
1432 let dedup_iter = FlatDedupIterator::new(merged_iter, FlatLastRow::new(false));
1433 Box::new(dedup_iter)
1434 }
1435 MergeMode::LastNonNull => {
1436 let field_column_start = field_column_start(metadata, schema.fields().len());
1437
1438 let dedup_iter = FlatDedupIterator::new(
1439 merged_iter,
1440 FlatLastNonNull::new(field_column_start, false),
1441 );
1442 Box::new(dedup_iter)
1443 }
1444 }
1445 } else {
1446 Box::new(merged_iter)
1447 };
1448
1449 if estimated_total_rows > encode_row_threshold
1451 || estimated_total_bytes > encode_bytes_threshold
1452 {
1453 let encoder = BulkPartEncoder::new_with_float_field_encoding(
1454 metadata.clone(),
1455 row_group_size,
1456 float_field_encoding,
1457 )?;
1458 let mut metrics = BulkPartEncodeMetrics::default();
1459 let encoded_part = encoder.encode_record_batch_iter(
1460 boxed_iter,
1461 schema,
1462 min_timestamp,
1463 max_timestamp,
1464 min_sequence..=max_sequence,
1465 &mut metrics,
1466 )?;
1467
1468 common_telemetry::trace!("merge_parts_group metrics: {:?}", metrics);
1469
1470 Ok(encoded_part.map(MergedPart::Encoded))
1471 } else {
1472 let mut batches = Vec::new();
1474 let mut actual_total_rows = 0;
1475
1476 for batch_result in boxed_iter {
1477 let batch = batch_result?;
1478 actual_total_rows += batch.num_rows();
1479 batches.push(batch);
1480 }
1481
1482 if actual_total_rows == 0 {
1483 return Ok(None);
1484 }
1485
1486 let multi_part = MultiBulkPart::new(
1487 batches,
1488 min_timestamp,
1489 max_timestamp,
1490 min_sequence,
1491 max_sequence,
1492 estimated_series_count,
1493 metadata,
1494 );
1495
1496 common_telemetry::trace!(
1497 "merge_parts_group created MultiBulkPart: rows={}, batches={}",
1498 actual_total_rows,
1499 multi_part.num_batches()
1500 );
1501
1502 Ok(Some(MergedPart::Multi(multi_part)))
1503 }
1504 }
1505}
1506
1507struct CompactTaskGuard(Arc<AtomicBool>);
1510
1511impl CompactTaskGuard {
1512 fn try_new(compacting: Arc<AtomicBool>) -> Option<Self> {
1513 compacting
1515 .compare_exchange(false, true, Ordering::Relaxed, Ordering::Relaxed)
1516 .ok()
1517 .map(|_| Self(compacting))
1518 }
1519}
1520
1521impl Drop for CompactTaskGuard {
1522 fn drop(&mut self) {
1523 self.0.store(false, Ordering::Relaxed);
1524 }
1525}
1526
1527struct MemCompactTask {
1529 metadata: RegionMetadataRef,
1530 parts: Arc<RwLock<BulkParts>>,
1531 _guard: CompactTaskGuard,
1532 compactor: Arc<Mutex<MemtableCompactor>>,
1534 append_mode: bool,
1536 merge_mode: MergeMode,
1538}
1539
1540impl MemCompactTask {
1541 fn compact(&self) -> Result<()> {
1542 self.compactor.lock().unwrap().merge_parts(
1543 &self.parts,
1544 &self.metadata,
1545 !self.append_mode,
1546 self.merge_mode,
1547 )
1548 }
1549}
1550
1551#[derive(Debug)]
1553pub struct CompactDispatcher {
1554 semaphore: Arc<Semaphore>,
1555}
1556
1557impl CompactDispatcher {
1558 pub fn new(permits: usize) -> Self {
1560 Self {
1561 semaphore: Arc::new(Semaphore::new(permits)),
1562 }
1563 }
1564
1565 fn dispatch_compact(&self, task: MemCompactTask) {
1567 let semaphore = self.semaphore.clone();
1568 common_runtime::spawn_global(async move {
1569 let Ok(permit) = semaphore.acquire_owned().await else {
1570 return;
1571 };
1572
1573 common_runtime::spawn_blocking_global(move || {
1574 let _permit = permit;
1578 if let Err(e) = task.compact() {
1579 common_telemetry::error!(e; "Failed to compact memtable, region: {}", task.metadata.region_id);
1580 }
1581 });
1582 });
1583 }
1584}
1585
1586#[derive(Debug)]
1588pub struct BulkMemtableBuilder {
1589 config: BulkMemtableConfig,
1591 write_buffer_manager: Option<WriteBufferManagerRef>,
1592 compact_dispatcher: Option<Arc<CompactDispatcher>>,
1593 append_mode: bool,
1594 merge_mode: MergeMode,
1595 row_group_size: usize,
1597 float_field_encoding: FloatFieldEncoding,
1598}
1599
1600impl Default for BulkMemtableBuilder {
1601 fn default() -> Self {
1602 Self {
1603 config: BulkMemtableConfig::default(),
1604 write_buffer_manager: None,
1605 compact_dispatcher: None,
1606 append_mode: false,
1607 merge_mode: MergeMode::default(),
1608 row_group_size: DEFAULT_ROW_GROUP_SIZE,
1609 float_field_encoding: FloatFieldEncoding::default(),
1610 }
1611 }
1612}
1613
1614impl BulkMemtableBuilder {
1615 pub fn new(
1617 write_buffer_manager: Option<WriteBufferManagerRef>,
1618 append_mode: bool,
1619 merge_mode: MergeMode,
1620 ) -> Self {
1621 Self {
1622 write_buffer_manager,
1623 append_mode,
1624 merge_mode,
1625 ..Default::default()
1626 }
1627 }
1628
1629 pub fn with_config(mut self, config: BulkMemtableConfig) -> Self {
1631 self.config = config;
1632 self
1633 }
1634
1635 pub fn with_row_group_size(mut self, row_group_size: usize) -> Self {
1637 self.row_group_size = row_group_size;
1638 self
1639 }
1640
1641 pub(super) fn with_float_field_encoding(mut self, encoding: FloatFieldEncoding) -> Self {
1642 self.float_field_encoding = encoding;
1643 self
1644 }
1645
1646 pub fn with_compact_dispatcher(mut self, compact_dispatcher: Arc<CompactDispatcher>) -> Self {
1648 self.compact_dispatcher = Some(compact_dispatcher);
1649 self
1650 }
1651
1652 #[cfg(test)]
1653 pub(crate) fn config(&self) -> &BulkMemtableConfig {
1654 &self.config
1655 }
1656}
1657
1658impl MemtableBuilder for BulkMemtableBuilder {
1659 fn build(&self, id: MemtableId, metadata: &RegionMetadataRef) -> MemtableRef {
1660 Arc::new(BulkMemtable::new_with_row_group_size_and_encoding(
1661 id,
1662 self.config.clone(),
1663 metadata.clone(),
1664 self.write_buffer_manager.clone(),
1665 self.compact_dispatcher.clone(),
1666 self.append_mode,
1667 self.merge_mode,
1668 self.row_group_size,
1669 self.float_field_encoding,
1670 ))
1671 }
1672
1673 fn use_bulk_insert(&self, _metadata: &RegionMetadataRef) -> bool {
1674 true
1675 }
1676}
1677
1678#[cfg(test)]
1679mod tests {
1680 use api::helper::encode_json_value;
1681 use api::v1::value::ValueData;
1682 use api::v1::{Mutation, Row, Rows, SemanticType};
1683 use common_error::ext::WhateverResult;
1684 use datatypes::arrow::array::AsArray;
1685 use datatypes::arrow::datatypes::{DataType as ArrowDataType, UInt64Type};
1686 use datatypes::data_type::ConcreteDataType;
1687 use datatypes::extension::json::{
1688 JSON2_REMAINDER_FIELD_NAME, Json2ExtensionType, Json2PhysicalLayout, JsonMetadata,
1689 };
1690 use datatypes::json::value::JsonValue;
1691 use datatypes::json::{JsonSettings, JsonTypeHint};
1692 use datatypes::schema::ColumnSchema;
1693 use datatypes::types::json_type::{JsonNativeType, JsonObjectType};
1694 use mito_codec::row_converter::build_primary_key_codec;
1695 use parquet::basic::{Encoding, Type as PhysicalType};
1696 use serde_json::json;
1697 use store_api::metadata::{ColumnMetadata, RegionMetadataBuilder, RegionMetadataRef};
1698
1699 use super::*;
1700 use crate::memtable::bulk::part::BulkPartConverter;
1701 use crate::read::scan_region::PredicateGroup;
1702 use crate::sst::parquet::flat_format::sequence_column_index;
1703 use crate::sst::{FlatSchemaOptions, to_flat_sst_arrow_schema};
1704 use crate::test_util::memtable_util::{
1705 build_key_values_with_ts_seq_values, metadata_for_test, region_metadata_to_row_schema,
1706 };
1707
1708 fn create_bulk_part_with_converter(
1709 k0: &str,
1710 k1: u32,
1711 timestamps: Vec<i64>,
1712 values: Vec<Option<f64>>,
1713 sequence: u64,
1714 ) -> Result<BulkPart> {
1715 let metadata = metadata_for_test();
1716 let capacity = 100;
1717 let primary_key_codec = build_primary_key_codec(&metadata);
1718 let schema = to_flat_sst_arrow_schema(
1719 &metadata,
1720 &FlatSchemaOptions::from_encoding(metadata.primary_key_encoding),
1721 );
1722
1723 let mut converter =
1724 BulkPartConverter::new(&metadata, schema, capacity, primary_key_codec, true);
1725
1726 let key_values = build_key_values_with_ts_seq_values(
1727 &metadata,
1728 k0.to_string(),
1729 k1,
1730 timestamps.into_iter(),
1731 values.into_iter(),
1732 sequence,
1733 );
1734
1735 converter.append_key_values(&key_values)?;
1736 converter.convert()
1737 }
1738
1739 #[test]
1740 fn test_adaptive_encode_bytes_threshold() {
1741 assert_eq!(
1743 DEFAULT_ENCODE_BYTES_THRESHOLD,
1744 adaptive_encode_bytes_threshold(1024 * 1024 * 1024) );
1746 assert_eq!(
1748 256 * 1024 * 1024,
1749 adaptive_encode_bytes_threshold(8 * 1024 * 1024 * 1024) );
1751 assert_eq!(
1753 MAX_ENCODE_BYTES_THRESHOLD,
1754 adaptive_encode_bytes_threshold(64 * 1024 * 1024 * 1024) );
1756 }
1757
1758 #[test]
1759 fn test_bulk_memtable_sanitizes_zero_merge_threshold() {
1760 let metadata = metadata_for_test();
1761 let config = BulkMemtableConfig {
1762 merge_threshold: 0,
1763 ..Default::default()
1764 };
1765
1766 let memtable =
1767 BulkMemtable::new(999, config, metadata, None, None, false, MergeMode::LastRow);
1768
1769 assert_eq!(DEFAULT_MERGE_THRESHOLD, memtable.config.merge_threshold);
1770 assert_eq!(
1771 DEFAULT_MERGE_THRESHOLD,
1772 memtable.compactor.lock().unwrap().config.merge_threshold
1773 );
1774 }
1775
1776 #[test]
1777 fn test_min_sequence_survives_mixed_parts_compaction_and_fork() {
1778 let metadata = metadata_for_test();
1779 let config = BulkMemtableConfig {
1780 merge_threshold: 3,
1781 encode_row_threshold: 1,
1782 encode_bytes_threshold: 1,
1783 ..Default::default()
1784 };
1785 let memtable = BulkMemtable::new(
1786 1,
1787 config,
1788 metadata.clone(),
1789 None,
1790 None,
1791 false,
1792 MergeMode::LastNonNull,
1793 );
1794 memtable.set_unordered_part_threshold(0);
1795 for (sequence, expected_min, expected_ranges) in [(100, 100, 1), (10, 10, 2), (200, 10, 1)]
1796 {
1797 let part = create_bulk_part_with_converter(
1798 "a",
1799 1,
1800 vec![2000, 1000],
1801 vec![Some(1.0), Some(2.0)],
1802 sequence,
1803 )
1804 .unwrap();
1805 assert_eq!(sequence + 1, part.sequence);
1808 assert_eq!(sequence, part.min_sequence);
1809 let sequences = part
1810 .batch
1811 .column(sequence_column_index(part.batch.num_columns()))
1812 .as_primitive::<UInt64Type>();
1813 assert_eq!(&[sequence + 1, sequence], sequences.values().as_ref());
1814 memtable.write_bulk(part).unwrap();
1815 assert_eq!(expected_min, memtable.stats().min_sequence);
1816 assert_eq!(expected_min, memtable.min_sequence());
1817 assert_eq!(expected_ranges, memtable.stats().num_ranges);
1818 assert!(!memtable.compacting.load(Ordering::Relaxed));
1819 }
1820 assert_eq!(10, memtable.stats().min_sequence);
1823 assert_eq!(201, memtable.stats().max_sequence);
1824 let ranges = memtable.ranges(None, RangesOptions::default()).unwrap();
1825 let merged = ranges.ranges.values().next().unwrap();
1826 assert!(merged.encoded().is_some());
1827 assert_eq!(10, merged.stats().min_sequence);
1828 let fork = memtable.fork(2, &metadata);
1829 assert!(fork.is_empty());
1830 assert_eq!(0, fork.min_sequence());
1831 fork.write_bulk(
1832 create_bulk_part_with_converter("a", 1, vec![1000], vec![Some(1.0)], 50).unwrap(),
1833 )
1834 .unwrap();
1835 assert_eq!(50, fork.stats().min_sequence);
1836 assert_eq!(50, fork.min_sequence());
1837 }
1838
1839 #[test]
1840 fn test_bulk_memtable_write_read() {
1841 let metadata = metadata_for_test();
1842 let memtable = BulkMemtable::new(
1843 999,
1844 BulkMemtableConfig::default(),
1845 metadata.clone(),
1846 None,
1847 None,
1848 false,
1849 MergeMode::LastRow,
1850 );
1851 memtable.set_unordered_part_threshold(0);
1853
1854 let test_data = [
1855 (
1856 "key_a",
1857 1u32,
1858 vec![1000i64, 2000i64],
1859 vec![Some(10.5), Some(20.5)],
1860 100u64,
1861 ),
1862 (
1863 "key_b",
1864 2u32,
1865 vec![1500i64, 2500i64],
1866 vec![Some(15.5), Some(25.5)],
1867 200u64,
1868 ),
1869 ("key_c", 3u32, vec![3000i64], vec![Some(30.5)], 300u64),
1870 ];
1871
1872 for (k0, k1, timestamps, values, seq) in test_data.iter() {
1873 let part =
1874 create_bulk_part_with_converter(k0, *k1, timestamps.clone(), values.clone(), *seq)
1875 .unwrap();
1876 memtable.write_bulk(part).unwrap();
1877 }
1878
1879 let stats = memtable.stats();
1880 assert_eq!(5, stats.num_rows);
1881 assert_eq!(3, stats.num_ranges);
1882 assert_eq!(300, stats.max_sequence);
1883 assert_eq!(100, stats.min_sequence);
1884
1885 let (min_ts, max_ts) = stats.time_range.unwrap();
1886 assert_eq!(1000, min_ts.value());
1887 assert_eq!(3000, max_ts.value());
1888
1889 let predicate_group = PredicateGroup::new(&metadata, &[]).unwrap();
1890 let ranges = memtable
1891 .ranges(
1892 None,
1893 RangesOptions::default().with_predicate(predicate_group),
1894 )
1895 .unwrap();
1896
1897 assert_eq!(3, ranges.ranges.len());
1898 let total_rows: usize = ranges.ranges.values().map(|r| r.stats().num_rows()).sum();
1899 assert_eq!(5, total_rows);
1900
1901 for range in ranges.ranges.values() {
1902 assert!(range.num_rows() > 0);
1903 assert!(range.is_record_batch());
1904
1905 let record_batch_iter = range.build_record_batch_iter(None, None).unwrap();
1906
1907 let mut total_rows = 0;
1908 for batch_result in record_batch_iter {
1909 let batch = batch_result.unwrap();
1910 total_rows += batch.num_rows();
1911 assert!(batch.num_rows() > 0);
1912 assert_eq!(8, batch.num_columns());
1913 }
1914 assert_eq!(total_rows, range.num_rows());
1915 }
1916 }
1917
1918 #[test]
1919 fn test_bulk_memtable_compact_parts_with_json2() {
1920 let metadata = mock_metadata_with_json2(JsonSettings::default());
1921
1922 let config = BulkMemtableConfig {
1923 merge_threshold: 2,
1924 encode_row_threshold: 1,
1925 encode_bytes_threshold: 1,
1926 ..Default::default()
1927 };
1928 let memtable = BulkMemtable::new(
1929 999,
1930 config,
1931 metadata.clone(),
1932 None,
1933 None,
1934 true,
1935 MergeMode::LastRow,
1936 );
1937 memtable.set_unordered_part_threshold(0);
1938
1939 let part1 = mock_bulk_part_with_json2(&metadata, vec![1000, 2000], 100).unwrap();
1940 let part2 = mock_bulk_part_with_json2(&metadata, vec![3000, 4000], 200).unwrap();
1941
1942 memtable.write_bulk(part1).unwrap();
1943 memtable.write_bulk(part2).unwrap();
1944 memtable.compact(false).unwrap();
1945
1946 let stats = memtable.stats();
1947 assert_eq!(4, stats.num_rows);
1948 assert_eq!(201, stats.max_sequence);
1949
1950 let predicate_group = PredicateGroup::new(&metadata, &[]).unwrap();
1951 let opts = RangesOptions::default().with_predicate(predicate_group);
1952 let ranges = memtable.ranges(None, opts).unwrap();
1953
1954 assert_eq!(1, ranges.ranges.len());
1955 let total_rows: usize = ranges.ranges.values().map(|r| r.stats().num_rows()).sum();
1956 assert_eq!(4, total_rows);
1957 }
1958
1959 #[test]
1960 fn test_bulk_memtable_merge_bounds_json2_paths() -> WhateverResult<()> {
1961 let metadata = mock_metadata_with_json2(JsonSettings::try_new(vec![], Some(1))?);
1962 let first = mock_bulk_part_with_json2_values(
1963 &metadata,
1964 vec![1000, 2000],
1965 vec![json!({"a": 1}), json!({"a": 2})],
1966 100,
1967 )?;
1968 let second = mock_bulk_part_with_json2_values(
1969 &metadata,
1970 vec![3000, 4000],
1971 vec![json!({"a": 3, "b": 3}), json!({"a": 4, "b": 4})],
1972 200,
1973 )?;
1974 let parts = vec![
1975 PartToMerge::Bulk {
1976 part: first,
1977 file_id: FileId::random(),
1978 },
1979 PartToMerge::Bulk {
1980 part: second,
1981 file_id: FileId::random(),
1982 },
1983 ];
1984
1985 let merged = MemtableCompactor::merge_parts_group(
1986 parts,
1987 &metadata,
1988 false,
1989 MergeMode::LastRow,
1990 usize::MAX,
1991 usize::MAX,
1992 DEFAULT_ROW_GROUP_SIZE,
1993 FloatFieldEncoding::default(),
1994 )?
1995 .unwrap();
1996 let MergedPart::Multi(part) = merged else {
1997 unreachable!()
1998 };
1999 let schema = part.schemas().next().unwrap();
2000 let ArrowDataType::Struct(fields) = schema.field(0).data_type() else {
2001 unreachable!()
2002 };
2003 assert_eq!(
2004 vec![JSON2_REMAINDER_FIELD_NAME, "a"],
2005 fields.iter().map(|x| x.name().as_str()).collect::<Vec<_>>()
2006 );
2007 Ok(())
2008 }
2009
2010 #[test]
2011 fn test_unordered_parts_align_json2_layouts() -> WhateverResult<()> {
2012 let metadata = mock_metadata_with_json2(JsonSettings::try_new(vec![], Some(2))?);
2013 let memtable = BulkMemtable::new(
2014 42,
2015 BulkMemtableConfig::default(),
2016 metadata.clone(),
2017 None,
2018 None,
2019 false,
2020 MergeMode::LastRow,
2021 );
2022 memtable.write_bulk(mock_bulk_part_with_json2_values(
2023 &metadata,
2024 vec![1000, 2000],
2025 vec![json!({"a": 1}), json!({"a": 2})],
2026 100,
2027 )?)?;
2028 memtable.write_bulk(mock_bulk_part_with_json2_values(
2029 &metadata,
2030 vec![3000, 4000],
2031 vec![json!({"b": 3}), json!({"b": 4})],
2032 200,
2033 )?)?;
2034
2035 let predicate = PredicateGroup::new(&metadata, &[])?;
2036 let ranges = memtable.ranges(None, RangesOptions::default().with_predicate(predicate))?;
2037 let range = ranges.ranges.values().next().unwrap();
2038 let batch = range.build_record_batch_iter(None, None)?.next().unwrap()?;
2039 let schema = batch.schema();
2040 let ArrowDataType::Struct(fields) = schema.field(0).data_type() else {
2041 unreachable!()
2042 };
2043 assert_eq!(
2044 vec![JSON2_REMAINDER_FIELD_NAME],
2045 fields.iter().map(|x| x.name().as_str()).collect::<Vec<_>>()
2046 );
2047 assert_eq!(4, batch.num_rows());
2049 let json = datatypes::vectors::json::array::JsonArray::from(batch.column(0));
2050 let restored = json.project_to_v2(schema.field(0), &ArrowDataType::Binary)?;
2051 let restored = datatypes::vectors::json::array::JsonArray::from(&restored);
2052 assert_eq!(json!({"a": 1}), restored.try_get_value(0)?);
2053 assert_eq!(json!({"a": 2}), restored.try_get_value(1)?);
2054 assert_eq!(json!({"b": 3}), restored.try_get_value(2)?);
2055 assert_eq!(json!({"b": 4}), restored.try_get_value(3)?);
2056 Ok(())
2057 }
2058
2059 #[test]
2060 fn test_bulk_part_converter_uses_json2_v2_layout() -> WhateverResult<()> {
2061 let settings = JsonSettings::try_new(
2062 vec![JsonTypeHint {
2063 path: vec!["id".to_string()],
2064 data_type: ConcreteDataType::int64_datatype(),
2065 inverted_index: false,
2066 }],
2067 Some(0),
2068 )?;
2069 let metadata = mock_metadata_with_json2(settings);
2070 let part = mock_bulk_part_with_json2(&metadata, vec![1000, 2000], 100)?;
2071 let schema = part.batch.schema();
2072 let field = schema.field(0);
2073 let layout = Json2PhysicalLayout::try_from_root(field)?;
2074
2075 assert!(layout.is_version_2());
2076 let ArrowDataType::Struct(fields) = field.data_type() else {
2077 unreachable!()
2078 };
2079 assert_eq!(
2080 vec![JSON2_REMAINDER_FIELD_NAME, "id"],
2081 fields.iter().map(|x| x.name().as_str()).collect::<Vec<_>>()
2082 );
2083 Ok(())
2084 }
2085
2086 fn mock_metadata_with_json2(settings: JsonSettings) -> RegionMetadataRef {
2087 let col_meta_1 = ColumnMetadata {
2088 column_schema: ColumnSchema::new(
2089 "ts",
2090 ConcreteDataType::timestamp_millisecond_datatype(),
2091 false,
2092 ),
2093 semantic_type: SemanticType::Timestamp,
2094 column_id: 0,
2095 };
2096
2097 let data_type = ConcreteDataType::json2(JsonNativeType::Object(JsonObjectType::new()));
2098 let mut col_schema = ColumnSchema::new("data", data_type, true);
2099 col_schema.with_extension_type(&Json2ExtensionType::new(Arc::new(JsonMetadata::new(
2100 settings,
2101 ))));
2102
2103 let col_meta_2 = ColumnMetadata {
2104 column_schema: col_schema,
2105 semantic_type: SemanticType::Field,
2106 column_id: 1,
2107 };
2108 let mut builder = RegionMetadataBuilder::new(RegionId::new(123, 789));
2109 builder
2110 .push_column_metadata(col_meta_1)
2111 .push_column_metadata(col_meta_2)
2112 .primary_key(vec![]);
2113 Arc::new(builder.build().unwrap())
2114 }
2115
2116 fn mock_bulk_part_with_json2(
2117 metadata: &RegionMetadataRef,
2118 timestamps: Vec<i64>,
2119 sequence: u64,
2120 ) -> Result<BulkPart> {
2121 let values = timestamps
2122 .iter()
2123 .map(|ts| {
2124 json!({
2125 "id": ts,
2126 "payload": {
2127 "message": format!("row-{ts}"),
2128 },
2129 })
2130 })
2131 .collect();
2132 mock_bulk_part_with_json2_values(metadata, timestamps, values, sequence)
2133 }
2134
2135 fn mock_bulk_part_with_json2_values(
2136 metadata: &RegionMetadataRef,
2137 timestamps: Vec<i64>,
2138 values: Vec<serde_json::Value>,
2139 sequence: u64,
2140 ) -> Result<BulkPart> {
2141 let capacity = timestamps.len();
2142 debug_assert_eq!(capacity, values.len());
2143 let primary_key_codec = build_primary_key_codec(metadata);
2144 let json_type = JsonNativeType::Object(JsonObjectType::from([
2145 ("id".to_string(), JsonNativeType::i64()),
2146 (
2147 "payload".to_string(),
2148 JsonNativeType::Object(JsonObjectType::from([(
2149 "message".to_string(),
2150 JsonNativeType::String,
2151 )])),
2152 ),
2153 ]));
2154 let mut options = FlatSchemaOptions::from_encoding(metadata.primary_key_encoding);
2155 options
2156 .concretized_json_types
2157 .insert("data".to_string(), json_type.as_arrow_type());
2158 let schema = to_flat_sst_arrow_schema(metadata, &options);
2159
2160 let mut converter =
2161 BulkPartConverter::new(metadata, schema, capacity, primary_key_codec, true);
2162
2163 let rows = timestamps
2164 .into_iter()
2165 .zip(values)
2166 .map(|(ts, value)| {
2167 let val1 = api::v1::Value {
2168 value_data: Some(ValueData::TimestampMillisecondValue(ts)),
2169 };
2170 let value_data = ValueData::JsonValue(encode_json_value(JsonValue::from(value)));
2171 let val2 = api::v1::Value {
2172 value_data: Some(value_data),
2173 };
2174 Row {
2175 values: vec![val1, val2],
2176 }
2177 })
2178 .collect();
2179
2180 let mutation = Mutation {
2181 op_type: 1,
2182 sequence,
2183 rows: Some(Rows {
2184 schema: region_metadata_to_row_schema(metadata),
2185 rows,
2186 }),
2187 write_hint: None,
2188 };
2189 let key_values = KeyValues::new(metadata.as_ref(), mutation).unwrap();
2190
2191 converter.append_key_values(&key_values)?;
2192 converter.convert()
2193 }
2194
2195 #[test]
2196 fn test_bulk_memtable_ranges_with_projection() {
2197 let metadata = metadata_for_test();
2198 let memtable = BulkMemtable::new(
2199 111,
2200 BulkMemtableConfig::default(),
2201 metadata.clone(),
2202 None,
2203 None,
2204 false,
2205 MergeMode::LastRow,
2206 );
2207
2208 let bulk_part = create_bulk_part_with_converter(
2209 "projection_test",
2210 5,
2211 vec![5000, 6000, 7000],
2212 vec![Some(50.0), Some(60.0), Some(70.0)],
2213 500,
2214 )
2215 .unwrap();
2216
2217 memtable.write_bulk(bulk_part).unwrap();
2218
2219 let projection = vec![4u32];
2220 let predicate_group = PredicateGroup::new(&metadata, &[]).unwrap();
2221 let ranges = memtable
2222 .ranges(
2223 Some(&projection),
2224 RangesOptions::default().with_predicate(predicate_group),
2225 )
2226 .unwrap();
2227
2228 assert_eq!(1, ranges.ranges.len());
2229 let range = ranges.ranges.get(&0).unwrap();
2230
2231 assert!(range.is_record_batch());
2232 let record_batch_iter = range.build_record_batch_iter(None, None).unwrap();
2233
2234 let mut total_rows = 0;
2235 for batch_result in record_batch_iter {
2236 let batch = batch_result.unwrap();
2237 assert!(batch.num_rows() > 0);
2238 assert_eq!(5, batch.num_columns());
2239 total_rows += batch.num_rows();
2240 }
2241 assert_eq!(3, total_rows);
2242 }
2243
2244 #[test]
2245 fn test_bulk_memtable_unsupported_operations() {
2246 let metadata = metadata_for_test();
2247 let memtable = BulkMemtable::new(
2248 111,
2249 BulkMemtableConfig::default(),
2250 metadata.clone(),
2251 None,
2252 None,
2253 false,
2254 MergeMode::LastRow,
2255 );
2256
2257 let key_values = build_key_values_with_ts_seq_values(
2258 &metadata,
2259 "test".to_string(),
2260 1,
2261 vec![1000].into_iter(),
2262 vec![Some(1.0)].into_iter(),
2263 1,
2264 );
2265
2266 let err = memtable.write(&key_values).unwrap_err();
2267 assert!(err.to_string().contains("not supported"));
2268
2269 let kv = key_values.iter().next().unwrap();
2270 let err = memtable.write_one(kv).unwrap_err();
2271 assert!(err.to_string().contains("not supported"));
2272 }
2273
2274 #[test]
2275 fn test_bulk_memtable_freeze() {
2276 let metadata = metadata_for_test();
2277 let memtable = BulkMemtable::new(
2278 222,
2279 BulkMemtableConfig::default(),
2280 metadata.clone(),
2281 None,
2282 None,
2283 false,
2284 MergeMode::LastRow,
2285 );
2286
2287 let bulk_part = create_bulk_part_with_converter(
2288 "freeze_test",
2289 10,
2290 vec![10000],
2291 vec![Some(100.0)],
2292 1000,
2293 )
2294 .unwrap();
2295
2296 memtable.write_bulk(bulk_part).unwrap();
2297 memtable.freeze().unwrap();
2298
2299 let stats_after_freeze = memtable.stats();
2300 assert_eq!(1, stats_after_freeze.num_rows);
2301 }
2302
2303 #[test]
2304 fn test_bulk_memtable_fork() {
2305 let metadata = metadata_for_test();
2306 let original_memtable = BulkMemtable::new_with_row_group_size_and_encoding(
2307 333,
2308 BulkMemtableConfig::default(),
2309 metadata.clone(),
2310 None,
2311 None,
2312 false,
2313 MergeMode::LastRow,
2314 DEFAULT_ROW_GROUP_SIZE,
2315 FloatFieldEncoding::ByteStreamSplit,
2316 );
2317
2318 let bulk_part =
2319 create_bulk_part_with_converter("fork_test", 15, vec![15000], vec![Some(150.0)], 1500)
2320 .unwrap();
2321
2322 original_memtable.write_bulk(bulk_part).unwrap();
2323
2324 let forked_memtable = original_memtable.fork_inner(444, &metadata);
2325
2326 assert_eq!(forked_memtable.id(), 444);
2327 assert_eq!(
2328 FloatFieldEncoding::ByteStreamSplit,
2329 original_memtable.float_field_encoding
2330 );
2331 assert_eq!(
2332 original_memtable.float_field_encoding,
2333 forked_memtable.float_field_encoding
2334 );
2335 assert_eq!(
2336 original_memtable.float_field_encoding,
2337 forked_memtable
2338 .compactor
2339 .lock()
2340 .unwrap()
2341 .float_field_encoding
2342 );
2343 assert!(forked_memtable.is_empty());
2344 assert_eq!(0, forked_memtable.stats().num_rows);
2345
2346 assert!(!original_memtable.is_empty());
2347 assert_eq!(1, original_memtable.stats().num_rows);
2348 }
2349
2350 #[test]
2351 fn test_bulk_memtable_ranges_multiple_parts() {
2352 let metadata = metadata_for_test();
2353 let memtable = BulkMemtable::new(
2354 777,
2355 BulkMemtableConfig::default(),
2356 metadata.clone(),
2357 None,
2358 None,
2359 false,
2360 MergeMode::LastRow,
2361 );
2362 memtable.set_unordered_part_threshold(0);
2364
2365 let parts_data = vec![
2366 (
2367 "part1",
2368 1u32,
2369 vec![1000i64, 1100i64],
2370 vec![Some(10.0), Some(11.0)],
2371 100u64,
2372 ),
2373 (
2374 "part2",
2375 2u32,
2376 vec![2000i64, 2100i64],
2377 vec![Some(20.0), Some(21.0)],
2378 200u64,
2379 ),
2380 ("part3", 3u32, vec![3000i64], vec![Some(30.0)], 300u64),
2381 ];
2382
2383 for (k0, k1, timestamps, values, seq) in parts_data {
2384 let part = create_bulk_part_with_converter(k0, k1, timestamps, values, seq).unwrap();
2385 memtable.write_bulk(part).unwrap();
2386 }
2387
2388 let predicate_group = PredicateGroup::new(&metadata, &[]).unwrap();
2389 let ranges = memtable
2390 .ranges(
2391 None,
2392 RangesOptions::default().with_predicate(predicate_group),
2393 )
2394 .unwrap();
2395
2396 assert_eq!(3, ranges.ranges.len());
2397 let total_rows: usize = ranges.ranges.values().map(|r| r.stats().num_rows()).sum();
2398 assert_eq!(5, total_rows);
2399 assert_eq!(3, ranges.ranges.len());
2400
2401 for (range_id, range) in ranges.ranges.iter() {
2402 assert!(*range_id < 3);
2403 assert!(range.num_rows() > 0);
2404 assert!(range.is_record_batch());
2405 }
2406 }
2407
2408 #[test]
2409 fn test_bulk_memtable_ranges_with_sequence_filter() {
2410 let metadata = metadata_for_test();
2411 let memtable = BulkMemtable::new(
2412 888,
2413 BulkMemtableConfig::default(),
2414 metadata.clone(),
2415 None,
2416 None,
2417 false,
2418 MergeMode::LastRow,
2419 );
2420
2421 let part = create_bulk_part_with_converter(
2422 "seq_test",
2423 1,
2424 vec![1000, 2000, 3000],
2425 vec![Some(10.0), Some(20.0), Some(30.0)],
2426 500,
2427 )
2428 .unwrap();
2429
2430 memtable.write_bulk(part).unwrap();
2431
2432 let predicate_group = PredicateGroup::new(&metadata, &[]).unwrap();
2433 let sequence_filter = Some(SequenceRange::LtEq { max: 400 }); let ranges = memtable
2435 .ranges(
2436 None,
2437 RangesOptions::default()
2438 .with_predicate(predicate_group)
2439 .with_sequence(sequence_filter),
2440 )
2441 .unwrap();
2442
2443 assert_eq!(1, ranges.ranges.len());
2444 let range = ranges.ranges.get(&0).unwrap();
2445
2446 let mut record_batch_iter = range.build_record_batch_iter(None, None).unwrap();
2447 assert!(record_batch_iter.next().is_none());
2448 }
2449
2450 #[test]
2451 fn test_bulk_memtable_ranges_with_encoded_parts() {
2452 let metadata = metadata_for_test();
2453 let config = BulkMemtableConfig {
2454 merge_threshold: 8,
2455 ..Default::default()
2456 };
2457 let memtable = BulkMemtable::new(
2458 999,
2459 config,
2460 metadata.clone(),
2461 None,
2462 None,
2463 false,
2464 MergeMode::LastRow,
2465 );
2466 memtable.set_unordered_part_threshold(0);
2468
2469 for i in 0..10 {
2471 let part = create_bulk_part_with_converter(
2472 &format!("key_{}", i),
2473 i,
2474 vec![1000 + i as i64 * 100],
2475 vec![Some(i as f64 * 10.0)],
2476 100 + i as u64,
2477 )
2478 .unwrap();
2479 memtable.write_bulk(part).unwrap();
2480 }
2481
2482 memtable.compact(false).unwrap();
2483
2484 let predicate_group = PredicateGroup::new(&metadata, &[]).unwrap();
2485 let ranges = memtable
2486 .ranges(
2487 None,
2488 RangesOptions::default().with_predicate(predicate_group),
2489 )
2490 .unwrap();
2491
2492 assert_eq!(3, ranges.ranges.len());
2494 let total_rows: usize = ranges.ranges.values().map(|r| r.stats().num_rows()).sum();
2495 assert_eq!(10, total_rows);
2496
2497 for range in ranges.ranges.values() {
2498 assert!(range.num_rows() > 0);
2499 assert!(range.is_record_batch());
2500
2501 let record_batch_iter = range.build_record_batch_iter(None, None).unwrap();
2502 let mut total_rows = 0;
2503 for batch_result in record_batch_iter {
2504 let batch = batch_result.unwrap();
2505 total_rows += batch.num_rows();
2506 assert!(batch.num_rows() > 0);
2507 }
2508 assert_eq!(total_rows, range.num_rows());
2509 }
2510 }
2511
2512 #[test]
2513 fn test_bulk_memtable_unordered_part() {
2514 let metadata = metadata_for_test();
2515 let memtable = BulkMemtable::new(
2516 1001,
2517 BulkMemtableConfig::default(),
2518 metadata.clone(),
2519 None,
2520 None,
2521 false,
2522 MergeMode::LastRow,
2523 );
2524
2525 memtable.set_unordered_part_threshold(5);
2528 memtable.set_unordered_part_compact_threshold(10);
2530
2531 for i in 0..3 {
2533 let part = create_bulk_part_with_converter(
2534 &format!("key_{}", i),
2535 i,
2536 vec![1000 + i as i64 * 100, 1100 + i as i64 * 100],
2537 vec![Some(i as f64 * 10.0), Some(i as f64 * 10.0 + 1.0)],
2538 100 + i as u64,
2539 )
2540 .unwrap();
2541 assert_eq!(2, part.num_rows());
2542 memtable.write_bulk(part).unwrap();
2543 }
2544
2545 let stats = memtable.stats();
2547 assert_eq!(6, stats.num_rows);
2548
2549 for i in 3..5 {
2552 let part = create_bulk_part_with_converter(
2553 &format!("key_{}", i),
2554 i,
2555 vec![1000 + i as i64 * 100, 1100 + i as i64 * 100],
2556 vec![Some(i as f64 * 10.0), Some(i as f64 * 10.0 + 1.0)],
2557 100 + i as u64,
2558 )
2559 .unwrap();
2560 memtable.write_bulk(part).unwrap();
2561 }
2562
2563 let stats = memtable.stats();
2565 assert_eq!(10, stats.num_rows);
2566
2567 let predicate_group = PredicateGroup::new(&metadata, &[]).unwrap();
2569 let ranges = memtable
2570 .ranges(
2571 None,
2572 RangesOptions::default().with_predicate(predicate_group),
2573 )
2574 .unwrap();
2575
2576 assert!(!ranges.ranges.is_empty());
2578 let total_rows: usize = ranges.ranges.values().map(|r| r.stats().num_rows()).sum();
2579 assert_eq!(10, total_rows);
2580
2581 let mut total_rows_read = 0;
2583 for range in ranges.ranges.values() {
2584 assert!(range.is_record_batch());
2585 let record_batch_iter = range.build_record_batch_iter(None, None).unwrap();
2586
2587 for batch_result in record_batch_iter {
2588 let batch = batch_result.unwrap();
2589 total_rows_read += batch.num_rows();
2590 }
2591 }
2592 assert_eq!(10, total_rows_read);
2593 }
2594
2595 #[test]
2596 fn test_bulk_memtable_unordered_part_mixed_sizes() {
2597 let metadata = metadata_for_test();
2598 let memtable = BulkMemtable::new(
2599 1002,
2600 BulkMemtableConfig::default(),
2601 metadata.clone(),
2602 None,
2603 None,
2604 false,
2605 MergeMode::LastRow,
2606 );
2607
2608 memtable.set_unordered_part_threshold(4);
2610 memtable.set_unordered_part_compact_threshold(8);
2611
2612 for i in 0..2 {
2614 let part = create_bulk_part_with_converter(
2615 &format!("small_{}", i),
2616 i,
2617 vec![1000 + i as i64, 2000 + i as i64, 3000 + i as i64],
2618 vec![Some(i as f64), Some(i as f64 + 1.0), Some(i as f64 + 2.0)],
2619 10 + i as u64,
2620 )
2621 .unwrap();
2622 assert_eq!(3, part.num_rows());
2623 memtable.write_bulk(part).unwrap();
2624 }
2625
2626 let large_part = create_bulk_part_with_converter(
2628 "large_key",
2629 100,
2630 vec![5000, 6000, 7000, 8000, 9000],
2631 vec![
2632 Some(100.0),
2633 Some(101.0),
2634 Some(102.0),
2635 Some(103.0),
2636 Some(104.0),
2637 ],
2638 50,
2639 )
2640 .unwrap();
2641 assert_eq!(5, large_part.num_rows());
2642 memtable.write_bulk(large_part).unwrap();
2643
2644 let part = create_bulk_part_with_converter(
2646 "small_2",
2647 2,
2648 vec![4000, 4100],
2649 vec![Some(20.0), Some(21.0)],
2650 30,
2651 )
2652 .unwrap();
2653 memtable.write_bulk(part).unwrap();
2654
2655 let stats = memtable.stats();
2656 assert_eq!(13, stats.num_rows); let predicate_group = PredicateGroup::new(&metadata, &[]).unwrap();
2660 let ranges = memtable
2661 .ranges(
2662 None,
2663 RangesOptions::default().with_predicate(predicate_group),
2664 )
2665 .unwrap();
2666
2667 let total_rows: usize = ranges.ranges.values().map(|r| r.stats().num_rows()).sum();
2668 assert_eq!(13, total_rows);
2669
2670 let mut total_rows_read = 0;
2671 for range in ranges.ranges.values() {
2672 let record_batch_iter = range.build_record_batch_iter(None, None).unwrap();
2673 for batch_result in record_batch_iter {
2674 let batch = batch_result.unwrap();
2675 total_rows_read += batch.num_rows();
2676 }
2677 }
2678 assert_eq!(13, total_rows_read);
2679 }
2680
2681 #[test]
2682 fn test_bulk_memtable_unordered_part_byte_threshold() {
2683 let metadata = metadata_for_test();
2684 let first =
2685 create_bulk_part_with_converter("first", 1, vec![1000], vec![Some(1.0)], 1).unwrap();
2686 let second =
2687 create_bulk_part_with_converter("second", 2, vec![2000], vec![Some(2.0)], 2).unwrap();
2688 let threshold = first.estimated_size().max(second.estimated_size());
2689 let memtable = BulkMemtable::new(
2690 1004,
2691 BulkMemtableConfig {
2692 encode_bytes_threshold: threshold,
2693 ..Default::default()
2694 },
2695 metadata.clone(),
2696 None,
2697 None,
2698 false,
2699 MergeMode::LastRow,
2700 );
2701 memtable.set_unordered_part_compact_threshold(usize::MAX);
2702
2703 memtable.write_bulk(first).unwrap();
2705 assert_eq!(1, memtable.parts.read().unwrap().unordered_part.num_parts());
2706
2707 memtable.write_bulk(second).unwrap();
2709 let parts = memtable.parts.read().unwrap();
2710 assert!(parts.unordered_part.is_empty());
2711 assert_eq!(1, parts.parts.len());
2712 drop(parts);
2713
2714 let oversized =
2715 create_bulk_part_with_converter("oversized", 3, vec![3000], vec![Some(3.0)], 3)
2716 .unwrap();
2717 let oversized_memtable = BulkMemtable::new(
2718 1005,
2719 BulkMemtableConfig {
2720 encode_bytes_threshold: oversized.estimated_size() - 1,
2721 ..Default::default()
2722 },
2723 metadata,
2724 None,
2725 None,
2726 false,
2727 MergeMode::LastRow,
2728 );
2729 oversized_memtable.write_bulk(oversized).unwrap();
2730 let parts = oversized_memtable.parts.read().unwrap();
2731 assert!(parts.unordered_part.is_empty());
2732 assert_eq!(1, parts.parts.len());
2733 }
2734
2735 #[test]
2736 fn test_bulk_memtable_unordered_part_with_ranges() {
2737 let metadata = metadata_for_test();
2738 let memtable = BulkMemtable::new(
2739 1003,
2740 BulkMemtableConfig::default(),
2741 metadata.clone(),
2742 None,
2743 None,
2744 false,
2745 MergeMode::LastRow,
2746 );
2747
2748 memtable.set_unordered_part_threshold(3);
2750 memtable.set_unordered_part_compact_threshold(100); for i in 0..3 {
2754 let part = create_bulk_part_with_converter(
2755 &format!("key_{}", i),
2756 i,
2757 vec![1000 + i as i64 * 100],
2758 vec![Some(i as f64 * 10.0)],
2759 100 + i as u64,
2760 )
2761 .unwrap();
2762 assert_eq!(1, part.num_rows());
2763 memtable.write_bulk(part).unwrap();
2764 }
2765
2766 let stats = memtable.stats();
2767 assert_eq!(3, stats.num_rows);
2768
2769 let predicate_group = PredicateGroup::new(&metadata, &[]).unwrap();
2771 let ranges = memtable
2772 .ranges(
2773 None,
2774 RangesOptions::default().with_predicate(predicate_group),
2775 )
2776 .unwrap();
2777
2778 assert_eq!(1, ranges.ranges.len());
2780 let total_rows: usize = ranges.ranges.values().map(|r| r.stats().num_rows()).sum();
2781 assert_eq!(3, total_rows);
2782
2783 let range = ranges.ranges.get(&0).unwrap();
2785 let record_batch_iter = range.build_record_batch_iter(None, None).unwrap();
2786
2787 let mut total_rows = 0;
2788 for batch_result in record_batch_iter {
2789 let batch = batch_result.unwrap();
2790 total_rows += batch.num_rows();
2791 assert!(batch.num_rows() > 0);
2793 }
2794 assert_eq!(3, total_rows);
2795 }
2796
2797 fn create_bulk_part_wrapper(part: BulkPart) -> BulkPartWrapper {
2799 BulkPartWrapper {
2800 part: PartToMerge::Bulk {
2801 part,
2802 file_id: FileId::random(),
2803 },
2804 merging: false,
2805 }
2806 }
2807
2808 #[test]
2809 fn test_may_merge_parts_below_threshold() {
2810 let mut bulk_parts = BulkParts::default();
2811
2812 for i in 0..DEFAULT_MERGE_THRESHOLD - 1 {
2814 let part = create_bulk_part_with_converter(
2815 &format!("key_{}", i),
2816 i as u32,
2817 vec![1000 + i as i64 * 100],
2818 vec![Some(i as f64 * 10.0)],
2819 100 + i as u64,
2820 )
2821 .unwrap();
2822 bulk_parts.parts.push(create_bulk_part_wrapper(part));
2823 if i == 0 {
2824 assert!(!bulk_parts.may_merge_parts(0));
2825 assert!(!bulk_parts.may_merge_parts(1));
2826 }
2827 }
2828
2829 assert!(!bulk_parts.may_merge_parts(DEFAULT_MERGE_THRESHOLD));
2831 }
2832
2833 #[test]
2834 fn test_may_merge_parts_at_threshold() {
2835 let mut bulk_parts = BulkParts::default();
2836 let merge_threshold = 8;
2837
2838 for i in 0..merge_threshold {
2840 let part = create_bulk_part_with_converter(
2841 &format!("key_{}", i),
2842 i as u32,
2843 vec![1000 + i as i64 * 100],
2844 vec![Some(i as f64 * 10.0)],
2845 100 + i as u64,
2846 )
2847 .unwrap();
2848 bulk_parts.parts.push(create_bulk_part_wrapper(part));
2849 }
2850
2851 assert!(bulk_parts.may_merge_parts(merge_threshold));
2853 }
2854
2855 #[test]
2856 fn test_raw_parts_do_not_use_encoded_merge_limit() {
2857 let mut bulk_parts = BulkParts::default();
2858 let merge_threshold = 4;
2859 for i in 0..merge_threshold {
2860 let part = create_bulk_part_with_converter(
2861 &format!("key_{}", i),
2862 i as u32,
2863 vec![1000 + i as i64],
2864 vec![Some(i as f64)],
2865 100 + i as u64,
2866 )
2867 .unwrap();
2868 bulk_parts.parts.push(create_bulk_part_wrapper(part));
2869 }
2870
2871 let max_size = bulk_parts.parts[0].part.estimated_size();
2872 assert!(bulk_parts.may_merge_parts(merge_threshold));
2873 assert_eq!(
2874 1,
2875 bulk_parts
2876 .collect_parts_to_merge(merge_threshold, 1, max_size)
2877 .groups
2878 .len()
2879 );
2880 }
2881
2882 #[test]
2883 fn test_may_merge_parts_with_merging_flag() {
2884 let mut bulk_parts = BulkParts::default();
2885 let merge_threshold = 8;
2886
2887 for i in 0..10 {
2889 let part = create_bulk_part_with_converter(
2890 &format!("key_{}", i),
2891 i as u32,
2892 vec![1000 + i as i64 * 100],
2893 vec![Some(i as f64 * 10.0)],
2894 100 + i as u64,
2895 )
2896 .unwrap();
2897 bulk_parts.parts.push(create_bulk_part_wrapper(part));
2898 }
2899
2900 assert!(bulk_parts.may_merge_parts(merge_threshold));
2902
2903 for wrapper in bulk_parts.parts.iter_mut().take(3) {
2905 wrapper.merging = true;
2906 }
2907
2908 assert!(!bulk_parts.may_merge_parts(merge_threshold));
2910 }
2911
2912 #[test]
2913 fn test_compact_task_guard() {
2914 let compacting = Arc::new(AtomicBool::new(false));
2915 let guard = CompactTaskGuard::try_new(compacting.clone()).unwrap();
2916 assert!(compacting.load(Ordering::Relaxed));
2917 assert!(CompactTaskGuard::try_new(compacting.clone()).is_none());
2918 drop(guard);
2919 assert!(!compacting.load(Ordering::Relaxed));
2920 assert!(CompactTaskGuard::try_new(compacting.clone()).is_some());
2921 assert!(!compacting.load(Ordering::Relaxed));
2922 }
2923
2924 #[test]
2925 fn test_compact_scheduling_disabled() {
2926 let memtable = BulkMemtable::new(
2927 0,
2928 BulkMemtableConfig {
2929 merge_threshold: 2,
2930 max_merge_groups: 0,
2931 ..Default::default()
2932 },
2933 metadata_for_test(),
2934 None,
2935 Some(Arc::new(CompactDispatcher::new(0))),
2936 false,
2937 MergeMode::LastRow,
2938 );
2939 let part =
2940 create_bulk_part_with_converter("key", 0, vec![1000], vec![Some(1.0)], 0).unwrap();
2941 memtable.parts.write().unwrap().parts = vec![
2942 create_bulk_part_wrapper(part.clone()),
2943 create_bulk_part_wrapper(part),
2944 ];
2945 memtable.schedule_compact();
2946 assert!(!memtable.compacting.load(Ordering::Relaxed));
2947 }
2948
2949 #[test]
2950 fn test_collect_parts_to_merge_sequence_components() {
2951 let make_part = |min, max| {
2952 let mut part =
2953 create_bulk_part_with_converter("key", 0, vec![1000], vec![Some(1.0)], max)
2954 .unwrap();
2955 part.min_sequence = min;
2956 part
2957 };
2958 let metadata = metadata_for_test();
2959 let encoder = BulkPartEncoder::new(metadata.clone(), DEFAULT_ROW_GROUP_SIZE).unwrap();
2960 let mut parts = BulkParts {
2962 parts: vec![
2963 create_bulk_part_wrapper(make_part(70, 70)),
2964 create_bulk_part_wrapper(make_part(10, 30)),
2965 BulkPartWrapper {
2966 part: PartToMerge::Multi {
2967 part: MultiBulkPart::from_bulk_part(make_part(35, 50), &metadata),
2968 file_id: FileId::random(),
2969 },
2970 merging: false,
2971 },
2972 create_bulk_part_wrapper(make_part(60, 60)),
2973 BulkPartWrapper {
2974 part: PartToMerge::Encoded {
2975 part: encoder.encode_part(&make_part(20, 40)).unwrap().unwrap(),
2976 file_id: FileId::random(),
2977 },
2978 merging: false,
2979 },
2980 ],
2981 ..Default::default()
2982 };
2983 assert!(parts.may_merge_parts(parts.parts.len()));
2986 assert_eq!(
2987 vec![vec![1, 4, 2], vec![3, 0]],
2988 parts.pick_merge_groups(2, 2, 1)
2989 );
2990 parts.unordered_part.push(make_part(0, 100));
2992 assert!(parts.may_merge_parts(2));
2993 assert!(parts.pick_merge_groups(2, 2, 1).is_empty());
2994 parts.unordered_part.clear();
2995
2996 parts.unordered_part.push(make_part(50, 55));
2998 assert_eq!(vec![vec![3, 0]], parts.pick_merge_groups(2, 2, 1));
2999 parts.unordered_part.clear();
3000
3001 parts.unordered_part.push(make_part(65, 65));
3003 assert_eq!(vec![vec![1, 4, 2]], parts.pick_merge_groups(2, 2, 1));
3004 parts.unordered_part.clear();
3005
3006 parts.parts[4].merging = true;
3007 let selected = parts.collect_parts_to_merge(2, 2, 1);
3008 assert_eq!(1, selected.groups.len());
3009 assert_eq!(
3010 vec![60, 70],
3011 selected.groups[0]
3012 .iter()
3013 .map(PartToMerge::max_sequence)
3014 .collect::<Vec<_>>()
3015 );
3016 assert!(!parts.parts[1].merging);
3017 assert!(!parts.parts[2].merging);
3018 }
3019
3020 #[test]
3021 fn test_encoded_merge_candidate_size_limit() {
3022 assert!(BulkParts::is_merge_candidate_by_size(false, usize::MAX, 8));
3023 assert!(BulkParts::is_merge_candidate_by_size(true, 8, 8));
3024 assert!(!BulkParts::is_merge_candidate_by_size(true, 9, 8));
3025 }
3026
3027 #[test]
3028 fn test_bulk_part_encoder_preserves_byte_stream_split() -> WhateverResult<()> {
3029 let metadata = metadata_for_test();
3030 let bulk_part = create_bulk_part_with_converter(
3031 "byte_stream_split",
3032 0,
3033 vec![1000, 2000],
3034 vec![Some(10.0), Some(20.0)],
3035 100,
3036 )?;
3037 let encoder = BulkPartEncoder::new_with_float_field_encoding(
3038 metadata,
3039 DEFAULT_ROW_GROUP_SIZE,
3040 FloatFieldEncoding::ByteStreamSplit,
3041 )?;
3042 let encoded_part = encoder.encode_part(&bulk_part)?.unwrap();
3043 let field_column = encoded_part
3044 .metadata()
3045 .parquet_metadata
3046 .row_groups()
3047 .iter()
3048 .flat_map(|row_group| row_group.columns())
3049 .find(|column| {
3050 column.column_path().string() == "v1"
3051 && column.column_type() == PhysicalType::DOUBLE
3052 })
3053 .expect("Float64 field should be present in encoded bulk part");
3054
3055 assert!(
3056 field_column
3057 .encodings()
3058 .any(|encoding| encoding == Encoding::BYTE_STREAM_SPLIT)
3059 );
3060 Ok(())
3061 }
3062
3063 #[test]
3064 fn test_encoded_part_batch_size_uses_largest_uncompressed_row_group() {
3065 const NUM_ROWS: usize = 14;
3066 const ROW_GROUP_SIZE: usize = 4;
3067
3068 let metadata = metadata_for_test();
3069 let timestamps = (0..NUM_ROWS as i64).collect::<Vec<_>>();
3070 let field_values = (0..NUM_ROWS)
3071 .map(|value| Some(value as f64))
3072 .collect::<Vec<_>>();
3073 let bulk_part =
3074 create_bulk_part_with_converter("key", 0, timestamps, field_values, 0).unwrap();
3075 let encoder = BulkPartEncoder::new(metadata, ROW_GROUP_SIZE).unwrap();
3076 let encoded_part = encoder.encode_part(&bulk_part).unwrap().unwrap();
3077 let max_row_group = encoded_part
3078 .metadata()
3079 .parquet_metadata
3080 .row_groups()
3081 .iter()
3082 .max_by_key(|row_group| {
3083 row_group
3084 .columns()
3085 .iter()
3086 .map(|column| column.uncompressed_size() as u64)
3087 .sum::<u64>()
3088 })
3089 .unwrap();
3090 let max_uncompressed_size = max_row_group
3091 .columns()
3092 .iter()
3093 .map(|column| column.uncompressed_size() as u64)
3094 .sum();
3095 let expected = (max_row_group.num_rows() as u64, max_uncompressed_size);
3096 let part = PartToMerge::Encoded {
3097 part: encoded_part,
3098 file_id: FileId::random(),
3099 };
3100
3101 assert_eq!(Some(expected), part.batch_size_statistic());
3102 }
3103
3104 #[test]
3105 fn test_bulk_memtable_ranges_with_multi_bulk_part() {
3106 let metadata = metadata_for_test();
3107 let merge_threshold = 8;
3108 let config = BulkMemtableConfig {
3109 merge_threshold,
3110 ..Default::default()
3111 };
3112 let memtable = BulkMemtable::new(
3113 2005,
3114 config,
3115 metadata.clone(),
3116 None,
3117 None,
3118 false,
3119 MergeMode::LastRow,
3120 );
3121 memtable.set_unordered_part_threshold(0);
3123
3124 for i in 0..merge_threshold {
3128 let part = create_bulk_part_with_converter(
3129 &format!("key_{}", i),
3130 i as u32,
3131 vec![1000 + i as i64 * 100, 2000 + i as i64 * 100],
3132 vec![Some(i as f64 * 10.0), Some(i as f64 * 10.0 + 1.0)],
3133 100 + i as u64,
3134 )
3135 .unwrap();
3136 memtable.write_bulk(part).unwrap();
3137 }
3138
3139 memtable.compact(false).unwrap();
3141
3142 let predicate_group = PredicateGroup::new(&metadata, &[]).unwrap();
3144 let ranges = memtable
3145 .ranges(
3146 None,
3147 RangesOptions::default().with_predicate(predicate_group),
3148 )
3149 .unwrap();
3150
3151 assert_eq!(1, ranges.ranges.len());
3152 let expected_rows = merge_threshold * 2; let total_rows: usize = ranges.ranges.values().map(|r| r.stats().num_rows()).sum();
3154 assert_eq!(expected_rows, total_rows);
3155
3156 let mut total_rows_read = 0;
3158 for range in ranges.ranges.values() {
3159 assert!(range.is_record_batch());
3160 let record_batch_iter = range.build_record_batch_iter(None, None).unwrap();
3161
3162 for batch_result in record_batch_iter {
3163 let batch = batch_result.unwrap();
3164 total_rows_read += batch.num_rows();
3165 }
3166 }
3167 assert_eq!(expected_rows, total_rows_read);
3168 }
3169
3170 #[test]
3171 fn test_multi_bulk_range_iter_builder_all_pruned() {
3172 let metadata = metadata_for_test();
3173 let merge_threshold = 8;
3174 let config = BulkMemtableConfig {
3175 merge_threshold,
3176 ..Default::default()
3177 };
3178 let memtable = BulkMemtable::new(
3179 2006,
3180 config,
3181 metadata.clone(),
3182 None,
3183 None,
3184 false,
3185 MergeMode::LastRow,
3186 );
3187 memtable.set_unordered_part_threshold(0);
3188
3189 for i in 0..merge_threshold {
3191 let part = create_bulk_part_with_converter(
3192 &format!("key_{}", i),
3193 i as u32,
3194 vec![1000 + i as i64 * 100, 2000 + i as i64 * 100],
3195 vec![Some(i as f64 * 10.0), Some(i as f64 * 10.0 + 1.0)],
3196 100 + i as u64,
3197 )
3198 .unwrap();
3199 memtable.write_bulk(part).unwrap();
3200 }
3201 memtable.compact(false).unwrap();
3202
3203 let filter = datafusion_expr::col("k0").eq(datafusion_expr::lit("nonexistent"));
3205 let predicate_group = PredicateGroup::new(&metadata, &[filter]).unwrap();
3206 let ranges = memtable
3207 .ranges(
3208 None,
3209 RangesOptions::default().with_predicate(predicate_group),
3210 )
3211 .unwrap();
3212
3213 for range in ranges.ranges.values() {
3216 assert!(range.is_record_batch());
3217 let record_batch_iter = range.build_record_batch_iter(None, None).unwrap();
3218 let total_rows: usize = record_batch_iter.map(|r| r.unwrap().num_rows()).sum();
3219 assert_eq!(0, total_rows);
3220 }
3221 }
3222}