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