Skip to main content

mito2/region/
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//! Version control of mito engine.
16//!
17//! Version is an immutable snapshot of region's metadata.
18//!
19//! To read latest data from `VersionControl`, we should
20//! 1. Acquire `Version` from `VersionControl`.
21//! 2. Then acquire last sequence.
22//!
23//! Reason: data may be flushed/compacted and some data with old sequence may be removed
24//! and became invisible between step 1 and 2, so need to acquire version at first.
25
26use std::sync::{Arc, RwLock};
27use std::time::Duration;
28
29use common_telemetry::info;
30use store_api::metadata::RegionMetadataRef;
31use store_api::storage::{RegionId, SequenceNumber};
32
33use crate::error::Result;
34use crate::manifest::action::{RegionEdit, TruncateKind};
35use crate::memtable::time_partition::TimePartitions;
36use crate::memtable::version::{MemtableVersion, MemtableVersionRef};
37use crate::memtable::{MemtableBuilderRef, MemtableId};
38use crate::region::options::RegionOptions;
39use crate::sst::file::FileMeta;
40use crate::sst::file_purger::FilePurgerRef;
41use crate::sst::version::{SstVersion, SstVersionRef};
42use crate::wal::EntryId;
43
44/// Controls metadata and sequence numbers for a region.
45///
46/// It manages metadata in a copy-on-write fashion. Any modification to a region's metadata
47/// will generate a new [Version].
48#[derive(Debug)]
49pub(crate) struct VersionControl {
50    data: RwLock<VersionControlData>,
51}
52
53impl VersionControl {
54    /// Returns a new [VersionControl] with specific `version`.
55    pub(crate) fn new(version: Version) -> VersionControl {
56        // Initialize sequence and entry id from flushed sequence and entry id.
57        let (flushed_sequence, flushed_entry_id) =
58            (version.flushed_sequence, version.flushed_entry_id);
59        VersionControl {
60            data: RwLock::new(VersionControlData {
61                version: Arc::new(version),
62                last_sequence: flushed_sequence,
63                committed_sequence: flushed_sequence,
64                last_entry_id: flushed_entry_id,
65                is_dropped: false,
66            }),
67        }
68    }
69
70    /// Returns the id of the region controlled by this instance.
71    pub(crate) fn region_id(&self) -> RegionId {
72        self.data.read().unwrap().version.metadata.region_id
73    }
74
75    /// Returns current copy of data.
76    pub(crate) fn current(&self) -> VersionControlData {
77        self.data.read().unwrap().clone()
78    }
79
80    /// Updates last sequence number to the maximum.
81    pub(crate) fn update_last_sequence(&self, seq: SequenceNumber) {
82        let mut data = self.data.write().unwrap();
83        data.last_sequence = data.last_sequence.max(seq);
84    }
85
86    /// Updates the `committed_sequence` of version.
87    pub(crate) fn set_committed_sequence(&self, seq: SequenceNumber) {
88        let mut data = self.data.write().unwrap();
89        data.committed_sequence = data.committed_sequence.max(seq);
90        data.last_sequence = data.last_sequence.max(data.committed_sequence);
91    }
92
93    /// Updates committed sequence and entry id.
94    pub(crate) fn set_sequence_and_entry_id(&self, seq: SequenceNumber, entry_id: EntryId) {
95        let mut data = self.data.write().unwrap();
96        data.committed_sequence = data.committed_sequence.max(seq);
97        data.last_sequence = data.last_sequence.max(data.committed_sequence);
98        data.last_entry_id = entry_id;
99    }
100
101    /// Updates last entry id.
102    pub(crate) fn set_entry_id(&self, entry_id: EntryId) {
103        let mut data = self.data.write().unwrap();
104        data.last_entry_id = entry_id;
105    }
106
107    /// Sequence number of last committed data.
108    pub(crate) fn committed_sequence(&self) -> SequenceNumber {
109        self.data.read().unwrap().committed_sequence
110    }
111
112    /// Freezes the mutable memtable if it is not empty.
113    pub(crate) fn freeze_mutable(&self) -> Result<()> {
114        let version = self.current().version;
115        let time_window = version.compaction_time_window;
116
117        let Some(new_memtables) = version
118            .memtables
119            .freeze_mutable(&version.metadata, time_window)?
120        else {
121            return Ok(());
122        };
123
124        // Create a new version with memtable switched.
125        let new_version = Arc::new(
126            VersionBuilder::from_version(version)
127                .memtables(new_memtables)
128                .build(),
129        );
130
131        let mut version_data = self.data.write().unwrap();
132        version_data.version = new_version;
133
134        Ok(())
135    }
136
137    /// Applies region option changes and generates a new version.
138    pub(crate) fn alter_options(&self, options: RegionOptions) {
139        let version = self.current().version;
140        let new_version = Arc::new(
141            VersionBuilder::from_version(version)
142                .options(options)
143                .build(),
144        );
145        let mut version_data = self.data.write().unwrap();
146        version_data.version = new_version;
147    }
148
149    /// Apply edit to current version.
150    ///
151    /// If `edit` is None, only removes the specified memtables.
152    pub(crate) fn apply_edit(
153        &self,
154        edit: Option<RegionEdit>,
155        memtables_to_remove: &[MemtableId],
156        purger: FilePurgerRef,
157    ) {
158        let version = self.current().version;
159        let builder = VersionBuilder::from_version(version);
160        let committed_sequence = edit.as_ref().and_then(|e| e.committed_sequence);
161        let builder = if let Some(edit) = edit {
162            builder.apply_edit(edit, purger)
163        } else {
164            builder
165        };
166        let new_version = Arc::new(builder.remove_memtables(memtables_to_remove).build());
167
168        let mut version_data = self.data.write().unwrap();
169        version_data.committed_sequence = if let Some(committed_in_edit) = committed_sequence {
170            version_data.committed_sequence.max(committed_in_edit)
171        } else {
172            version_data.committed_sequence
173        };
174        version_data.last_sequence = version_data
175            .last_sequence
176            .max(version_data.committed_sequence);
177        version_data.version = new_version;
178    }
179
180    /// Mark all opened files as deleted and set the delete marker in [VersionControlData]
181    pub(crate) fn mark_dropped(&self) {
182        let version = self.current().version;
183        let memtable_builder = version.memtables.mutable.memtable_builder().clone();
184        let new_mutable =
185            Self::new_mutable_from_version(&version, version.metadata.clone(), memtable_builder);
186
187        let mut data = self.data.write().unwrap();
188        data.is_dropped = true;
189        data.version.ssts.mark_all_deleted();
190        // Reset version so we can release the reference to memtables and SSTs.
191        let new_version =
192            Arc::new(VersionBuilder::new(version.metadata.clone(), new_mutable).build());
193        data.version = new_version;
194    }
195
196    /// Alter schema of the region.
197    ///
198    /// It replaces existing mutable memtable with a memtable that uses the
199    /// new schema. Memtables of the version must be empty.
200    pub(crate) fn alter_schema(&self, metadata: RegionMetadataRef) {
201        let version = self.current().version;
202        let memtable_builder = version.memtables.mutable.memtable_builder().clone();
203        let new_mutable =
204            Self::new_mutable_from_version(&version, metadata.clone(), memtable_builder);
205        debug_assert!(version.memtables.mutable.is_empty());
206        debug_assert!(version.memtables.immutables().is_empty());
207        let new_version = Arc::new(
208            VersionBuilder::from_version(version)
209                .metadata(metadata)
210                .memtables(MemtableVersion::new(new_mutable))
211                .build(),
212        );
213
214        let mut version_data = self.data.write().unwrap();
215        version_data.version = new_version;
216    }
217
218    /// Alter metadata of the region without rebuilding memtables.
219    pub(crate) fn alter_metadata(&self, metadata: RegionMetadataRef) {
220        let version = self.current().version;
221        let new_version = Arc::new(
222            VersionBuilder::from_version(version)
223                .metadata(metadata)
224                .build(),
225        );
226
227        let mut version_data = self.data.write().unwrap();
228        version_data.version = new_version;
229    }
230
231    /// Alter schema and format of the region.
232    ///
233    /// It replaces existing mutable memtable with a memtable that uses the
234    /// new format. Memtables of the version must be empty.
235    pub(crate) fn alter_schema_and_format(
236        &self,
237        metadata: RegionMetadataRef,
238        options: RegionOptions,
239        memtable_builder: MemtableBuilderRef,
240    ) {
241        let version = self.current().version;
242        // Use the new metadata to build `TimePartitions`.
243        let new_mutable =
244            Self::new_mutable_from_version(&version, metadata.clone(), memtable_builder);
245        debug_assert!(version.memtables.mutable.is_empty());
246        debug_assert!(version.memtables.immutables().is_empty());
247        let new_version = Arc::new(
248            VersionBuilder::from_version(version)
249                .metadata(metadata)
250                .options(options)
251                .memtables(MemtableVersion::new(new_mutable))
252                .build(),
253        );
254
255        let mut version_data = self.data.write().unwrap();
256        version_data.version = new_version;
257    }
258
259    /// Truncate current version.
260    pub(crate) fn truncate(&self, truncate_kind: TruncateKind) {
261        let version = self.current().version;
262
263        match truncate_kind {
264            TruncateKind::All {
265                truncated_entry_id,
266                truncated_sequence,
267            } => {
268                let memtable_builder = version.memtables.mutable.memtable_builder().clone();
269                let new_mutable = Self::new_mutable_from_version(
270                    &version,
271                    version.metadata.clone(),
272                    memtable_builder,
273                );
274                let new_version = Arc::new(
275                    VersionBuilder::from_version(version)
276                        .memtables(MemtableVersion::new(new_mutable))
277                        .clear_files()
278                        .flushed_entry_id(truncated_entry_id)
279                        .flushed_sequence(truncated_sequence)
280                        .truncated_entry_id(Some(truncated_entry_id))
281                        .build(),
282                );
283
284                let mut version_data = self.data.write().unwrap();
285                version_data.version.ssts.mark_all_deleted();
286                version_data.version = new_version;
287            }
288            TruncateKind::Partial { files_to_remove } => {
289                let new_version = Arc::new(
290                    VersionBuilder::from_version(version)
291                        .remove_files(files_to_remove.into_iter())
292                        .build(),
293                );
294
295                let mut version_data = self.data.write().unwrap();
296                // notice since it's partial, no need to mark all files as deleted
297                version_data.version = new_version;
298            }
299        };
300    }
301
302    /// Discards all memtables while preserving persisted SST files.
303    pub(crate) fn discard_unflushed(
304        &self,
305        discarded_entry_id: EntryId,
306        discarded_sequence: SequenceNumber,
307    ) {
308        let version = self.current().version;
309        let memtable_builder = version.memtables.mutable.memtable_builder().clone();
310        let new_mutable =
311            Self::new_mutable_from_version(&version, version.metadata.clone(), memtable_builder);
312        let new_version = Arc::new(
313            VersionBuilder::from_version(version)
314                .memtables(MemtableVersion::new(new_mutable))
315                .flushed_entry_id(discarded_entry_id)
316                .flushed_sequence(discarded_sequence)
317                .build(),
318        );
319
320        let mut version_data = self.data.write().unwrap();
321        version_data.version = new_version;
322    }
323
324    /// Overwrites the current version with a new version.
325    pub(crate) fn overwrite_current(&self, version: VersionRef) {
326        let mut version_data = self.data.write().unwrap();
327        version_data.last_sequence = version_data.last_sequence.max(version.flushed_sequence);
328        version_data.version = version;
329    }
330
331    fn new_mutable_from_version(
332        version: &Version,
333        metadata: RegionMetadataRef,
334        memtable_builder: MemtableBuilderRef,
335    ) -> TimePartitions {
336        TimePartitions::new(
337            metadata,
338            memtable_builder,
339            version.memtables.mutable.next_memtable_id(),
340            Some(version.memtables.mutable.part_duration()),
341        )
342    }
343}
344
345pub(crate) type VersionControlRef = Arc<VersionControl>;
346
347/// Data of [VersionControl].
348#[derive(Debug, Clone)]
349pub(crate) struct VersionControlData {
350    /// Latest version.
351    pub(crate) version: VersionRef,
352    /// Last allocated sequence number.
353    pub(crate) last_sequence: SequenceNumber,
354    /// Sequence number of last committed data.
355    ///
356    /// Starts from 1 (zero means no data).
357    pub(crate) committed_sequence: SequenceNumber,
358    /// Last WAL entry Id.
359    ///
360    /// Starts from 1 (zero means no data).
361    pub(crate) last_entry_id: EntryId,
362    /// Marker of whether this region is dropped/dropping
363    pub(crate) is_dropped: bool,
364}
365
366impl VersionControlData {
367    /// Approximate timeseries count in current version.
368    pub(crate) fn series_count(&self) -> usize {
369        self.version.memtables.mutable.series_count()
370    }
371}
372
373/// Static metadata of a region.
374#[derive(Clone, Debug)]
375pub(crate) struct Version {
376    /// Metadata of the region.
377    ///
378    /// Altering metadata isn't frequent, storing metadata in Arc to allow sharing
379    /// metadata and reuse metadata when creating a new `Version`.
380    pub(crate) metadata: RegionMetadataRef,
381    /// Mutable and immutable memtables.
382    ///
383    /// Wrapped in Arc to make clone of `Version` much cheaper.
384    pub(crate) memtables: MemtableVersionRef,
385    /// SSTs of the region.
386    pub(crate) ssts: SstVersionRef,
387    /// Inclusive max WAL entry id of flushed data.
388    pub(crate) flushed_entry_id: EntryId,
389    /// Inclusive max sequence of flushed data.
390    pub(crate) flushed_sequence: SequenceNumber,
391    /// Latest entry id during the truncating table.
392    ///
393    /// Used to check if it is a flush task during the truncating table.
394    pub(crate) truncated_entry_id: Option<EntryId>,
395    /// Inferred compaction time window from flush.
396    ///
397    /// If compaction options contain a time window, it will overwrite this value
398    /// when creating a new version from the [VersionBuilder].
399    pub(crate) compaction_time_window: Option<Duration>,
400    /// Options of the region.
401    pub(crate) options: RegionOptions,
402}
403
404pub(crate) type VersionRef = Arc<Version>;
405
406/// Version builder.
407pub(crate) struct VersionBuilder {
408    metadata: RegionMetadataRef,
409    memtables: MemtableVersionRef,
410    ssts: SstVersionRef,
411    flushed_entry_id: EntryId,
412    flushed_sequence: SequenceNumber,
413    truncated_entry_id: Option<EntryId>,
414    compaction_time_window: Option<Duration>,
415    options: RegionOptions,
416}
417
418impl VersionBuilder {
419    /// Returns a new builder.
420    pub(crate) fn new(metadata: RegionMetadataRef, mutable: TimePartitions) -> Self {
421        VersionBuilder {
422            metadata: metadata.clone(),
423            memtables: Arc::new(MemtableVersion::new(mutable)),
424            ssts: Arc::new(SstVersion::new(metadata)),
425            flushed_entry_id: 0,
426            flushed_sequence: 0,
427            truncated_entry_id: None,
428            compaction_time_window: None,
429            options: RegionOptions::default(),
430        }
431    }
432
433    /// Returns a new builder from an existing version.
434    pub(crate) fn from_version(version: VersionRef) -> Self {
435        VersionBuilder {
436            metadata: version.metadata.clone(),
437            memtables: version.memtables.clone(),
438            ssts: version.ssts.clone(),
439            flushed_entry_id: version.flushed_entry_id,
440            flushed_sequence: version.flushed_sequence,
441            truncated_entry_id: version.truncated_entry_id,
442            compaction_time_window: version.compaction_time_window,
443            options: version.options.clone(),
444        }
445    }
446
447    /// Sets memtables.
448    pub(crate) fn memtables(mut self, memtables: MemtableVersion) -> Self {
449        self.memtables = Arc::new(memtables);
450        self
451    }
452
453    /// Sets metadata.
454    pub(crate) fn metadata(mut self, metadata: RegionMetadataRef) -> Self {
455        if !Arc::ptr_eq(&self.metadata, &metadata) {
456            Arc::make_mut(&mut self.ssts).set_metadata(metadata.clone());
457        }
458        self.metadata = metadata;
459        self
460    }
461
462    /// Sets flushed entry id.
463    pub(crate) fn flushed_entry_id(mut self, entry_id: EntryId) -> Self {
464        self.flushed_entry_id = entry_id;
465        self
466    }
467
468    /// Sets flushed sequence.
469    pub(crate) fn flushed_sequence(mut self, sequence: SequenceNumber) -> Self {
470        self.flushed_sequence = sequence;
471        self
472    }
473
474    /// Sets truncated entry id.
475    pub(crate) fn truncated_entry_id(mut self, entry_id: Option<EntryId>) -> Self {
476        self.truncated_entry_id = entry_id;
477        self
478    }
479
480    /// Sets compaction time window.
481    pub(crate) fn compaction_time_window(mut self, window: Option<Duration>) -> Self {
482        self.compaction_time_window = window;
483        self
484    }
485
486    /// Sets options.
487    pub(crate) fn options(mut self, options: RegionOptions) -> Self {
488        self.options = options;
489        self
490    }
491
492    /// Apply edit to the builder.
493    pub(crate) fn apply_edit(mut self, edit: RegionEdit, file_purger: FilePurgerRef) -> Self {
494        if let Some(entry_id) = edit.flushed_entry_id {
495            self.flushed_entry_id = self.flushed_entry_id.max(entry_id);
496        }
497        if let Some(sequence) = edit.flushed_sequence {
498            self.flushed_sequence = self.flushed_sequence.max(sequence);
499        }
500        if let Some(window) = edit.compaction_time_window {
501            self.compaction_time_window = Some(window);
502        }
503        if !edit.files_to_add.is_empty() || !edit.files_to_remove.is_empty() {
504            let mut ssts = (*self.ssts).clone();
505            ssts.add_files(file_purger, edit.files_to_add.into_iter());
506            ssts.remove_files(edit.files_to_remove.into_iter());
507            self.ssts = Arc::new(ssts);
508        }
509
510        self
511    }
512
513    /// Remove memtables from the builder.
514    pub(crate) fn remove_memtables(mut self, ids: &[MemtableId]) -> Self {
515        if !ids.is_empty() {
516            let mut memtables = (*self.memtables).clone();
517            memtables.remove_memtables(ids);
518            self.memtables = Arc::new(memtables);
519        }
520        self
521    }
522
523    /// Add files to the builder.
524    pub(crate) fn add_files(
525        mut self,
526        file_purger: FilePurgerRef,
527        files: impl Iterator<Item = FileMeta>,
528    ) -> Self {
529        let mut ssts = (*self.ssts).clone();
530        ssts.add_files(file_purger, files);
531        self.ssts = Arc::new(ssts);
532
533        self
534    }
535
536    pub(crate) fn remove_files(mut self, files: impl Iterator<Item = FileMeta>) -> Self {
537        let mut ssts = (*self.ssts).clone();
538        ssts.remove_files(files);
539        self.ssts = Arc::new(ssts);
540
541        self
542    }
543
544    /// Clear all files in the builder.
545    pub(crate) fn clear_files(mut self) -> Self {
546        self.ssts = Arc::new(SstVersion::new(self.metadata.clone()));
547        self
548    }
549
550    /// Builds a new [Version] from the builder.
551    /// It overwrites the window size by compaction option.
552    pub(crate) fn build(self) -> Version {
553        let compaction_time_window = self
554            .options
555            .compaction
556            .time_window()
557            .or(self.compaction_time_window);
558        if self.compaction_time_window.is_some()
559            && compaction_time_window != self.compaction_time_window
560        {
561            info!(
562                "VersionBuilder overwrites region compaction time window from {:?} to {:?}, region: {}",
563                self.compaction_time_window, compaction_time_window, self.metadata.region_id
564            );
565        }
566
567        Version {
568            metadata: self.metadata,
569            memtables: self.memtables,
570            ssts: self.ssts,
571            flushed_entry_id: self.flushed_entry_id,
572            flushed_sequence: self.flushed_sequence,
573            truncated_entry_id: self.truncated_entry_id,
574            compaction_time_window,
575            options: self.options,
576        }
577    }
578}
579
580#[cfg(test)]
581mod tests {
582    use super::*;
583    use crate::test_util::version_util::VersionControlBuilder;
584
585    #[test]
586    fn test_sequence_watermarks_are_monotonic() {
587        let builder = VersionControlBuilder::new();
588        let mut version = builder.build_version();
589        version.flushed_sequence = 8;
590        version.flushed_entry_id = 4;
591        let vc = VersionControl::new(version);
592        assert_eq!(
593            (vc.current().last_sequence, vc.committed_sequence()),
594            (8, 8)
595        );
596
597        vc.update_last_sequence(20);
598        vc.update_last_sequence(9);
599        vc.set_committed_sequence(10);
600        vc.set_committed_sequence(9);
601        assert_eq!(
602            (vc.current().last_sequence, vc.committed_sequence()),
603            (20, 10)
604        );
605        assert_eq!(vc.current().last_entry_id, 4);
606        vc.set_sequence_and_entry_id(12, 5);
607        vc.set_sequence_and_entry_id(4, 6);
608        assert_eq!(
609            (vc.current().last_sequence, vc.committed_sequence()),
610            (20, 12)
611        );
612        assert_eq!(vc.current().version.flushed_sequence, 8);
613
614        for sequence in [25, 11] {
615            vc.apply_edit(
616                Some(RegionEdit {
617                    files_to_add: vec![],
618                    files_to_remove: vec![],
619                    timestamp_ms: None,
620                    compaction_time_window: None,
621                    flushed_entry_id: None,
622                    flushed_sequence: None,
623                    committed_sequence: Some(sequence),
624                }),
625                &[],
626                builder.file_purger(),
627            );
628            assert_eq!(
629                (vc.current().last_sequence, vc.committed_sequence()),
630                (25, 25)
631            );
632            assert_eq!(vc.current().last_entry_id, 6);
633        }
634    }
635}