lsm_tree/tree/
inner.rs

1// Copyright (c) 2024-present, fjall-rs
2// This source code is licensed under both the Apache 2.0 and MIT License
3// (found in the LICENSE-* files in the repository)
4
5use crate::{
6    config::Config, file::LEVELS_MANIFEST_FILE, level_manifest::LevelManifest, memtable::Memtable,
7    segment::meta::SegmentId, stop_signal::StopSignal,
8};
9use std::sync::{atomic::AtomicU64, Arc, RwLock};
10
11/// Unique tree ID
12///
13/// Tree IDs are monotonically increasing integers.
14pub type TreeId = u64;
15
16/// Unique memtable ID
17///
18/// Memtable IDs map one-to-one to some segment.
19pub type MemtableId = u64;
20
21/// Stores references to all sealed memtables
22///
23/// Memtable IDs are monotonically increasing, so we don't really
24/// need a search tree; also there are only a handful of them at most.
25#[derive(Default)]
26pub struct SealedMemtables(Vec<(MemtableId, Arc<Memtable>)>);
27
28impl SealedMemtables {
29    pub fn add(&mut self, id: MemtableId, memtable: Arc<Memtable>) {
30        self.0.push((id, memtable));
31    }
32
33    pub fn remove(&mut self, id_to_remove: MemtableId) {
34        self.0.retain(|(id, _)| *id != id_to_remove);
35    }
36
37    pub fn iter(&self) -> impl DoubleEndedIterator<Item = &(MemtableId, Arc<Memtable>)> {
38        self.0.iter()
39    }
40
41    pub fn len(&self) -> usize {
42        self.0.len()
43    }
44}
45
46/// Hands out a unique (monotonically increasing) tree ID
47pub fn get_next_tree_id() -> TreeId {
48    static TREE_ID_COUNTER: AtomicU64 = AtomicU64::new(0);
49    TREE_ID_COUNTER.fetch_add(1, std::sync::atomic::Ordering::Relaxed)
50}
51
52#[allow(clippy::module_name_repetitions)]
53pub struct TreeInner {
54    /// Unique tree ID
55    pub id: TreeId,
56
57    /// Hands out a unique (monotonically increasing) segment ID
58    #[doc(hidden)]
59    pub segment_id_counter: Arc<AtomicU64>,
60
61    /// Active memtable that is being written to
62    pub(crate) active_memtable: Arc<RwLock<Arc<Memtable>>>,
63
64    /// Frozen memtables that are being flushed
65    pub(crate) sealed_memtables: Arc<RwLock<SealedMemtables>>,
66
67    /// Level manifest
68    #[doc(hidden)]
69    pub levels: Arc<RwLock<LevelManifest>>,
70
71    /// Tree configuration
72    pub config: Config,
73
74    /// Compaction may take a while; setting the signal to `true`
75    /// will interrupt the compaction and kill the worker.
76    pub(crate) stop_signal: StopSignal,
77
78    pub(crate) major_compaction_lock: RwLock<()>,
79}
80
81impl TreeInner {
82    pub(crate) fn create_new(config: Config) -> crate::Result<Self> {
83        let levels =
84            LevelManifest::create_new(config.level_count, config.path.join(LEVELS_MANIFEST_FILE))?;
85
86        Ok(Self {
87            id: get_next_tree_id(),
88            segment_id_counter: Arc::new(AtomicU64::default()),
89            config,
90            active_memtable: Arc::default(),
91            sealed_memtables: Arc::default(),
92            levels: Arc::new(RwLock::new(levels)),
93            stop_signal: StopSignal::default(),
94            major_compaction_lock: RwLock::default(),
95        })
96    }
97
98    pub fn get_next_segment_id(&self) -> SegmentId {
99        self.segment_id_counter
100            .fetch_add(1, std::sync::atomic::Ordering::Relaxed)
101    }
102}
103
104impl Drop for TreeInner {
105    fn drop(&mut self) {
106        log::debug!("Dropping TreeInner");
107
108        log::trace!("Sending stop signal to compactors");
109        self.stop_signal.send();
110    }
111}