Skip to main content

lsm_tree/
merge_operator.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::UserValue;
6use std::panic::RefUnwindSafe;
7
8/// A user-defined merge operator for commutative LSM operations.
9///
10/// Merge operators enable efficient read-modify-write operations by storing
11/// partial updates (operands) that are lazily combined during reads and
12/// compaction, avoiding the need for explicit read-modify-write cycles.
13///
14/// # Implementor contract
15///
16/// The merge function must be **deterministic and stable across multiple
17/// passes**. The `base_value` may itself be the result of a previous merge
18/// (e.g., from compaction or an earlier read resolution) rather than the
19/// original stored value. Repeated merging must produce identical bytes
20/// for the same logical state.
21///
22/// # Examples
23///
24/// A simple counter merge operator that sums integer operands:
25///
26/// ```
27/// use lsm_tree::{MergeOperator, UserValue};
28///
29/// struct CounterMerge;
30///
31/// impl MergeOperator for CounterMerge {
32///     fn merge(
33///         &self,
34///         _key: &[u8],
35///         base_value: Option<&[u8]>,
36///         operands: &[&[u8]],
37///     ) -> lsm_tree::Result<UserValue> {
38///         let mut counter: i64 = match base_value {
39///             Some(bytes) if bytes.len() == 8 => i64::from_le_bytes(
40///                 bytes.try_into().expect("checked length"),
41///             ),
42///             Some(_) => return Err(lsm_tree::Error::MergeOperator),
43///             None => 0,
44///         };
45///
46///         for operand in operands {
47///             if operand.len() != 8 {
48///                 return Err(lsm_tree::Error::MergeOperator);
49///             }
50///             counter += i64::from_le_bytes(
51///                 (*operand).try_into().expect("checked length"),
52///             );
53///         }
54///
55///         Ok(counter.to_le_bytes().to_vec().into())
56///     }
57/// }
58/// ```
59pub trait MergeOperator: Send + Sync + RefUnwindSafe + 'static {
60    /// Merges operands with an optional base value.
61    ///
62    /// `key` is the user key being merged.
63    ///
64    /// `base_value` is the existing value for the key, or `None` if no base
65    /// value exists (e.g., the key was never written or was deleted). This
66    /// may already be the output of a previous `merge` call (after compaction
67    /// or an earlier read), so implementations must be stable when re-merging.
68    ///
69    /// `operands` contains the merge operand values in ascending sequence
70    /// number order (chronological — oldest first).
71    ///
72    /// Returns the merged value on success.
73    ///
74    /// # Errors
75    ///
76    /// Returns [`crate::Error::MergeOperator`] if the merge fails (e.g., corrupted
77    /// operand data).
78    fn merge(
79        &self,
80        key: &[u8],
81        base_value: Option<&[u8]>,
82        operands: &[&[u8]],
83    ) -> crate::Result<UserValue>;
84}