Skip to main content

mito2/
memtable.rs

1// Copyright 2023 Greptime Team
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Memtables are write buffers for regions.
16
17use std::collections::{BTreeMap, HashMap};
18use std::fmt;
19use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
20use std::sync::{Arc, Mutex};
21use std::time::Duration;
22
23pub use bulk::part::EncodedBulkPart;
24use bytes::Bytes;
25use common_time::Timestamp;
26use datatypes::arrow::datatypes::SchemaRef;
27use datatypes::arrow::record_batch::RecordBatch;
28use mito_codec::key_values::KeyValue;
29pub use mito_codec::key_values::KeyValues;
30use mito_codec::row_converter::{PrimaryKeyCodec, build_primary_key_codec};
31use snafu::ensure;
32use store_api::codec::PrimaryKeyEncoding;
33use store_api::metadata::{RegionMetadata, RegionMetadataRef};
34use store_api::storage::{ColumnId, RegionId, SequenceNumber, SequenceRange};
35
36use crate::config::MitoConfig;
37use crate::error::{InvalidRegionOptionsSnafu, Result, UnsupportedOperationSnafu};
38use crate::flush::WriteBufferManagerRef;
39use crate::memtable::bulk::{BulkMemtableBuilder, BulkMemtableConfig, CompactDispatcher};
40use crate::memtable::time_series::TimeSeriesMemtableBuilder;
41use crate::metrics::WRITE_BUFFER_BYTES;
42use crate::read::Batch;
43use crate::read::batch_adapter::BatchToRecordBatchAdapter;
44use crate::read::prune::PruneTimeIterator;
45use crate::read::scan_region::PredicateGroup;
46use crate::region::options::{MemtableOptions, MergeMode, RegionOptions};
47use crate::sst::FormatType;
48use crate::sst::file::FileTimeRange;
49use crate::sst::parquet::SstInfo;
50use crate::sst::parquet::file_range::PreFilterMode;
51
52mod builder;
53pub mod bulk;
54pub mod simple_bulk_memtable;
55mod stats;
56pub mod time_partition;
57pub mod time_series;
58pub(crate) mod version;
59
60pub use bulk::part::{
61    BulkPart, BulkPartEncoder, BulkPartMeta, UnorderedPart, record_batch_estimated_size,
62    sort_primary_key_record_batch,
63};
64#[cfg(any(test, feature = "test"))]
65pub use time_partition::filter_record_batch;
66
67/// Groups sequence intervals into ordered, disjoint components. A component must
68/// not be split during a partial merge: an excluded interval could contain an
69/// intermediate row version. Bounds may conservatively include removed rows.
70pub(crate) fn group_by_sequence<T>(
71    mut parts: Vec<T>,
72    bounds: impl Fn(&T) -> (SequenceNumber, SequenceNumber),
73) -> Vec<Vec<T>> {
74    parts.sort_by_key(&bounds);
75    let mut groups: Vec<Vec<T>> = Vec::new();
76    let mut group_max = 0;
77    for part in parts {
78        let (min, max) = bounds(&part);
79        match groups.last_mut() {
80            Some(group) if min <= group_max => {
81                group.push(part);
82                group_max = group_max.max(max);
83            }
84            _ => {
85                groups.push(vec![part]);
86                group_max = max;
87            }
88        }
89    }
90    groups
91}
92
93/// Id for memtables.
94///
95/// Should be unique under the same region.
96pub type MemtableId = u32;
97
98/// Options for querying ranges from a memtable.
99#[derive(Clone)]
100pub struct RangesOptions {
101    /// Whether the ranges are being queried for flush.
102    pub for_flush: bool,
103    /// Mode to pre-filter columns in ranges.
104    pub pre_filter_mode: PreFilterMode,
105    /// Predicate to filter the data.
106    pub predicate: PredicateGroup,
107    /// Sequence range to filter the data.
108    pub sequence: Option<SequenceRange>,
109    /// Maximum number of rows readers should produce in one batch.
110    pub batch_size: usize,
111}
112
113impl Default for RangesOptions {
114    fn default() -> Self {
115        Self {
116            for_flush: false,
117            pre_filter_mode: PreFilterMode::All,
118            predicate: PredicateGroup::default(),
119            sequence: None,
120            batch_size: crate::sst::parquet::DEFAULT_READ_BATCH_SIZE,
121        }
122    }
123}
124
125impl RangesOptions {
126    /// Creates a new [RangesOptions] for flushing.
127    pub fn for_flush() -> Self {
128        Self {
129            for_flush: true,
130            pre_filter_mode: PreFilterMode::All,
131            predicate: PredicateGroup::default(),
132            sequence: None,
133            batch_size: crate::sst::parquet::DEFAULT_READ_BATCH_SIZE,
134        }
135    }
136
137    /// Sets the pre-filter mode.
138    #[must_use]
139    pub fn with_pre_filter_mode(mut self, pre_filter_mode: PreFilterMode) -> Self {
140        self.pre_filter_mode = pre_filter_mode;
141        self
142    }
143
144    /// Sets the predicate.
145    #[must_use]
146    pub fn with_predicate(mut self, predicate: PredicateGroup) -> Self {
147        self.predicate = predicate;
148        self
149    }
150
151    /// Sets the sequence range.
152    #[must_use]
153    pub fn with_sequence(mut self, sequence: Option<SequenceRange>) -> Self {
154        self.sequence = sequence;
155        self
156    }
157
158    /// Sets the maximum number of rows readers should produce in one batch.
159    #[must_use]
160    pub fn with_batch_size(mut self, batch_size: usize) -> Self {
161        self.batch_size = batch_size.clamp(1, crate::sst::parquet::DEFAULT_READ_BATCH_SIZE);
162        self
163    }
164}
165
166#[derive(Debug, Default, Clone)]
167pub struct MemtableStats {
168    /// The estimated bytes allocated by this memtable from heap.
169    pub estimated_bytes: usize,
170    /// The inclusive time range that this memtable contains. It is None if
171    /// and only if the memtable is empty.
172    pub time_range: Option<(Timestamp, Timestamp)>,
173    /// Total rows in memtable
174    pub num_rows: usize,
175    /// Total number of ranges in the memtable.
176    pub num_ranges: usize,
177    /// The maximum sequence number in the memtable.
178    pub max_sequence: SequenceNumber,
179    /// Lower bound of the row sequences written to this memtable or range.
180    /// May remain below the actual minimum after deduplication. Zero is conservative
181    /// when a range does not track its minimum; the value is unused for empty memtables.
182    pub min_sequence: SequenceNumber,
183    /// Number of estimated timeseries in memtable.
184    pub series_count: usize,
185}
186
187impl MemtableStats {
188    /// Attaches the time range to the stats.
189    #[cfg(any(test, feature = "test"))]
190    pub fn with_time_range(mut self, time_range: Option<(Timestamp, Timestamp)>) -> Self {
191        self.time_range = time_range;
192        self
193    }
194
195    #[cfg(feature = "test")]
196    pub fn with_max_sequence(mut self, max_sequence: SequenceNumber) -> Self {
197        self.max_sequence = max_sequence;
198        self
199    }
200
201    /// Returns the estimated bytes allocated by this memtable.
202    pub fn bytes_allocated(&self) -> usize {
203        self.estimated_bytes
204    }
205
206    /// Returns the time range of the memtable.
207    pub fn time_range(&self) -> Option<(Timestamp, Timestamp)> {
208        self.time_range
209    }
210
211    /// Returns the num of total rows in memtable.
212    pub fn num_rows(&self) -> usize {
213        self.num_rows
214    }
215
216    /// Returns the number of ranges in the memtable.
217    pub fn num_ranges(&self) -> usize {
218        self.num_ranges
219    }
220
221    /// Returns the maximum sequence number in the memtable.
222    pub fn max_sequence(&self) -> SequenceNumber {
223        self.max_sequence
224    }
225
226    /// Series count in memtable.
227    pub fn series_count(&self) -> usize {
228        self.series_count
229    }
230}
231
232pub type BoxedBatchIterator = Box<dyn Iterator<Item = Result<Batch>> + Send>;
233
234pub type BoxedRecordBatchIterator = Box<dyn Iterator<Item = Result<RecordBatch>> + Send>;
235
236/// Ranges in a memtable.
237#[derive(Default)]
238pub struct MemtableRanges {
239    /// Range IDs and ranges.
240    pub ranges: BTreeMap<usize, MemtableRange>,
241}
242
243impl MemtableRanges {
244    /// Returns the total number of rows across all ranges.
245    pub fn num_rows(&self) -> usize {
246        self.ranges.values().map(|r| r.stats().num_rows()).sum()
247    }
248
249    /// Returns the total series count across all ranges.
250    pub fn series_count(&self) -> usize {
251        self.ranges.values().map(|r| r.stats().series_count()).sum()
252    }
253
254    /// Returns the maximum sequence number across all ranges.
255    pub fn max_sequence(&self) -> SequenceNumber {
256        self.ranges
257            .values()
258            .map(|r| r.stats().max_sequence())
259            .max()
260            .unwrap_or(0)
261    }
262}
263
264impl IterBuilder for MemtableRanges {
265    fn build(&self, _metrics: Option<MemScanMetrics>) -> Result<BoxedBatchIterator> {
266        ensure!(
267            self.ranges.len() == 1,
268            UnsupportedOperationSnafu {
269                err_msg: format!(
270                    "Building an iterator from MemtableRanges expects 1 range, but got {}",
271                    self.ranges.len()
272                ),
273            }
274        );
275
276        self.ranges.values().next().unwrap().build_iter()
277    }
278
279    fn is_record_batch(&self) -> bool {
280        self.ranges.values().all(|range| range.is_record_batch())
281    }
282}
283
284/// In memory write buffer.
285pub trait Memtable: Send + Sync + fmt::Debug {
286    /// Returns the id of this memtable.
287    fn id(&self) -> MemtableId;
288
289    /// Writes key values into the memtable.
290    fn write(&self, kvs: &KeyValues) -> Result<()>;
291
292    /// Writes one key value pair into the memtable.
293    fn write_one(&self, key_value: KeyValue) -> Result<()>;
294
295    /// Writes an encoded batch of into memtable.
296    fn write_bulk(&self, part: crate::memtable::bulk::part::BulkPart) -> Result<()>;
297
298    /// Returns the ranges in the memtable.
299    ///
300    /// The returned map contains the range id and the range after applying the predicate.
301    fn ranges(
302        &self,
303        projection: Option<&[ColumnId]>,
304        options: RangesOptions,
305    ) -> Result<MemtableRanges>;
306
307    /// Returns true if the memtable is empty.
308    fn is_empty(&self) -> bool;
309
310    /// Turns a mutable memtable into an immutable memtable.
311    fn freeze(&self) -> Result<()>;
312
313    /// Returns the [MemtableStats] info of Memtable.
314    fn stats(&self) -> MemtableStats;
315
316    /// Returns a conservative row-sequence lower bound without computing full statistics.
317    /// Returns zero when write statistics are unavailable. Callers must ignore
318    /// empty memtables when using this bound to constrain compaction.
319    fn min_sequence(&self) -> SequenceNumber;
320
321    /// Forks this (immutable) memtable and returns a new mutable memtable with specific memtable `id`.
322    ///
323    /// A region must freeze the memtable before invoking this method.
324    fn fork(&self, id: MemtableId, metadata: &RegionMetadataRef) -> MemtableRef;
325
326    /// Compacts the memtable.
327    ///
328    /// The `for_flush` is true when the flush job calls this method.
329    fn compact(&self, for_flush: bool) -> Result<()> {
330        let _ = for_flush;
331        Ok(())
332    }
333}
334
335pub type MemtableRef = Arc<dyn Memtable>;
336
337/// Builder to build a new [Memtable].
338pub trait MemtableBuilder: Send + Sync + fmt::Debug {
339    /// Builds a new memtable instance.
340    fn build(&self, id: MemtableId, metadata: &RegionMetadataRef) -> MemtableRef;
341
342    /// Returns true if the memtable supports bulk insert and benefits from it.
343    fn use_bulk_insert(&self, metadata: &RegionMetadataRef) -> bool {
344        let _metadata = metadata;
345        false
346    }
347}
348
349pub type MemtableBuilderRef = Arc<dyn MemtableBuilder>;
350
351/// Memtable memory allocation tracker.
352#[derive(Default)]
353pub struct AllocTracker {
354    write_buffer_manager: Option<WriteBufferManagerRef>,
355    /// Bytes allocated by the tracker.
356    bytes_allocated: AtomicUsize,
357    /// Whether allocating is done.
358    is_done_allocating: AtomicBool,
359}
360
361impl fmt::Debug for AllocTracker {
362    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
363        f.debug_struct("AllocTracker")
364            .field("bytes_allocated", &self.bytes_allocated)
365            .field("is_done_allocating", &self.is_done_allocating)
366            .finish()
367    }
368}
369
370impl AllocTracker {
371    /// Returns a new [AllocTracker].
372    pub fn new(write_buffer_manager: Option<WriteBufferManagerRef>) -> AllocTracker {
373        AllocTracker {
374            write_buffer_manager,
375            bytes_allocated: AtomicUsize::new(0),
376            is_done_allocating: AtomicBool::new(false),
377        }
378    }
379
380    /// Tracks `bytes` memory is allocated.
381    pub(crate) fn on_allocation(&self, bytes: usize) {
382        self.bytes_allocated.fetch_add(bytes, Ordering::Relaxed);
383        WRITE_BUFFER_BYTES.add(bytes as i64);
384        if let Some(write_buffer_manager) = &self.write_buffer_manager {
385            write_buffer_manager.reserve_mem(bytes);
386        }
387    }
388
389    /// Marks we have finished allocating memory so we can free it from
390    /// the write buffer's limit.
391    ///
392    /// The region MUST ensure that it calls this method inside the region writer's write lock.
393    pub(crate) fn done_allocating(&self) {
394        if let Some(write_buffer_manager) = &self.write_buffer_manager
395            && self
396                .is_done_allocating
397                .compare_exchange(false, true, Ordering::Relaxed, Ordering::Relaxed)
398                .is_ok()
399        {
400            write_buffer_manager.schedule_free_mem(self.bytes_allocated.load(Ordering::Relaxed));
401        }
402    }
403
404    /// Returns bytes allocated.
405    pub(crate) fn bytes_allocated(&self) -> usize {
406        self.bytes_allocated.load(Ordering::Relaxed)
407    }
408
409    /// Returns the write buffer manager.
410    pub(crate) fn write_buffer_manager(&self) -> Option<WriteBufferManagerRef> {
411        self.write_buffer_manager.clone()
412    }
413}
414
415impl Drop for AllocTracker {
416    fn drop(&mut self) {
417        if !self.is_done_allocating.load(Ordering::Relaxed) {
418            self.done_allocating();
419        }
420
421        let bytes_allocated = self.bytes_allocated.load(Ordering::Relaxed);
422        WRITE_BUFFER_BYTES.sub(bytes_allocated as i64);
423
424        // Memory tracked by this tracker is freed.
425        if let Some(write_buffer_manager) = &self.write_buffer_manager {
426            write_buffer_manager.free_mem(bytes_allocated);
427        }
428    }
429}
430
431/// Provider of memtable builders for regions.
432#[derive(Clone)]
433pub(crate) struct MemtableBuilderProvider {
434    write_buffer_manager: Option<WriteBufferManagerRef>,
435    config: Arc<MitoConfig>,
436    default_bulk_memtable_config: BulkMemtableConfig,
437    compact_dispatcher: Arc<CompactDispatcher>,
438}
439
440/// Ensures JSON2 columns use flat SST output after resolving the SST format.
441pub(crate) fn ensure_json2_uses_flat_format(
442    metadata: &RegionMetadata,
443    options: &RegionOptions,
444) -> Result<()> {
445    if metadata
446        .column_metadatas
447        .iter()
448        .any(|x| x.column_schema.data_type.is_json2())
449    {
450        // Bulk memtables alone do not ensure the SST writer supports JSON2.
451        ensure!(
452            options.sst_format == Some(FormatType::Flat),
453            InvalidRegionOptionsSnafu {
454                reason: "JSON2 columns require sst_format=flat",
455            }
456        );
457    }
458    Ok(())
459}
460
461impl MemtableBuilderProvider {
462    pub(crate) fn new(
463        write_buffer_manager: Option<WriteBufferManagerRef>,
464        config: Arc<MitoConfig>,
465    ) -> Self {
466        let compact_dispatcher =
467            Arc::new(CompactDispatcher::new(config.max_background_compactions));
468        let default_bulk_memtable_config = BulkMemtableConfig::default_for_write_buffer_size(
469            config.global_write_buffer_size.as_bytes() as usize,
470        );
471
472        Self {
473            write_buffer_manager,
474            config,
475            default_bulk_memtable_config,
476            compact_dispatcher,
477        }
478    }
479
480    /// Parses region options with this provider's default bulk memtable config.
481    pub(crate) fn parse_options(
482        &self,
483        region_id: RegionId,
484        options: &HashMap<String, String>,
485    ) -> Result<RegionOptions> {
486        RegionOptions::try_from_options_with_bulk_config(
487            region_id,
488            options,
489            &self.default_bulk_memtable_config,
490        )
491    }
492
493    pub(crate) fn builder_for_options(&self, options: &RegionOptions) -> MemtableBuilderRef {
494        let dedup = options.need_dedup();
495        let merge_mode = options.merge_mode();
496        let primary_key_encoding = options.primary_key_encoding();
497        let flat_format = options
498            .sst_format
499            .map(|format| format == FormatType::Flat)
500            .unwrap_or(self.config.default_flat_format);
501        if flat_format {
502            if options.memtable.is_some()
503                && !matches!(&options.memtable, Some(MemtableOptions::Bulk(_)))
504            {
505                common_telemetry::info!(
506                    "Overriding memtable config, use BulkMemtable under flat format"
507                );
508            }
509
510            return Arc::new(self.bulk_memtable_builder(dedup, merge_mode, options));
511        }
512
513        if primary_key_encoding == PrimaryKeyEncoding::Sparse {
514            if options.memtable.is_some()
515                && !matches!(&options.memtable, Some(MemtableOptions::Bulk(_)))
516            {
517                common_telemetry::info!(
518                    "Overriding memtable config, use BulkMemtable for sparse primary key encoding"
519                );
520            }
521            return Arc::new(self.bulk_memtable_builder(dedup, merge_mode, options));
522        }
523
524        // The format is not flat.
525        match &options.memtable {
526            Some(MemtableOptions::Bulk(config)) => Arc::new(
527                BulkMemtableBuilder::new(self.write_buffer_manager.clone(), !dedup, merge_mode)
528                    .with_config(config.clone())
529                    .with_row_group_size(options.row_group_size())
530                    .with_float_field_encoding(options.float_field_encoding)
531                    .with_compact_dispatcher(self.compact_dispatcher.clone()),
532            ),
533            Some(MemtableOptions::TimeSeries) => Arc::new(TimeSeriesMemtableBuilder::new(
534                self.write_buffer_manager.clone(),
535                dedup,
536                merge_mode,
537            )),
538            None => self.default_primary_key_memtable_builder(dedup, merge_mode),
539        }
540    }
541
542    fn bulk_memtable_builder(
543        &self,
544        dedup: bool,
545        merge_mode: MergeMode,
546        options: &RegionOptions,
547    ) -> BulkMemtableBuilder {
548        let mut builder = BulkMemtableBuilder::new(
549            self.write_buffer_manager.clone(),
550            !dedup, // append_mode: true if not dedup, false if dedup
551            merge_mode,
552        )
553        .with_config(self.default_bulk_memtable_config.clone())
554        .with_row_group_size(options.row_group_size())
555        .with_float_field_encoding(options.float_field_encoding)
556        .with_compact_dispatcher(self.compact_dispatcher.clone());
557
558        if let Some(MemtableOptions::Bulk(config)) = &options.memtable {
559            builder = builder.with_config(config.clone());
560        }
561
562        builder
563    }
564
565    fn default_primary_key_memtable_builder(
566        &self,
567        dedup: bool,
568        merge_mode: MergeMode,
569    ) -> MemtableBuilderRef {
570        Arc::new(TimeSeriesMemtableBuilder::new(
571            self.write_buffer_manager.clone(),
572            dedup,
573            merge_mode,
574        ))
575    }
576}
577
578/// Metrics for scanning a memtable.
579#[derive(Clone, Default)]
580pub struct MemScanMetrics(Arc<Mutex<MemScanMetricsData>>);
581
582impl MemScanMetrics {
583    /// Merges the metrics.
584    pub(crate) fn merge_inner(&self, inner: &MemScanMetricsData) {
585        let mut metrics = self.0.lock().unwrap();
586        metrics.total_series += inner.total_series;
587        metrics.num_rows += inner.num_rows;
588        metrics.num_batches += inner.num_batches;
589        metrics.scan_cost += inner.scan_cost;
590        metrics.prefilter_cost += inner.prefilter_cost;
591        metrics.prefilter_rows_filtered += inner.prefilter_rows_filtered;
592    }
593
594    /// Gets the metrics data.
595    pub(crate) fn data(&self) -> MemScanMetricsData {
596        self.0.lock().unwrap().clone()
597    }
598}
599
600#[derive(Clone, Default)]
601pub(crate) struct MemScanMetricsData {
602    /// Total series in the memtable.
603    pub(crate) total_series: usize,
604    /// Number of rows read.
605    pub(crate) num_rows: usize,
606    /// Number of batch read.
607    pub(crate) num_batches: usize,
608    /// Duration to scan the memtable.
609    pub(crate) scan_cost: Duration,
610    /// Duration of prefilter in memtable scan.
611    pub(crate) prefilter_cost: Duration,
612    /// Number of rows filtered by prefilter in memtable scan.
613    pub(crate) prefilter_rows_filtered: usize,
614}
615
616/// Encoded range in the memtable.
617pub struct EncodedRange {
618    /// Encoded file data.
619    pub data: Bytes,
620    /// Metadata of the encoded range.
621    pub sst_info: SstInfo,
622}
623
624/// Builder to build an iterator to read the range.
625/// The builder should know the projection and the predicate to build the iterator.
626pub trait IterBuilder: Send + Sync {
627    /// Returns the iterator to read the range.
628    fn build(&self, metrics: Option<MemScanMetrics>) -> Result<BoxedBatchIterator>;
629
630    /// Returns whether the iterator is a record batch iterator.
631    fn is_record_batch(&self) -> bool {
632        false
633    }
634
635    /// Returns the record batch iterator to read the range.
636    /// ## Note
637    /// Implementations should ensure the iterator yields data within given time range.
638    fn build_record_batch(
639        &self,
640        time_range: Option<(Timestamp, Timestamp)>,
641        metrics: Option<MemScanMetrics>,
642    ) -> Result<BoxedRecordBatchIterator> {
643        let _metrics = metrics;
644        let _ = time_range;
645        UnsupportedOperationSnafu {
646            err_msg: "Record batch iterator is not supported by this memtable",
647        }
648        .fail()
649    }
650
651    /// Returns a cheap schema hint for record batches yielded by this builder.
652    fn record_batch_schema_hint(&self) -> Option<SchemaRef> {
653        None
654    }
655
656    /// Returns the [EncodedRange] if the range is already encoded into SST.
657    fn encoded_range(&self) -> Option<EncodedRange> {
658        None
659    }
660}
661
662pub type BoxedIterBuilder = Box<dyn IterBuilder>;
663
664/// Computes the column IDs to read based on the projection.
665///
666/// If `projection` is `Some`, returns those column IDs. If `None`, returns all column IDs
667/// from the metadata.
668pub fn read_column_ids_from_projection(
669    metadata: &RegionMetadataRef,
670    projection: Option<&[ColumnId]>,
671) -> Vec<ColumnId> {
672    if let Some(projection) = projection {
673        projection.to_vec()
674    } else {
675        metadata
676            .column_metadatas
677            .iter()
678            .map(|c| c.column_id)
679            .collect()
680    }
681}
682
683/// Context to adapt batch iterators to record batch iterators for flat scan.
684pub struct BatchToRecordBatchContext {
685    metadata: RegionMetadataRef,
686    codec: Arc<dyn PrimaryKeyCodec>,
687    read_column_ids: Vec<ColumnId>,
688}
689
690impl BatchToRecordBatchContext {
691    /// Creates a new context for adapting batch iterators.
692    pub fn new(metadata: RegionMetadataRef, mut read_column_ids: Vec<ColumnId>) -> Self {
693        if read_column_ids.is_empty() {
694            read_column_ids.push(metadata.time_index_column().column_id);
695        }
696
697        let codec = build_primary_key_codec(&metadata);
698        Self {
699            metadata,
700            codec,
701            read_column_ids,
702        }
703    }
704
705    fn adapt_iter(&self, iter: BoxedBatchIterator) -> BoxedRecordBatchIterator {
706        Box::new(BatchToRecordBatchAdapter::new(
707            iter,
708            self.metadata.clone(),
709            self.codec.clone(),
710            &self.read_column_ids,
711        ))
712    }
713}
714
715/// Context shared by ranges of the same memtable.
716pub struct MemtableRangeContext {
717    /// Id of the memtable.
718    id: MemtableId,
719    /// Iterator builder.
720    builder: BoxedIterBuilder,
721    /// All filters.
722    predicate: PredicateGroup,
723    /// Optional context to adapt batch iterators for flat scans.
724    batch_to_record_batch: Option<Arc<BatchToRecordBatchContext>>,
725}
726
727pub type MemtableRangeContextRef = Arc<MemtableRangeContext>;
728
729impl MemtableRangeContext {
730    /// Creates a new [MemtableRangeContext].
731    pub fn new(id: MemtableId, builder: BoxedIterBuilder, predicate: PredicateGroup) -> Self {
732        Self::new_with_batch_to_record_batch(id, builder, predicate, None)
733    }
734
735    /// Creates a new [MemtableRangeContext] with optional adapter context.
736    pub fn new_with_batch_to_record_batch(
737        id: MemtableId,
738        builder: BoxedIterBuilder,
739        predicate: PredicateGroup,
740        batch_to_record_batch: Option<Arc<BatchToRecordBatchContext>>,
741    ) -> Self {
742        Self {
743            id,
744            builder,
745            predicate,
746            batch_to_record_batch,
747        }
748    }
749}
750
751/// A range in the memtable.
752#[derive(Clone)]
753pub struct MemtableRange {
754    /// Shared context.
755    context: MemtableRangeContextRef,
756    /// Statistics for this memtable range.
757    stats: MemtableStats,
758}
759
760impl MemtableRange {
761    /// Creates a new range from context and stats.
762    pub fn new(context: MemtableRangeContextRef, stats: MemtableStats) -> Self {
763        Self { context, stats }
764    }
765
766    /// Returns the statistics for this range.
767    pub fn stats(&self) -> &MemtableStats {
768        &self.stats
769    }
770
771    /// Returns the id of the memtable to read.
772    pub fn id(&self) -> MemtableId {
773        self.context.id
774    }
775
776    /// Builds an iterator to read the range.
777    /// Filters the result by the specific time range, this ensures memtable won't return
778    /// rows out of the time range when new rows are inserted.
779    pub fn build_prune_iter(
780        &self,
781        time_range: FileTimeRange,
782        metrics: Option<MemScanMetrics>,
783    ) -> Result<BoxedBatchIterator> {
784        let iter = self.context.builder.build(metrics)?;
785        let time_filters = self.context.predicate.time_filters();
786        Ok(Box::new(PruneTimeIterator::new(
787            iter,
788            time_range,
789            time_filters,
790        )))
791    }
792
793    /// Builds an iterator to read all rows in range.
794    pub fn build_iter(&self) -> Result<BoxedBatchIterator> {
795        self.context.builder.build(None)
796    }
797
798    /// Builds a record batch iterator to read rows in range.
799    ///
800    /// For mutable memtables (adapter path), applies time-range pruning to ensure rows
801    /// outside the time range are filtered, matching the behavior of `build_prune_iter`.
802    pub fn build_record_batch_iter(
803        &self,
804        time_range: Option<FileTimeRange>,
805        metrics: Option<MemScanMetrics>,
806    ) -> Result<BoxedRecordBatchIterator> {
807        if self.context.builder.is_record_batch() {
808            return self.context.builder.build_record_batch(time_range, metrics);
809        }
810
811        if let Some(context) = self.context.batch_to_record_batch.as_ref() {
812            let iter = self.context.builder.build(metrics)?;
813            let iter: BoxedBatchIterator = if let Some(time_range) = time_range {
814                let time_filters = self.context.predicate.time_filters();
815                Box::new(PruneTimeIterator::new(iter, time_range, time_filters))
816            } else {
817                iter
818            };
819            return Ok(context.adapt_iter(iter));
820        }
821
822        UnsupportedOperationSnafu {
823            err_msg: "Record batch iterator is not supported by this memtable",
824        }
825        .fail()
826    }
827
828    /// Returns a cheap schema hint for record batches yielded by this range.
829    pub fn record_batch_schema_hint(&self) -> Option<SchemaRef> {
830        self.context.builder.record_batch_schema_hint()
831    }
832
833    /// Returns whether the iterator is a record batch iterator.
834    pub fn is_record_batch(&self) -> bool {
835        self.context.builder.is_record_batch()
836    }
837
838    pub fn num_rows(&self) -> usize {
839        self.stats.num_rows
840    }
841
842    /// Returns the encoded range if available.
843    pub fn encoded(&self) -> Option<EncodedRange> {
844        self.context.builder.encoded_range()
845    }
846}
847
848#[cfg(test)]
849mod tests {
850    use std::collections::HashMap;
851    use std::sync::Arc;
852
853    use common_base::readable_size::ReadableSize;
854    use common_error::ext::WhateverResult;
855    use datatypes::prelude::ConcreteDataType;
856    use datatypes::types::json_type::{JsonNativeType, JsonObjectType};
857    use store_api::metadata::RegionMetadataBuilder;
858    use store_api::storage::RegionId;
859
860    use super::*;
861    use crate::flush::{WriteBufferManager, WriteBufferManagerImpl};
862    use crate::memtable::bulk::BulkMemtableConfig;
863    use crate::test_util::sst_util::sst_region_metadata;
864
865    #[test]
866    fn test_alloc_tracker_without_manager() {
867        let tracker = AllocTracker::new(None);
868        assert_eq!(0, tracker.bytes_allocated());
869        tracker.on_allocation(100);
870        assert_eq!(100, tracker.bytes_allocated());
871        tracker.on_allocation(200);
872        assert_eq!(300, tracker.bytes_allocated());
873
874        tracker.done_allocating();
875        assert_eq!(300, tracker.bytes_allocated());
876    }
877
878    #[test]
879    fn test_alloc_tracker_with_manager() {
880        let manager = Arc::new(WriteBufferManagerImpl::new(1000));
881        {
882            let tracker = AllocTracker::new(Some(manager.clone() as WriteBufferManagerRef));
883
884            tracker.on_allocation(100);
885            assert_eq!(100, tracker.bytes_allocated());
886            assert_eq!(100, manager.memory_usage());
887            assert_eq!(100, manager.mutable_usage());
888
889            for _ in 0..2 {
890                // Done allocating won't free the same memory multiple times.
891                tracker.done_allocating();
892                assert_eq!(100, manager.memory_usage());
893                assert_eq!(0, manager.mutable_usage());
894            }
895        }
896
897        assert_eq!(0, manager.memory_usage());
898        assert_eq!(0, manager.mutable_usage());
899    }
900
901    #[test]
902    fn test_alloc_tracker_without_done_allocating() {
903        let manager = Arc::new(WriteBufferManagerImpl::new(1000));
904        {
905            let tracker = AllocTracker::new(Some(manager.clone() as WriteBufferManagerRef));
906
907            tracker.on_allocation(100);
908            assert_eq!(100, tracker.bytes_allocated());
909            assert_eq!(100, manager.memory_usage());
910            assert_eq!(100, manager.mutable_usage());
911        }
912
913        assert_eq!(0, manager.memory_usage());
914        assert_eq!(0, manager.mutable_usage());
915    }
916
917    #[test]
918    fn test_forced_bulk_memtable_preserves_bulk_config() {
919        let provider = MemtableBuilderProvider::new(None, Arc::new(MitoConfig::default()));
920        let config = BulkMemtableConfig {
921            merge_threshold: 7,
922            encode_row_threshold: 11,
923            encode_bytes_threshold: 13,
924            max_merge_groups: 17,
925        };
926        let options = RegionOptions {
927            memtable: Some(MemtableOptions::Bulk(config.clone())),
928            primary_key_encoding: Some(PrimaryKeyEncoding::Sparse),
929            ..Default::default()
930        };
931
932        let builder =
933            provider.bulk_memtable_builder(options.need_dedup(), options.merge_mode(), &options);
934
935        assert_eq!(&config, builder.config());
936    }
937
938    #[test]
939    fn test_provider_uses_adaptive_config_for_implicit_bulk_builder() {
940        let config = MitoConfig {
941            global_write_buffer_size: ReadableSize::gb(8),
942            ..Default::default()
943        };
944        let provider = MemtableBuilderProvider::new(None, Arc::new(config));
945        let options = RegionOptions::default();
946
947        let builder =
948            provider.bulk_memtable_builder(options.need_dedup(), options.merge_mode(), &options);
949
950        assert_eq!(256 * 1024 * 1024, builder.config().encode_bytes_threshold);
951    }
952
953    #[test]
954    fn test_provider_parses_bulk_memtable_with_adaptive_config() {
955        let config = MitoConfig {
956            global_write_buffer_size: ReadableSize::gb(8),
957            ..Default::default()
958        };
959        let provider = MemtableBuilderProvider::new(None, Arc::new(config));
960        let options = HashMap::from([("memtable.type".to_string(), "bulk".to_string())]);
961
962        let options = provider
963            .parse_options(RegionId::new(0, 0), &options)
964            .unwrap();
965
966        let Some(MemtableOptions::Bulk(config)) = options.memtable else {
967            panic!("expected bulk memtable options");
968        };
969        assert_eq!(256 * 1024 * 1024, config.encode_bytes_threshold);
970    }
971
972    #[test]
973    fn test_provider_preserves_explicit_bulk_encode_bytes_threshold() {
974        let config = MitoConfig {
975            global_write_buffer_size: ReadableSize::gb(8),
976            ..Default::default()
977        };
978        let provider = MemtableBuilderProvider::new(None, Arc::new(config));
979        let options = HashMap::from([
980            ("memtable.type".to_string(), "bulk".to_string()),
981            (
982                "memtable.bulk.encode_bytes_threshold".to_string(),
983                "13".to_string(),
984            ),
985        ]);
986
987        let options = provider
988            .parse_options(RegionId::new(0, 0), &options)
989            .unwrap();
990
991        let Some(MemtableOptions::Bulk(config)) = options.memtable else {
992            panic!("expected bulk memtable options");
993        };
994        assert_eq!(13, config.encode_bytes_threshold);
995    }
996
997    #[test]
998    fn test_json2_requires_flat_format() -> WhateverResult<()> {
999        let mut metadata = sst_region_metadata();
1000        metadata.column_metadatas[2].column_schema.data_type =
1001            ConcreteDataType::json2(JsonNativeType::Object(JsonObjectType::new()));
1002        let metadata = RegionMetadataBuilder::from_existing(metadata).build()?;
1003        let mut options = RegionOptions {
1004            sst_format: Some(FormatType::PrimaryKey),
1005            memtable: Some(MemtableOptions::TimeSeries),
1006            ..Default::default()
1007        };
1008
1009        let err = ensure_json2_uses_flat_format(&metadata, &options).unwrap_err();
1010        assert!(
1011            err.to_string()
1012                .contains("JSON2 columns require sst_format=flat")
1013        );
1014
1015        options.memtable = None;
1016        assert!(ensure_json2_uses_flat_format(&metadata, &options).is_err());
1017
1018        options.memtable = Some(MemtableOptions::Bulk(BulkMemtableConfig::default()));
1019        assert!(ensure_json2_uses_flat_format(&metadata, &options).is_err());
1020
1021        options.memtable = Some(MemtableOptions::TimeSeries);
1022        options.sst_format = Some(FormatType::Flat);
1023        ensure_json2_uses_flat_format(&metadata, &options)?;
1024
1025        options.sst_format = Some(FormatType::PrimaryKey);
1026        options.primary_key_encoding = Some(PrimaryKeyEncoding::Sparse);
1027        assert!(ensure_json2_uses_flat_format(&metadata, &options).is_err());
1028
1029        options.sst_format = Some(FormatType::Flat);
1030        ensure_json2_uses_flat_format(&metadata, &options)?;
1031        Ok(())
1032    }
1033}