Skip to main content

mito2/memtable/
version.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//! Memtable version.
16
17use std::sync::Arc;
18use std::sync::atomic::{AtomicBool, Ordering};
19use std::time::Duration;
20
21use common_time::Timestamp;
22use smallvec::SmallVec;
23use store_api::metadata::RegionMetadataRef;
24use store_api::storage::SequenceNumber;
25
26use crate::error::Result;
27use crate::memtable::bulk::part::BulkPart;
28use crate::memtable::time_partition::TimePartitions;
29use crate::memtable::{KeyValues, MemtableBuilderRef, MemtableId, MemtableRef};
30
31pub(crate) type SmallMemtableVec = SmallVec<[MemtableRef; 2]>;
32
33/// A version of current memtables in a region.
34#[derive(Debug, Clone)]
35pub(crate) struct MemtableVersion {
36    /// Mutable memtable.
37    pub(crate) mutable: MutableMemtablesRef,
38    /// Immutable memtables.
39    ///
40    /// We only allow one flush job per region but if a flush job failed, then we
41    /// might need to store more than one immutable memtable on the next time we
42    /// flush the region.
43    immutables: SmallMemtableVec,
44    /// Whether immutable memtables may contain skip-WAL writes.
45    /// Flushes are serialized per region and cover all immutable memtables.
46    immutable_has_skip_wal_writes: bool,
47}
48
49pub(crate) type MemtableVersionRef = Arc<MemtableVersion>;
50
51impl MemtableVersion {
52    /// Returns a new [MemtableVersion] with specific mutable memtable.
53    pub(crate) fn new(mutable: TimePartitions) -> MemtableVersion {
54        MemtableVersion {
55            mutable: Arc::new(MutableMemtables::new(mutable)),
56            immutables: SmallVec::new(),
57            immutable_has_skip_wal_writes: false,
58        }
59    }
60
61    /// Returns whether current memtables may contain skip-WAL writes.
62    pub(crate) fn has_skip_wal_writes(&self) -> bool {
63        self.mutable.has_skip_wal_writes() || self.immutable_has_skip_wal_writes
64    }
65
66    /// Immutable memtables.
67    pub(crate) fn immutables(&self) -> &[MemtableRef] {
68        &self.immutables
69    }
70
71    /// Lists mutable and immutable memtables.
72    pub(crate) fn list_memtables(&self) -> Vec<MemtableRef> {
73        let mut mems = Vec::with_capacity(self.immutables.len() + self.mutable.num_partitions());
74        self.mutable.list_memtables(&mut mems);
75        mems.extend_from_slice(&self.immutables);
76        mems
77    }
78
79    /// Returns a sequence lower bound covering mutable and immutable memtables.
80    /// Empty memtables impose no compaction barrier, including newly forked ones.
81    pub(crate) fn min_sequence(&self) -> Option<SequenceNumber> {
82        self.list_memtables()
83            .iter()
84            .filter(|mem| !mem.is_empty())
85            .map(|mem| mem.min_sequence())
86            .min()
87    }
88
89    /// Returns a new [MemtableVersion] which switches the old mutable memtable to immutable
90    /// memtable.
91    ///
92    /// It will switch to use the `time_window` provided.
93    ///
94    /// Returns `None` if the mutable memtable is empty.
95    pub(crate) fn freeze_mutable(
96        &self,
97        metadata: &RegionMetadataRef,
98        time_window: Option<Duration>,
99    ) -> Result<Option<MemtableVersion>> {
100        if self.mutable.is_empty() {
101            // No need to freeze the mutable memtable, but we need to check the time window.
102            if Some(self.mutable.part_duration()) == time_window {
103                // If the time window is the same, we don't need to update it.
104                return Ok(None);
105            }
106
107            // Update the time window.
108            let mutable = self
109                .mutable
110                .partitions
111                .new_with_part_duration(time_window, None);
112            common_telemetry::debug!(
113                "Freeze empty memtable, update partition duration from {:?} to {:?}",
114                self.mutable.part_duration(),
115                time_window
116            );
117            return Ok(Some(MemtableVersion {
118                mutable: Arc::new(MutableMemtables::new(mutable)),
119                immutables: self.immutables.clone(),
120                immutable_has_skip_wal_writes: self.immutable_has_skip_wal_writes,
121            }));
122        }
123
124        // Marks the mutable memtable as immutable so it can free the memory usage from our
125        // soft limit.
126        self.mutable.partitions.freeze()?;
127        // Fork the memtable.
128        if Some(self.mutable.part_duration()) != time_window {
129            common_telemetry::debug!(
130                "Fork memtable, update partition duration from {:?}, to {:?}",
131                self.mutable.part_duration(),
132                time_window
133            );
134        }
135        let mutable = Arc::new(MutableMemtables::new(
136            self.mutable.partitions.fork(metadata, time_window),
137        ));
138
139        let mut immutables =
140            SmallVec::with_capacity(self.immutables.len() + self.mutable.num_partitions());
141        immutables.extend(self.immutables.iter().cloned());
142        // Pushes the mutable memtable to immutable list.
143        self.mutable
144            .partitions
145            .list_memtables_to_small_vec(&mut immutables);
146
147        Ok(Some(MemtableVersion {
148            mutable,
149            immutables,
150            immutable_has_skip_wal_writes: self.immutable_has_skip_wal_writes
151                || self.mutable.has_skip_wal_writes(),
152        }))
153    }
154
155    /// Removes memtables by ids from immutable memtables.
156    pub(crate) fn remove_memtables(&mut self, ids: &[MemtableId]) {
157        self.immutables = self
158            .immutables
159            .iter()
160            .filter(|mem| !ids.contains(&mem.id()))
161            .cloned()
162            .collect();
163        if self.immutables.is_empty() {
164            self.immutable_has_skip_wal_writes = false;
165        }
166    }
167
168    /// Returns the memory usage of the mutable memtable.
169    pub(crate) fn mutable_usage(&self) -> usize {
170        self.mutable.memory_usage()
171    }
172
173    /// Returns the memory usage of the immutable memtables.
174    pub(crate) fn immutables_usage(&self) -> usize {
175        self.immutables
176            .iter()
177            .map(|mem| mem.stats().estimated_bytes)
178            .sum()
179    }
180
181    /// Returns the number of rows in memtables.
182    pub(crate) fn num_rows(&self) -> u64 {
183        self.immutables
184            .iter()
185            .map(|mem| mem.stats().num_rows as u64)
186            .sum::<u64>()
187            + self.mutable.num_rows()
188    }
189
190    /// Returns the time range covered by the memtables, if any hold data.
191    pub(crate) fn time_range(&self) -> Option<(Timestamp, Timestamp)> {
192        let mut mutables = Vec::new();
193        self.mutable.list_memtables(&mut mutables);
194        self.immutables
195            .iter()
196            .chain(mutables.iter())
197            .filter_map(|mem| mem.stats().time_range())
198            .reduce(|(min_a, max_a), (min_b, max_b)| (min_a.min(min_b), max_a.max(max_b)))
199    }
200
201    /// Returns true if the memtable version is empty.
202    ///
203    /// The version is empty when mutable memtable is empty and there is no
204    /// immutable memtables.
205    pub(crate) fn is_empty(&self) -> bool {
206        self.mutable.is_empty() && self.immutables.is_empty()
207    }
208}
209
210/// Mutable time partitions and their shared write state.
211///
212/// Version clones share this object. Freezing creates a new object so writes to
213/// the new mutable memtables cannot change the old version's state.
214#[derive(Debug)]
215pub(crate) struct MutableMemtables {
216    partitions: TimePartitions,
217    /// Set before installation because failed writes may install some rows.
218    has_skip_wal_writes: AtomicBool,
219}
220
221pub(crate) type MutableMemtablesRef = Arc<MutableMemtables>;
222
223impl MutableMemtables {
224    fn new(partitions: TimePartitions) -> Self {
225        Self {
226            partitions,
227            has_skip_wal_writes: AtomicBool::new(false),
228        }
229    }
230
231    /// Marks that these memtables may contain skip-WAL writes.
232    pub(crate) fn mark_skip_wal_writes(&self) {
233        self.has_skip_wal_writes.store(true, Ordering::Relaxed);
234    }
235
236    fn has_skip_wal_writes(&self) -> bool {
237        self.has_skip_wal_writes.load(Ordering::Relaxed)
238    }
239
240    /// Writes key values to the mutable time partitions.
241    pub(crate) fn write(&self, kvs: &KeyValues) -> Result<()> {
242        self.partitions.write(kvs)
243    }
244
245    /// Writes a bulk part to the mutable time partitions.
246    pub(crate) fn write_bulk(&self, part: BulkPart) -> Result<()> {
247        self.partitions.write_bulk(part)
248    }
249
250    /// Returns whether all mutable time partitions are empty.
251    pub(crate) fn is_empty(&self) -> bool {
252        self.partitions.is_empty()
253    }
254
255    /// Returns the memtable builder.
256    pub(crate) fn memtable_builder(&self) -> &MemtableBuilderRef {
257        self.partitions.memtable_builder()
258    }
259
260    /// Returns the next memtable ID.
261    pub(crate) fn next_memtable_id(&self) -> MemtableId {
262        self.partitions.next_memtable_id()
263    }
264
265    /// Returns the time partition duration.
266    pub(crate) fn part_duration(&self) -> Duration {
267        self.partitions.part_duration()
268    }
269
270    /// Creates empty time partitions with an optional new duration and builder.
271    /// The returned partitions do not share data or write state with this object.
272    pub(crate) fn new_with_part_duration(
273        &self,
274        part_duration: Option<Duration>,
275        memtable_builder: Option<MemtableBuilderRef>,
276    ) -> TimePartitions {
277        self.partitions
278            .new_with_part_duration(part_duration, memtable_builder)
279    }
280
281    /// Returns the mutable memory usage.
282    pub(crate) fn memory_usage(&self) -> usize {
283        self.partitions.memory_usage()
284    }
285
286    /// Returns the number of time series in mutable time partitions.
287    pub(crate) fn series_count(&self) -> usize {
288        self.partitions.series_count()
289    }
290
291    fn num_rows(&self) -> u64 {
292        self.partitions.num_rows()
293    }
294
295    fn num_partitions(&self) -> usize {
296        self.partitions.num_partitions()
297    }
298
299    fn list_memtables(&self, memtables: &mut Vec<MemtableRef>) {
300        self.partitions.list_memtables(memtables);
301    }
302}
303
304#[cfg(test)]
305mod tests {
306    use super::*;
307    use crate::memtable::time_series::TimeSeriesMemtableBuilder;
308    use crate::test_util::memtable_util;
309
310    #[test]
311    fn test_skip_wal_writes_follow_memtable_lifecycle() {
312        for new_skip_wal_writes in [false, true] {
313            let metadata = memtable_util::metadata_for_test();
314            let partitions = TimePartitions::new(
315                metadata.clone(),
316                Arc::new(TimeSeriesMemtableBuilder::default()),
317                0,
318                Some(Duration::from_secs(5)),
319            );
320            let version = MemtableVersion::new(partitions);
321            let snapshot = version.clone();
322            assert!(!version.has_skip_wal_writes());
323
324            let kvs = memtable_util::build_key_values(
325                &metadata,
326                "hello".to_string(),
327                0,
328                &[1000, 7000],
329                1,
330            );
331            version.mutable.mark_skip_wal_writes();
332            version.mutable.write(&kvs).unwrap();
333            assert!(snapshot.has_skip_wal_writes());
334
335            let mut frozen = version
336                .freeze_mutable(&metadata, Some(Duration::from_secs(5)))
337                .unwrap()
338                .unwrap();
339            assert_eq!(2, frozen.immutables().len());
340            assert!(frozen.has_skip_wal_writes());
341            assert!(!frozen.mutable.has_skip_wal_writes());
342            assert!(!Arc::ptr_eq(&version.mutable, &frozen.mutable));
343
344            let during_flush = frozen.clone();
345            if new_skip_wal_writes {
346                frozen.mutable.mark_skip_wal_writes();
347            }
348            frozen.mutable.write(&kvs).unwrap();
349            let ids: Vec<_> = frozen.immutables().iter().map(|mem| mem.id()).collect();
350            frozen.remove_memtables(&ids);
351            assert_eq!(new_skip_wal_writes, frozen.has_skip_wal_writes());
352            assert_eq!(
353                new_skip_wal_writes,
354                during_flush.mutable.has_skip_wal_writes()
355            );
356            // Removing flushed memtables does not mutate the old flush snapshot.
357            assert_eq!(2, during_flush.immutables().len());
358            assert!(during_flush.has_skip_wal_writes());
359
360            let mut next_flush = frozen
361                .freeze_mutable(&metadata, Some(Duration::from_secs(5)))
362                .unwrap()
363                .unwrap();
364            let ids: Vec<_> = next_flush.immutables().iter().map(|mem| mem.id()).collect();
365            next_flush.remove_memtables(&ids);
366            assert!(!next_flush.has_skip_wal_writes());
367        }
368    }
369
370    #[test]
371    fn test_skip_wal_writes_survive_flush_retry() {
372        let metadata = memtable_util::metadata_for_test();
373        let partitions = TimePartitions::new(
374            metadata.clone(),
375            Arc::new(TimeSeriesMemtableBuilder::default()),
376            0,
377            Some(Duration::from_secs(5)),
378        );
379        let version = MemtableVersion::new(partitions);
380        let kvs = memtable_util::build_key_values(&metadata, "hello".to_string(), 0, &[1000], 1);
381        version.mutable.mark_skip_wal_writes();
382        version.mutable.write(&kvs).unwrap();
383        let frozen = version
384            .freeze_mutable(&metadata, Some(Duration::from_secs(5)))
385            .unwrap()
386            .unwrap();
387
388        // A failed flush leaves immutable memtables installed. Changing the
389        // empty mutable's partition duration must retain their write state.
390        let frozen = frozen
391            .freeze_mutable(&metadata, Some(Duration::from_secs(10)))
392            .unwrap()
393            .unwrap();
394        assert!(frozen.has_skip_wal_writes());
395        assert!(!frozen.mutable.has_skip_wal_writes());
396
397        // A retry includes the old immutable memtables and new logged writes.
398        frozen.mutable.write(&kvs).unwrap();
399        let mut retried = frozen
400            .freeze_mutable(&metadata, Some(Duration::from_secs(10)))
401            .unwrap()
402            .unwrap();
403        assert_eq!(2, retried.immutables().len());
404        assert!(retried.has_skip_wal_writes());
405        let ids: Vec<_> = retried.immutables().iter().map(|mem| mem.id()).collect();
406        retried.remove_memtables(&ids);
407        assert!(!retried.has_skip_wal_writes());
408
409        let replacement = MemtableVersion::new(TimePartitions::new(
410            metadata,
411            frozen.mutable.memtable_builder().clone(),
412            frozen.mutable.next_memtable_id(),
413            None,
414        ));
415        assert!(!replacement.has_skip_wal_writes());
416        assert!(frozen.has_skip_wal_writes());
417    }
418}