1use crate::epoch::Epoch;
16use crate::memtable::Row;
17use crate::rowid::RowId;
18use std::collections::HashMap;
19
20const FANOUT: usize = 8;
22const BUFFER_CAP: usize = 16;
24const LEAF_CAP: usize = 32;
26
27type VKey = (RowId, Epoch);
29
30#[derive(Debug, Clone)]
32enum Message {
33 Upsert(Row),
34 Tombstone { row_id: RowId, epoch: Epoch },
35}
36
37impl Message {
38 fn key(&self) -> VKey {
39 match self {
40 Message::Upsert(r) => (r.row_id, r.committed_epoch),
41 Message::Tombstone { row_id, epoch } => (*row_id, *epoch),
42 }
43 }
44
45 fn to_row(&self) -> (Epoch, Row) {
46 match self {
47 Message::Upsert(r) => (r.committed_epoch, r.clone()),
48 Message::Tombstone { row_id, epoch } => (
49 *epoch,
50 Row {
51 row_id: *row_id,
52 committed_epoch: *epoch,
53 columns: HashMap::new(),
54 deleted: true,
55 },
56 ),
57 }
58 }
59}
60
61enum Node {
62 Leaf {
63 rows: Vec<Row>,
64 },
65 Internal {
66 keys: Vec<VKey>,
67 children: Vec<Node>,
68 buffer: Vec<Message>,
69 },
70}
71
72impl Node {
73 fn empty_leaf() -> Self {
74 Node::Leaf { rows: Vec::new() }
75 }
76}
77
78struct Split {
79 key: VKey,
80 node: Node,
81}
82
83pub struct BeTree {
85 root: Node,
86 mutations: usize,
87}
88
89impl Default for BeTree {
90 fn default() -> Self {
91 Self::new()
92 }
93}
94
95impl BeTree {
96 pub fn new() -> Self {
97 Self {
98 root: Node::empty_leaf(),
99 mutations: 0,
100 }
101 }
102
103 pub fn mutations(&self) -> usize {
105 self.mutations
106 }
107
108 pub fn is_empty(&self) -> bool {
109 self.mutations == 0
110 }
111
112 pub fn insert_row(&mut self, row: Row) {
114 self.insert(Message::Upsert(row));
115 }
116
117 pub fn delete(&mut self, row_id: RowId, epoch: Epoch) {
119 self.insert(Message::Tombstone { row_id, epoch });
120 }
121
122 fn insert(&mut self, msg: Message) {
123 self.mutations += 1;
124 match &mut self.root {
125 Node::Leaf { rows } => Self::leaf_apply(rows, msg),
126 Node::Internal { buffer, .. } => buffer.push(msg),
127 }
128 if let Some(split) = Self::maintain(&mut self.root) {
129 let left = std::mem::replace(&mut self.root, Node::empty_leaf());
130 self.root = Node::Internal {
131 keys: vec![split.key],
132 children: vec![left, split.node],
133 buffer: Vec::new(),
134 };
135 }
136 }
137
138 pub fn get(&self, row_id: RowId, snapshot: Epoch) -> Option<Row> {
141 self.get_version(row_id, snapshot).map(|(_, r)| r)
142 }
143
144 pub fn get_version(&self, row_id: RowId, snapshot: Epoch) -> Option<(Epoch, Row)> {
148 let mut best: Option<(Epoch, Row)> = None;
149 Self::collect(&self.root, row_id, snapshot, &mut best);
150 best
151 }
152
153 pub fn get_visible(&self, row_id: RowId, snapshot: Epoch) -> Option<Row> {
155 let r = self.get(row_id, snapshot)?;
156 if r.deleted {
157 None
158 } else {
159 Some(r)
160 }
161 }
162
163 pub fn versions(&self) -> Vec<Row> {
167 let mut out = Vec::with_capacity(self.mutations);
168 Self::collect_all_versions(&self.root, &mut out);
169 out
170 }
171
172 pub fn into_sorted_rows(mut self) -> Vec<Row> {
175 Self::flush_all(&mut self.root);
176 Self::collect_leaves(&self.root)
177 }
178
179 fn maintain(node: &mut Node) -> Option<Split> {
182 match node {
183 Node::Leaf { rows } => {
184 if rows.len() > LEAF_CAP {
185 Some(Self::split_leaf(rows))
186 } else {
187 None
188 }
189 }
190 Node::Internal {
191 keys,
192 children,
193 buffer,
194 } => {
195 if buffer.len() > BUFFER_CAP {
196 let drained = std::mem::take(buffer);
197 for msg in drained {
198 let i = Self::child_index(keys, msg.key());
199 Self::push_into_child(&mut children[i], msg);
200 }
201 let mut i = 0;
202 while i < children.len() {
203 if let Some(split) = Self::maintain(&mut children[i]) {
204 keys.insert(i, split.key);
205 children.insert(i + 1, split.node);
206 i += 1;
207 }
208 i += 1;
209 }
210 }
211 if children.len() > FANOUT {
212 Some(Self::split_internal(keys, children))
213 } else {
214 None
215 }
216 }
217 }
218 }
219
220 fn leaf_apply(rows: &mut Vec<Row>, msg: Message) {
221 let key = msg.key();
223 let row = match msg {
224 Message::Upsert(r) => r,
225 Message::Tombstone { row_id, epoch } => Row {
226 row_id,
227 committed_epoch: epoch,
228 columns: HashMap::new(),
229 deleted: true,
230 },
231 };
232 let i = rows.partition_point(|r| (r.row_id, r.committed_epoch) < key);
233 rows.insert(i, row);
234 }
235
236 fn push_into_child(child: &mut Node, msg: Message) {
237 match child {
238 Node::Leaf { rows } => Self::leaf_apply(rows, msg),
239 Node::Internal { buffer, .. } => buffer.push(msg),
240 }
241 }
242
243 fn child_index(keys: &[VKey], key: VKey) -> usize {
244 keys.partition_point(|k| *k <= key)
245 }
246
247 fn split_leaf(rows: &mut Vec<Row>) -> Split {
248 let mid = rows.len() / 2;
249 let right = rows.split_off(mid);
250 let key = (right[0].row_id, right[0].committed_epoch);
251 Split {
252 key,
253 node: Node::Leaf { rows: right },
254 }
255 }
256
257 fn split_internal(keys: &mut Vec<VKey>, children: &mut Vec<Node>) -> Split {
258 let m = keys.len() / 2;
259 let promoted = keys[m];
260 let right_keys = keys.split_off(m + 1);
261 keys.pop();
262 let right_children = children.split_off(m + 1);
263 Split {
264 key: promoted,
265 node: Node::Internal {
266 keys: right_keys,
267 children: right_children,
268 buffer: Vec::new(),
269 },
270 }
271 }
272
273 fn consider(best: &mut Option<(Epoch, Row)>, epoch: Epoch, row: Row) {
274 match best {
275 Some((be, _)) if *be >= epoch => {}
276 _ => *best = Some((epoch, row)),
277 }
278 }
279
280 fn collect(node: &Node, row_id: RowId, snapshot: Epoch, best: &mut Option<(Epoch, Row)>) {
281 match node {
282 Node::Leaf { rows } => {
283 let upper =
286 rows.partition_point(|r| (r.row_id, r.committed_epoch) <= (row_id, snapshot));
287 let mut i = upper;
288 while i > 0 {
289 let i2 = i - 1;
290 if rows[i2].row_id != row_id {
291 break;
292 }
293 let r = &rows[i2];
294 if r.committed_epoch <= snapshot {
295 Self::consider(best, r.committed_epoch, r.clone());
296 }
297 i = i2;
298 }
299 }
300 Node::Internal {
301 keys,
302 children,
303 buffer,
304 } => {
305 for msg in buffer.iter() {
306 let (rid, e) = msg.key();
307 if rid == row_id && e <= snapshot {
308 let (epoch, row) = msg.to_row();
309 Self::consider(best, epoch, row);
310 }
311 }
312 let i = Self::child_index(keys, (row_id, snapshot));
313 Self::collect(&children[i], row_id, snapshot, best);
314 }
315 }
316 }
317
318 fn flush_all(node: &mut Node) {
319 match node {
320 Node::Leaf { .. } => {}
321 Node::Internal {
322 keys,
323 children,
324 buffer,
325 } => {
326 let drained = std::mem::take(buffer);
327 for msg in drained {
328 let i = Self::child_index(keys, msg.key());
329 Self::push_into_child(&mut children[i], msg);
330 }
331 for c in children.iter_mut() {
332 Self::flush_all(c);
333 }
334 }
335 }
336 }
337
338 fn collect_leaves(node: &Node) -> Vec<Row> {
339 match node {
340 Node::Leaf { rows } => rows.clone(),
341 Node::Internal { children, .. } => {
342 children.iter().flat_map(Self::collect_leaves).collect()
343 }
344 }
345 }
346
347 fn collect_all_versions(node: &Node, out: &mut Vec<Row>) {
348 match node {
349 Node::Leaf { rows } => out.extend(rows.iter().cloned()),
350 Node::Internal {
351 children, buffer, ..
352 } => {
353 for msg in buffer {
354 out.push(msg.to_row().1);
355 }
356 for c in children {
357 Self::collect_all_versions(c, out);
358 }
359 }
360 }
361 }
362}
363
364#[cfg(test)]
365mod tests {
366 use super::*;
367 use crate::memtable::Value;
368
369 fn val_row(id: u64, epoch: u64, v: i64) -> Row {
370 Row::new(RowId(id), Epoch(epoch)).with_column(1, Value::Int64(v))
371 }
372
373 #[test]
374 fn point_lookups_round_trip() {
375 let mut t = BeTree::new();
376 for i in 0..50u64 {
377 t.insert_row(val_row(i, i, i as i64 * 10));
378 }
379 for i in 0..50u64 {
380 let r = t.get_visible(RowId(i), Epoch(100)).expect("row present");
381 assert_eq!(r.row_id, RowId(i));
382 assert!(matches!(r.columns.get(&1), Some(Value::Int64(v)) if *v == i as i64 * 10));
383 }
384 assert!(t.get_visible(RowId(500), Epoch(100)).is_none());
385 }
386
387 #[test]
388 fn many_inserts_force_depth_growth() {
389 let mut t = BeTree::new();
390 let n = 5_000u64;
391 for i in 0..n {
392 t.insert_row(val_row(i, i, i as i64));
393 }
394 for i in 0..n {
395 assert!(
396 t.get_visible(RowId(i), Epoch(n + 1)).is_some(),
397 "missing {i}"
398 );
399 }
400 assert_eq!(t.into_sorted_rows().len(), n as usize);
401 }
402
403 #[test]
404 fn multiple_versions_of_same_row_coexist_with_mvcc() {
405 let mut t = BeTree::new();
407 t.insert_row(val_row(7, 1, 100));
408 t.insert_row(val_row(7, 5, 200));
409 let old = t.get_visible(RowId(7), Epoch(2)).unwrap();
411 assert!(matches!(old.columns.get(&1), Some(Value::Int64(v)) if *v == 100));
412 let new = t.get_visible(RowId(7), Epoch(10)).unwrap();
413 assert!(matches!(new.columns.get(&1), Some(Value::Int64(v)) if *v == 200));
414 }
415
416 #[test]
417 fn tombstone_hides_row_at_and_after_epoch_but_not_before() {
418 let mut t = BeTree::new();
419 t.insert_row(val_row(3, 1, 42));
420 assert!(t.get_visible(RowId(3), Epoch(1)).is_some());
421 t.delete(RowId(3), Epoch(4));
422 assert!(t.get_visible(RowId(3), Epoch(4)).is_none());
423 assert!(t.get_visible(RowId(3), Epoch(9)).is_none());
424 assert!(t.get_visible(RowId(3), Epoch(3)).is_some());
426 }
427
428 #[test]
429 fn into_sorted_rows_is_keyed_by_row_then_epoch() {
430 let mut t = BeTree::new();
431 t.insert_row(val_row(30, 1, 1));
432 t.insert_row(val_row(10, 1, 1));
433 t.insert_row(val_row(30, 5, 2)); t.delete(RowId(10), Epoch(2));
435 let rows = t.into_sorted_rows();
436 let keys: Vec<(u64, u64)> = rows
437 .iter()
438 .map(|r| (r.row_id.0, r.committed_epoch.0))
439 .collect();
440 assert_eq!(keys, vec![(10, 1), (10, 2), (30, 1), (30, 5)]);
441 assert!(
442 rows.iter()
443 .find(|r| r.row_id == RowId(10) && r.committed_epoch == Epoch(2))
444 .unwrap()
445 .deleted
446 );
447 }
448
449 #[test]
455 fn many_versions_of_one_row_stay_lookupable_across_splits() {
456 let mut t = BeTree::new();
457 const N: u64 = 600;
458 for e in 0..N {
461 t.insert_row(val_row(7777, e, e as i64 * 2));
462 t.insert_row(val_row(e, e, 0));
465 }
466 assert_eq!(t.mutations(), 2 * N as usize);
467 for s in 0..N {
470 let r = t
471 .get_version(RowId(7777), Epoch(s))
472 .expect("missing version")
473 .0;
474 assert_eq!(r, Epoch(s), "snapshot {s} saw wrong newest version");
475 }
476 assert!(t.get_version(RowId(7777), Epoch(0)).is_some()); for e in 0..N {
480 assert!(t.get_visible(RowId(e), Epoch(e)).is_some(), "sibling {e}");
481 }
482
483 t.delete(RowId(7777), Epoch(N + 5));
486 assert!(
487 t.get_visible(RowId(7777), Epoch(N)).is_some(),
488 "before tombstone"
489 );
490 assert!(
491 t.get_visible(RowId(7777), Epoch(N + 5)).is_none(),
492 "at tombstone"
493 );
494 assert!(
495 t.get_visible(RowId(7777), Epoch(N + 99)).is_none(),
496 "after tombstone"
497 );
498 }
499}