Skip to main content

didbot_data/mst/
edit.rs

1//! Changing one key of a built tree without building it again.
2//!
3//! A node covers the keys between two keys taller than it. Changing one key
4//! rewrites exactly the nodes whose range holds that key: one per layer, from
5//! the root down. Below the key's own layer, an insert splits each of those
6//! nodes in two around the key, and a remove joins the two nodes either side
7//! of it. Every other node is left where it is, shared with the tree before.
8//!
9//! Nothing here is written into the tree until the edit is done. An edit
10//! reads the nodes it needs, stores the ones it writes beside the tree, and
11//! then applies both at once, so a node it writes and then replaces again
12//! never reaches the tree at all.
13
14use std::collections::{BTreeMap, BTreeSet};
15
16use super::key_height;
17use super::tree::{Entry, Node, Tree};
18use crate::cid::Cid;
19use crate::dag_cbor;
20
21/// The blocks an edit added and took away.
22///
23/// For [`Tree::insert`] and [`Tree::remove`] these are tree nodes. A
24/// repository commit reports its record and commit blocks the same way.
25#[derive(Debug, Clone, Default, PartialEq, Eq)]
26pub struct Rewritten {
27    /// Blocks held now and not held before.
28    pub created: BTreeSet<Cid>,
29    /// Blocks held before and not held now.
30    pub removed: BTreeSet<Cid>,
31}
32
33impl Rewritten {
34    /// Folds a later edit into this one, so the two read as a single edit.
35    ///
36    /// A block the later edit creates that this one removed was there all
37    /// along, and a block it removes that this one created never was.
38    pub fn then(&mut self, later: Rewritten) {
39        for cid in later.created {
40            if !self.removed.remove(&cid) {
41                self.created.insert(cid);
42            }
43        }
44        for cid in later.removed {
45            if !self.created.remove(&cid) {
46                self.removed.insert(cid);
47            }
48        }
49    }
50}
51
52impl Tree {
53    /// Points `key` at `value`, adding the key if the tree does not hold it.
54    ///
55    /// The tree afterwards is the one [`Tree::build`] makes from the new leaf
56    /// set, node for node. Setting a key to the value it already names
57    /// changes nothing and answers an empty [`Rewritten`].
58    pub fn insert(&mut self, key: &str, value: Cid) -> Rewritten {
59        let height = key_height(key);
60        let key = key.as_bytes();
61        let mut edit = Edit::new(self);
62        let (top, layer) = if height > self.layer {
63            // Taller than every key held: the key becomes the root's only
64            // entry, and the old tree is cut in two either side of it. Each
65            // half is lifted to sit exactly one layer below the new root.
66            let empty = self
67                .nodes
68                .get(&self.root)
69                .is_some_and(|root| root.entries.is_empty() && root.left.is_none());
70            let old = if empty {
71                // The empty tree's root is replaced, not split.
72                edit.open(&self.root);
73                None
74            } else {
75                Some(self.root.clone())
76            };
77            let (low, high) = edit.split(old, self.layer, key);
78            let low = edit.lift(low, self.layer, height - 1);
79            let high = edit.lift(high, self.layer, height - 1);
80            let top = Open {
81                left: low,
82                entries: vec![(key.to_vec(), value, high)],
83            };
84            (top, height)
85        } else {
86            let root = edit.open(&self.root);
87            (
88                edit.insert(root, self.layer, key, value, height),
89                self.layer,
90            )
91        };
92        let root = edit.store(top);
93        let staged = edit.finish(root, layer);
94        self.apply(staged)
95    }
96
97    /// Takes `key` out of the tree, if the tree holds it.
98    ///
99    /// The tree afterwards is the one [`Tree::build`] makes from the leaf set
100    /// without the key. A key the tree does not hold changes nothing and
101    /// answers an empty [`Rewritten`].
102    pub fn remove(&mut self, key: &str) -> Rewritten {
103        if self.path(key).value.is_none() {
104            return Rewritten::default();
105        }
106        let height = key_height(key);
107        let mut edit = Edit::new(self);
108        let root = edit.open(&self.root);
109        let mut top = edit.remove(root, self.layer, key.as_bytes(), height);
110        // The root sits at the height of the tallest key left. A root with
111        // no entries is a layer nothing holds up any more, so the node under
112        // it takes its place, until one has entries or nothing is left.
113        let mut layer = self.layer;
114        while top.entries.is_empty() {
115            let Some(below) = top.left.take() else {
116                layer = 0;
117                break;
118            };
119            top = edit.open(&below);
120            layer = layer.saturating_sub(1);
121        }
122        let root = edit.store(top);
123        let staged = edit.finish(root, layer);
124        self.apply(staged)
125    }
126
127    /// Writes an edit's nodes into the tree and takes its removed ones out.
128    fn apply(&mut self, staged: Staged) -> Rewritten {
129        let Staged {
130            root,
131            layer,
132            mut written,
133            present,
134        } = staged;
135        let mut rewritten = Rewritten::default();
136        for (cid, present) in present {
137            if present {
138                if self.nodes.contains_key(&cid) {
139                    continue;
140                }
141                if let Some((node, block)) = written.remove(&cid) {
142                    self.nodes.insert(cid.clone(), node);
143                    self.blocks.insert(cid.clone(), block);
144                    rewritten.created.insert(cid);
145                }
146            } else if self.nodes.remove(&cid).is_some() {
147                self.blocks.remove(&cid);
148                rewritten.removed.insert(cid);
149            }
150        }
151        self.root = root;
152        self.layer = layer;
153        rewritten
154    }
155}
156
157/// A node with its keys written out whole, which is how an edit works on it.
158///
159/// A stored node writes each key as a prefix of the one before it, which is
160/// only meaningful while the entries stay where they are. An edit moves
161/// entries between nodes, so it opens a node into this, works on it, and
162/// closes it back into a [`Node`] that computes the prefixes afresh.
163#[derive(Debug, Default)]
164struct Open {
165    /// The subtree before the first entry.
166    left: Option<Cid>,
167    /// Each entry's whole key, its value, and the subtree to its right.
168    entries: Vec<(Vec<u8>, Cid, Option<Cid>)>,
169}
170
171impl Open {
172    /// Opens a stored node.
173    fn of(node: &Node) -> Self {
174        let mut entries: Vec<(Vec<u8>, Cid, Option<Cid>)> = Vec::with_capacity(node.entries.len());
175        for entry in &node.entries {
176            let previous: &[u8] = entries.last().map_or(&[], |(key, _, _)| key.as_slice());
177            let mut key = previous[..entry.prefix.min(previous.len())].to_vec();
178            key.extend_from_slice(&entry.suffix);
179            entries.push((key, entry.value.clone(), entry.tree.clone()));
180        }
181        Self {
182            left: node.left.clone(),
183            entries,
184        }
185    }
186
187    /// Closes it back into a node, each key written against the one before.
188    fn close(&self) -> Node {
189        let mut previous: &[u8] = &[];
190        let entries = self
191            .entries
192            .iter()
193            .map(|(key, value, tree)| {
194                let prefix = previous
195                    .iter()
196                    .zip(key)
197                    .take_while(|(left, right)| left == right)
198                    .count();
199                previous = key;
200                Entry {
201                    prefix,
202                    suffix: key[prefix..].to_vec(),
203                    value: value.clone(),
204                    tree: tree.clone(),
205                }
206            })
207            .collect();
208        Node {
209            left: self.left.clone(),
210            entries,
211        }
212    }
213
214    /// Whether it holds nothing at all, which is no node.
215    fn is_empty(&self) -> bool {
216        self.entries.is_empty() && self.left.is_none()
217    }
218
219    /// How many entries sort before `key`.
220    fn position(&self, key: &[u8]) -> usize {
221        self.entries
222            .partition_point(|(held, _, _)| held.as_slice() < key)
223    }
224
225    /// The subtree just before entry `at`: `left` for the first, and the
226    /// previous entry's right-hand subtree for every other.
227    fn slot(&self, at: usize) -> Option<Cid> {
228        match at.checked_sub(1) {
229            None => self.left.clone(),
230            Some(previous) => self.entries[previous].2.clone(),
231        }
232    }
233
234    /// Replaces the subtree just before entry `at`.
235    fn set_slot(&mut self, at: usize, subtree: Option<Cid>) {
236        match at.checked_sub(1) {
237            None => self.left = subtree,
238            Some(previous) => self.entries[previous].2 = subtree,
239        }
240    }
241}
242
243/// An edit in progress: the tree as it was, and what the edit has done.
244struct Edit<'t> {
245    tree: &'t Tree,
246    /// Every node the edit has stored, by CID, with its block.
247    written: BTreeMap<Cid, (Node, Vec<u8>)>,
248    /// Whether each node the edit touched is in the tree once it is done.
249    ///
250    /// Opening a node takes it out and storing one puts it in, in the order
251    /// they happen. A node taken out and then stored again identically is
252    /// still there, which is what an edit that changes nothing amounts to.
253    present: BTreeMap<Cid, bool>,
254}
255
256/// A finished edit, ready to be written into the tree.
257struct Staged {
258    root: Cid,
259    layer: usize,
260    written: BTreeMap<Cid, (Node, Vec<u8>)>,
261    present: BTreeMap<Cid, bool>,
262}
263
264impl<'t> Edit<'t> {
265    fn new(tree: &'t Tree) -> Self {
266        Self {
267            tree,
268            written: BTreeMap::new(),
269            present: BTreeMap::new(),
270        }
271    }
272
273    /// Takes a node out of the tree to be rewritten.
274    ///
275    /// # Panics
276    ///
277    /// If the tree links to a node it does not hold. Every link in a tree
278    /// names a node the same tree stored, and nothing outside this module
279    /// can change the nodes a tree holds.
280    fn open(&mut self, cid: &Cid) -> Open {
281        let node = self
282            .written
283            .get(cid)
284            .map(|(node, _)| node)
285            .or_else(|| self.tree.nodes.get(cid))
286            .expect("a tree links only to nodes it holds");
287        let open = Open::of(node);
288        self.present.insert(cid.clone(), false);
289        open
290    }
291
292    /// Encodes a node and puts it in the tree.
293    fn store(&mut self, open: Open) -> Cid {
294        let node = open.close();
295        let block = dag_cbor::encode(&node.to_value());
296        let cid = Cid::of_dag_cbor(&block);
297        self.present.insert(cid.clone(), true);
298        self.written.insert(cid.clone(), (node, block));
299        cid
300    }
301
302    /// [`Self::store`], or no subtree at all for a node that holds nothing.
303    fn store_unless_empty(&mut self, open: Open) -> Option<Cid> {
304        (!open.is_empty()).then(|| self.store(open))
305    }
306
307    /// Puts `key` into the node at `layer`, and answers the node.
308    fn insert(
309        &mut self,
310        mut node: Open,
311        layer: usize,
312        key: &[u8],
313        value: Cid,
314        height: usize,
315    ) -> Open {
316        let at = node.position(key);
317        if layer == height {
318            if let Some(held) = node.entries.get_mut(at).filter(|(held, _, _)| held == key) {
319                held.1 = value;
320                return node;
321            }
322            // The subtree that ran across the key's place is cut in two: the
323            // half before it stays in its slot, and the half after it hangs
324            // off the new entry.
325            let (low, high) = self.split(node.slot(at), layer.saturating_sub(1), key);
326            node.set_slot(at, low);
327            node.entries.insert(at, (key.to_vec(), value, high));
328            return node;
329        }
330        // Below this layer. A missing subtree is a node that holds nothing
331        // yet, one layer down, whether or not the key lands on that layer.
332        let below = match node.slot(at) {
333            Some(cid) => self.open(&cid),
334            None => Open::default(),
335        };
336        let below = self.insert(below, layer - 1, key, value, height);
337        let below = self.store(below);
338        node.set_slot(at, Some(below));
339        node
340    }
341
342    /// Cuts the subtree at `layer` into the keys before `key` and the keys
343    /// after it. `key` is taller than `layer`, so no entry here is `key`.
344    fn split(
345        &mut self,
346        subtree: Option<Cid>,
347        layer: usize,
348        key: &[u8],
349    ) -> (Option<Cid>, Option<Cid>) {
350        let Some(cid) = subtree else {
351            return (None, None);
352        };
353        let node = self.open(&cid);
354        let at = node.position(key);
355        let (low, high) = self.split(node.slot(at), layer.saturating_sub(1), key);
356        let mut after = node.entries;
357        let before: Vec<_> = after.drain(..at).collect();
358        let mut left = Open {
359            left: node.left,
360            entries: before,
361        };
362        left.set_slot(at, low);
363        let right = Open {
364            left: high,
365            entries: after,
366        };
367        (
368            self.store_unless_empty(left),
369            self.store_unless_empty(right),
370        )
371    }
372
373    /// Hangs a subtree at `from` under entry-less nodes until it sits at `to`.
374    fn lift(&mut self, subtree: Option<Cid>, from: usize, to: usize) -> Option<Cid> {
375        let mut subtree = subtree?;
376        for _ in from..to {
377            subtree = self.store(Open {
378                left: Some(subtree),
379                entries: Vec::new(),
380            });
381        }
382        Some(subtree)
383    }
384
385    /// Takes `key` out of the node at `layer`, and answers the node.
386    ///
387    /// The caller has established that the tree holds `key`.
388    fn remove(&mut self, mut node: Open, layer: usize, key: &[u8], height: usize) -> Open {
389        let at = node.position(key);
390        if layer == height {
391            debug_assert!(
392                node.entries.get(at).is_some_and(|(held, _, _)| held == key),
393                "the key is on its own layer"
394            );
395            // The two subtrees either side of the key become one.
396            let (_, _, after) = node.entries.remove(at);
397            let joined = self.merge(node.slot(at), after, layer.saturating_sub(1));
398            node.set_slot(at, joined);
399            return node;
400        }
401        let Some(below) = node.slot(at) else {
402            return node;
403        };
404        let below = self.open(&below);
405        let below = self.remove(below, layer - 1, key, height);
406        let below = self.store_unless_empty(below);
407        node.set_slot(at, below);
408        node
409    }
410
411    /// Joins two neighbouring subtrees at `layer`, every key of `low` sorting
412    /// before every key of `high`.
413    fn merge(&mut self, low: Option<Cid>, high: Option<Cid>, layer: usize) -> Option<Cid> {
414        let (low, high) = match (low, high) {
415            (None, other) | (other, None) => return other,
416            (Some(low), Some(high)) => (low, high),
417        };
418        let mut low = self.open(&low);
419        let high = self.open(&high);
420        // The last subtree of one and the first of the other meet in the
421        // middle, and are joined one layer down in turn.
422        let seam = low.entries.len();
423        let joined = self.merge(low.slot(seam), high.left, layer.saturating_sub(1));
424        low.set_slot(seam, joined);
425        low.entries.extend(high.entries);
426        Some(self.store(low))
427    }
428
429    /// Ends the edit with `root` at the top.
430    fn finish(self, root: Cid, layer: usize) -> Staged {
431        Staged {
432            root,
433            layer,
434            written: self.written,
435            present: self.present,
436        }
437    }
438}
439
440#[cfg(test)]
441mod tests {
442    use rand::rngs::StdRng;
443    use rand::{Rng, SeedableRng};
444
445    use super::*;
446
447    fn leaf(seed: &str) -> Cid {
448        Cid::of_dag_cbor(seed.as_bytes())
449    }
450
451    /// Asserts that `tree` is the tree [`Tree::build`] makes from `leaves`,
452    /// node for node, and that `rewritten` is the difference from `before`.
453    fn assert_built(
454        tree: &Tree,
455        leaves: &BTreeMap<String, Cid>,
456        before: &BTreeSet<Cid>,
457        rewritten: &Rewritten,
458        step: &str,
459    ) {
460        let built = Tree::build(leaves);
461        assert_eq!(tree.root(), built.root(), "{step}: the roots differ");
462        assert_eq!(tree.layer, built.layer, "{step}: the root layers differ");
463        assert_eq!(tree.blocks(), built.blocks(), "{step}: the blocks differ");
464        assert_eq!(tree.nodes(), built.nodes(), "{step}: the nodes differ");
465        let after: BTreeSet<Cid> = built.blocks().keys().cloned().collect();
466        let created: BTreeSet<Cid> = after.difference(before).cloned().collect();
467        let removed: BTreeSet<Cid> = before.difference(&after).cloned().collect();
468        assert_eq!(
469            rewritten.created, created,
470            "{step}: the created nodes differ"
471        );
472        assert_eq!(
473            rewritten.removed, removed,
474            "{step}: the removed nodes differ"
475        );
476    }
477
478    /// Keys whose heights run from zero to five, so a sequence of edits grows
479    /// the root, shrinks it, and splits and joins nodes on every layer.
480    ///
481    /// Random keys are almost all on layer zero: a quarter reach layer one
482    /// and one in a thousand layer five. The tall ones are mined from a
483    /// counter rather than waited for.
484    fn pool() -> Vec<String> {
485        let collections = [
486            "app.bsky.feed.post",
487            "app.bsky.feed.like",
488            "com.example.thing",
489        ];
490        let mut keys: Vec<String> = Vec::new();
491        let mut per_height = [0usize; 6];
492        let wanted = [40, 20, 10, 6, 4, 2];
493        let mut n = 0u64;
494        while per_height
495            .iter()
496            .zip(&wanted)
497            .any(|(have, want)| have < want)
498        {
499            let collection = collections[(n % 3) as usize];
500            let key = format!("{collection}/3l{n:011}");
501            let height = key_height(&key).min(5);
502            if per_height[height] < wanted[height] {
503                per_height[height] += 1;
504                keys.push(key);
505            }
506            n += 1;
507        }
508        // Literal keys, which sort among the TIDs differently.
509        keys.extend([
510            "app.bsky.actor.profile/self".to_owned(),
511            "bot.did.registration/self".to_owned(),
512        ]);
513        keys
514    }
515
516    /// Random inserts, overwrites and removes, compared against a rebuild
517    /// after every one.
518    ///
519    /// `DIDBOT_MST_SEEDS` runs more seeds than the default.
520    #[test]
521    fn every_edit_lands_on_the_tree_a_rebuild_makes() {
522        let seeds: u64 = std::env::var("DIDBOT_MST_SEEDS")
523            .ok()
524            .and_then(|seeds| seeds.parse().ok())
525            .unwrap_or(24);
526        let pool = pool();
527        let mut layers = BTreeSet::new();
528        for seed in 0..seeds {
529            let mut rng = StdRng::seed_from_u64(seed);
530            let mut leaves: BTreeMap<String, Cid> = BTreeMap::new();
531            let mut tree = Tree::build(&leaves);
532            // Fill towards the whole pool, then drain back to nothing, with
533            // the bias flipped halfway so both ends are reached.
534            for step in 0..600 {
535                let filling = step < 300;
536                let key = &pool[rng.random_range(0..pool.len())];
537                let before: BTreeSet<Cid> = tree.blocks().keys().cloned().collect();
538                let insert = if filling {
539                    rng.random_bool(0.8)
540                } else {
541                    rng.random_bool(0.2)
542                };
543                let rewritten = if insert {
544                    let value = leaf(&format!("{key}@{step}"));
545                    leaves.insert(key.clone(), value.clone());
546                    tree.insert(key, value)
547                } else {
548                    leaves.remove(key);
549                    tree.remove(key)
550                };
551                layers.insert(tree.layer);
552                let what = if insert { "insert" } else { "remove" };
553                assert_built(
554                    &tree,
555                    &leaves,
556                    &before,
557                    &rewritten,
558                    &format!("seed {seed} step {step}: {what} {key}"),
559                );
560            }
561            // And everything that is left, down to the empty tree.
562            for key in leaves.keys().cloned().collect::<Vec<_>>() {
563                let before: BTreeSet<Cid> = tree.blocks().keys().cloned().collect();
564                leaves.remove(&key);
565                let rewritten = tree.remove(&key);
566                assert_built(
567                    &tree,
568                    &leaves,
569                    &before,
570                    &rewritten,
571                    &format!("seed {seed}: draining {key}"),
572                );
573            }
574            assert_eq!(tree.root(), Tree::build(&BTreeMap::new()).root());
575        }
576        // The root moved through every layer the pool reaches, so growing
577        // and shrinking it were both exercised rather than assumed.
578        assert_eq!(
579            layers,
580            (0..=5).collect(),
581            "the root layers the edits reached"
582        );
583    }
584
585    /// Every key of the pool, inserted in every order a seed picks, gives one
586    /// tree. The order the keys arrive in cannot reach the shape.
587    #[test]
588    fn insertion_order_does_not_reach_the_root() {
589        let pool = pool();
590        let leaves: BTreeMap<String, Cid> =
591            pool.iter().map(|key| (key.clone(), leaf(key))).collect();
592        let built = Tree::build(&leaves);
593        for seed in 0..8 {
594            let mut rng = StdRng::seed_from_u64(seed);
595            let mut order = pool.clone();
596            for i in (1..order.len()).rev() {
597                order.swap(i, rng.random_range(0..=i));
598            }
599            let mut tree = Tree::build(&BTreeMap::new());
600            for key in &order {
601                tree.insert(key, leaf(key));
602            }
603            assert_eq!(tree.blocks(), built.blocks(), "seed {seed}");
604            assert_eq!(tree.root(), built.root(), "seed {seed}");
605        }
606    }
607
608    /// Setting a key to what it already names, or removing a key the tree
609    /// does not hold, is no edit at all.
610    #[test]
611    fn an_edit_that_changes_nothing_rewrites_nothing() {
612        let pool = pool();
613        let leaves: BTreeMap<String, Cid> =
614            pool.iter().map(|key| (key.clone(), leaf(key))).collect();
615        let mut tree = Tree::build(&leaves);
616        let root = tree.root().clone();
617        assert_eq!(tree.insert(&pool[7], leaf(&pool[7])), Rewritten::default());
618        assert_eq!(
619            tree.remove("com.example.thing/absent"),
620            Rewritten::default()
621        );
622        assert_eq!(tree.root(), &root);
623    }
624}