Skip to main content

rushdb_storage/
derive.rs

1//! Apply-time derivation.
2//!
3//! Derived rows (presence, value dictionary, counters, declared-index
4//! entries) are computed by one deterministic [`Deriver`] from a commit's
5//! *base* ops plus the state before that commit, and written in the same
6//! backend batch at the same commit index. They are never logged in the WAL
7//! and never enter a transaction's write set, so they cannot cause write
8//! conflicts. Publication, command replay, and WAL replay (crash recovery,
9//! PITR, replicas) all call [`Store::derive_ops`], so every node derives the
10//! same rows from the same base log.
11
12use std::collections::BTreeMap;
13use std::ops::Bound;
14use std::sync::Arc;
15
16use crate::backend::StorageReadView;
17use crate::read_view::{ReadLimits, ScanProjection};
18use crate::{Op, StorageError, Store};
19
20/// Read access to the state *before* a commit, for a [`Deriver`].
21pub trait DeriveRead {
22    /// Current value of `key`, or `None` when absent.
23    ///
24    /// # Errors
25    /// Returns [`StorageError`] if the underlying read fails.
26    fn get(&self, key: &[u8]) -> Result<Option<Vec<u8>>, StorageError>;
27
28    /// Every live `(key, value)` whose key starts with `prefix`, in key order.
29    ///
30    /// # Errors
31    /// Returns [`StorageError`] if the underlying read fails.
32    fn range_prefix(&self, prefix: &[u8]) -> Result<Vec<(Vec<u8>, Vec<u8>)>, StorageError>;
33
34    /// Every live `(key, value)` with `start <= key < end`, in key order. An
35    /// empty `end` is unbounded above.
36    ///
37    /// # Errors
38    /// Returns [`StorageError`] if the underlying read fails.
39    fn range(&self, start: &[u8], end: &[u8]) -> Result<Vec<(Vec<u8>, Vec<u8>)>, StorageError>;
40}
41
42/// Rows of `map` in `[start, end)` (empty `end` = unbounded).
43fn map_range<'a, V: 'a>(
44    map: &'a BTreeMap<Vec<u8>, V>,
45    start: &[u8],
46    end: &[u8],
47) -> impl Iterator<Item = (&'a Vec<u8>, &'a V)> {
48    let upper = if end.is_empty() {
49        Bound::Unbounded
50    } else {
51        Bound::Excluded(end.to_vec())
52    };
53    map.range::<Vec<u8>, _>((Bound::Included(start.to_vec()), upper))
54}
55
56/// Deterministic derivation of derived rows from a commit's base ops.
57///
58/// Implementations must be pure functions of `(ci, base_ops, before)`: no
59/// clock, randomness, or global state, because replay and replicas call the
60/// same function and must reach byte-identical state.
61pub trait Deriver: Send + Sync {
62    /// Materialized derived ops (final values, not deltas) for one commit.
63    ///
64    /// # Errors
65    /// Returns [`StorageError`] when the base ops or the prior state are
66    /// inconsistent; the commit (or replay) then fails.
67    fn derive(
68        &self,
69        ci: u64,
70        base_ops: &[Op],
71        before: &dyn DeriveRead,
72    ) -> Result<Vec<Op>, StorageError>;
73
74    /// Whether `key` belongs to a derived family (so an overlay must derive
75    /// its staged writes before answering a read of it).
76    fn is_derived_key(&self, key: &[u8]) -> bool;
77
78    /// Whether a scan of `prefix` can reach derived keys.
79    fn prefix_reaches_derived(&self, prefix: &[u8]) -> bool;
80}
81
82impl DeriveRead for crate::ReadView {
83    fn get(&self, key: &[u8]) -> Result<Option<Vec<u8>>, StorageError> {
84        crate::ReadView::get(self, key)
85    }
86
87    fn range_prefix(&self, prefix: &[u8]) -> Result<Vec<(Vec<u8>, Vec<u8>)>, StorageError> {
88        crate::ReadView::range_prefix(self, prefix)
89    }
90
91    fn range(&self, start: &[u8], end: &[u8]) -> Result<Vec<(Vec<u8>, Vec<u8>)>, StorageError> {
92        crate::ReadView::range(self, start, end)
93    }
94}
95
96/// A read base plus a buffer of writes not yet visible in it (`None` =
97/// tombstone): the state before a commit inside a publish group.
98pub(crate) struct Layered<'a> {
99    pub(crate) base: &'a dyn DeriveRead,
100    pub(crate) buffer: &'a BTreeMap<Vec<u8>, Option<Vec<u8>>>,
101}
102
103impl DeriveRead for Layered<'_> {
104    fn get(&self, key: &[u8]) -> Result<Option<Vec<u8>>, StorageError> {
105        match self.buffer.get(key) {
106            Some(value) => Ok(value.clone()),
107            None => self.base.get(key),
108        }
109    }
110
111    fn range_prefix(&self, prefix: &[u8]) -> Result<Vec<(Vec<u8>, Vec<u8>)>, StorageError> {
112        let mut merged: BTreeMap<Vec<u8>, Vec<u8>> =
113            self.base.range_prefix(prefix)?.into_iter().collect();
114        let upper = crate::prefix_range(prefix).1;
115        let end = if upper.is_empty() {
116            Bound::Unbounded
117        } else {
118            Bound::Excluded(upper)
119        };
120        for (key, value) in self
121            .buffer
122            .range::<Vec<u8>, _>((Bound::Included(prefix.to_vec()), end))
123        {
124            match value {
125                Some(value) => {
126                    merged.insert(key.clone(), value.clone());
127                }
128                None => {
129                    merged.remove(key);
130                }
131            }
132        }
133        Ok(merged.into_iter().collect())
134    }
135
136    fn range(&self, start: &[u8], end: &[u8]) -> Result<Vec<(Vec<u8>, Vec<u8>)>, StorageError> {
137        let mut merged: BTreeMap<Vec<u8>, Vec<u8>> =
138            self.base.range(start, end)?.into_iter().collect();
139        for (key, value) in map_range(self.buffer, start, end) {
140            match value {
141                Some(value) => {
142                    merged.insert(key.clone(), value.clone());
143                }
144                None => {
145                    merged.remove(key);
146                }
147            }
148        }
149        Ok(merged.into_iter().collect())
150    }
151}
152
153/// Fold ops into a layered buffer (history is irrelevant to derivation).
154pub(crate) fn fold(buffer: &mut BTreeMap<Vec<u8>, Option<Vec<u8>>>, ops: &[Op]) {
155    for op in ops {
156        match op {
157            Op::Put(key, value) | Op::Set(key, value) => {
158                buffer.insert(key.clone(), Some(value.clone()));
159            }
160            Op::Del(key) | Op::Purge(key) => {
161                buffer.insert(key.clone(), None);
162            }
163        }
164    }
165}
166
167/// [`DeriveRead`] over one backend's current state.
168pub(crate) struct BackendRead(pub(crate) Box<dyn StorageReadView>);
169
170impl DeriveRead for BackendRead {
171    fn get(&self, key: &[u8]) -> Result<Option<Vec<u8>>, StorageError> {
172        self.0.read_value(
173            key,
174            u64::MAX,
175            true,
176            ReadLimits::default(),
177            ScanProjection::KeyValue,
178        )
179    }
180
181    fn range_prefix(&self, prefix: &[u8]) -> Result<Vec<(Vec<u8>, Vec<u8>)>, StorageError> {
182        let end = crate::prefix_range(prefix).1;
183        self.0
184            .current_range(
185                prefix,
186                &end,
187                ReadLimits::default(),
188                ScanProjection::KeyValue,
189            )?
190            .collect()
191    }
192
193    fn range(&self, start: &[u8], end: &[u8]) -> Result<Vec<(Vec<u8>, Vec<u8>)>, StorageError> {
194        self.0
195            .current_range(start, end, ReadLimits::default(), ScanProjection::KeyValue)?
196            .collect()
197    }
198}
199
200/// [`DeriveRead`] over an in-memory key map (command-replay shadow state).
201pub(crate) struct MapRead<'a>(pub(crate) &'a BTreeMap<Vec<u8>, Vec<u8>>);
202
203impl DeriveRead for MapRead<'_> {
204    fn get(&self, key: &[u8]) -> Result<Option<Vec<u8>>, StorageError> {
205        Ok(self.0.get(key).cloned())
206    }
207
208    fn range_prefix(&self, prefix: &[u8]) -> Result<Vec<(Vec<u8>, Vec<u8>)>, StorageError> {
209        Ok(self
210            .0
211            .range::<[u8], _>((Bound::Included(prefix), Bound::Unbounded))
212            .take_while(|(key, _)| key.starts_with(prefix))
213            .map(|(key, value)| (key.clone(), value.clone()))
214            .collect())
215    }
216
217    fn range(&self, start: &[u8], end: &[u8]) -> Result<Vec<(Vec<u8>, Vec<u8>)>, StorageError> {
218        Ok(map_range(self.0, start, end)
219            .map(|(key, value)| (key.clone(), value.clone()))
220            .collect())
221    }
222}
223
224/// [`DeriveRead`] over nothing (an empty prior state).
225pub(crate) struct Empty;
226
227impl DeriveRead for Empty {
228    fn get(&self, _key: &[u8]) -> Result<Option<Vec<u8>>, StorageError> {
229        Ok(None)
230    }
231
232    fn range_prefix(&self, _prefix: &[u8]) -> Result<Vec<(Vec<u8>, Vec<u8>)>, StorageError> {
233        Ok(Vec::new())
234    }
235
236    fn range(&self, _start: &[u8], _end: &[u8]) -> Result<Vec<(Vec<u8>, Vec<u8>)>, StorageError> {
237        Ok(Vec::new())
238    }
239}
240
241impl Store {
242    /// Install the apply-time deriver. Install it before any commit or replay
243    /// that must produce derived rows (see [`Store::open_with_deriver`]).
244    ///
245    /// Lock poisoning fences the store until reopen.
246    pub fn set_deriver(&self, deriver: Arc<dyn Deriver>) {
247        // WAL records replayed at open without a deriver applied base rows
248        // only; their derived rows are missing. Serving reads from that state
249        // would be silently wrong, so fence the store (reopen with the
250        // deriver installed, e.g. `rushdb_lmpg::open_store`).
251        if self
252            .underived_replay
253            .load(std::sync::atomic::Ordering::SeqCst)
254        {
255            self.health.fence();
256        }
257        *self.health.cleanup(self.deriver.write()) = Some(deriver);
258    }
259
260    /// **Fault injection for tests only.** Run `f` (typically one commit of
261    /// malformed base rows) with apply-time derivation suspended, so a test
262    /// can plant corruption the deriver would refuse and prove that readers
263    /// fail on it. The deriver is restored afterwards; derived rows for the
264    /// injected commit are intentionally absent.
265    #[doc(hidden)]
266    pub fn inject_without_derivation<T>(&self, f: impl FnOnce() -> T) -> T {
267        let saved = self.health.cleanup(self.deriver.write()).take();
268        let out = f();
269        *self.health.cleanup(self.deriver.write()) = saved;
270        out
271    }
272
273    pub(crate) fn deriver(&self) -> Option<Arc<dyn Deriver>> {
274        self.health.cleanup(self.deriver.read()).clone()
275    }
276
277    /// Base ops followed by their derived ops, or `None` when no deriver is
278    /// installed (the base ops are applied unchanged).
279    pub(crate) fn derive_ops(
280        deriver: Option<&Arc<dyn Deriver>>,
281        ci: u64,
282        base_ops: &[Op],
283        before: &dyn DeriveRead,
284    ) -> Result<Option<Vec<Op>>, StorageError> {
285        let Some(deriver) = deriver else {
286            return Ok(None);
287        };
288        let extra = deriver.derive(ci, base_ops, before)?;
289        if extra.is_empty() {
290            return Ok(None);
291        }
292        let mut all = Vec::with_capacity(base_ops.len() + extra.len());
293        all.extend_from_slice(base_ops);
294        all.extend(extra);
295        Ok(Some(all))
296    }
297}