Skip to main content

lsm_tree/compaction/leveled/
primitives.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright (c) 2026-present, Dmitry Prudnikov
3
4//! The four choices a compaction strategy makes for each level, each set on
5//! its own.
6//!
7//! Every compaction strategy answers four questions per level (Sarkar et al.,
8//! "Constructing and Analyzing the LSM Compaction Design Space", PVLDB 14(11)):
9//! when the level compacts (its [`Trigger`]), how its data lies (its
10//! [`Layout`]: one run, or runs that accumulate), how much one compaction out
11//! of it takes (its [`Granularity`]), and which data that is (its
12//! [`Movement`]). No combination is best for every workload, so each is a
13//! setting of its own. The leveled defaults are one combination of them.
14
15/// When a level compacts.
16#[derive(Clone, Copy, Debug, PartialEq, Eq)]
17pub enum Trigger {
18    /// When its runs reach `runs`, or its tables reach `tables`: a level of
19    /// runs that accumulate, read once per run, compacts by its shape.
20    ///
21    /// Runs are counted as a read meets them: tables that overlap no table of
22    /// a run join it, so batches of disjoint keys make one run, read once.
23    /// `tables` is what bounds such a level's size.
24    Shape {
25        /// The run count that triggers it.
26        runs: u8,
27        /// The table count that triggers it whatever the run count.
28        tables: u16,
29    },
30    /// When its bytes pass the target the level's place in the tree gives
31    /// it, by how far past.
32    Size,
33    /// Never on its own: the level only receives compactions. It takes no
34    /// free move either, into the level below or into the last.
35    Never,
36}
37
38/// How a level holds its data.
39#[derive(Clone, Copy, Debug, PartialEq, Eq)]
40pub enum Layout {
41    /// One run: a compaction into the level merges with the tables of it
42    /// that it overlaps.
43    Leveled,
44    /// Runs that accumulate: a compaction into the level takes in nothing of
45    /// it and writes its output beside the runs there. Output overlapping no
46    /// table of a run joins that run, as any level below L0 lays its tables
47    /// out: a read visits disjoint tables once, whichever run holds them.
48    Tiered,
49}
50
51/// How much one compaction out of a level takes.
52#[derive(Clone, Copy, Debug, PartialEq, Eq)]
53pub enum Granularity {
54    /// Every table of the level. With `through_full_next`, when the level it
55    /// compacts into is past its own target, that level goes along into the
56    /// one below it, so its bytes are not written twice in a row.
57    Level {
58        /// Whether a level past its target is compacted through.
59        through_full_next: bool,
60    },
61    /// The tables of the level a window of the next level's tables
62    /// contains, the window chosen by the level's [`Movement`].
63    Window,
64}
65
66/// Which data a compaction out of a level takes, when it takes part of it.
67#[derive(Clone, Copy, Debug, PartialEq, Eq)]
68pub enum Movement {
69    /// The window that rewrites least per byte it moves down, among those
70    /// that move no more than the level's overshoot plus `slack` bytes;
71    /// `None` = one table target.
72    LeastRewrite {
73        /// The bytes past the overshoot a window may move and still be
74        /// preferred.
75        slack: Option<u64>,
76    },
77    /// The window whose tables were read the most per byte rewritten: the
78    /// data reads touch is compacted first, and a cold range is left as it
79    /// is. Ties fall to the least rewrite, as [`Self::LeastRewrite`] ranks.
80    Hottest,
81}
82
83/// The four choices for one level.
84#[derive(Clone, Copy, Debug, PartialEq, Eq)]
85pub struct LevelPolicy {
86    /// When the level compacts.
87    pub trigger: Trigger,
88    /// How it holds its data.
89    pub layout: Layout,
90    /// How much one compaction out of it takes.
91    pub granularity: Granularity,
92    /// Which data that is.
93    pub movement: Movement,
94}