Skip to main content

reddb_server/storage/engine/btree/
impl.rs

1use super::*;
2use crate::storage::engine::overflow::OverflowChain;
3
4impl BTree {
5    /// If `stored` is a pointer cell (slice E layout), walk the overflow
6    /// chain anchored at it and return every page to the free list.
7    /// No-op for inline cells (raw or compressed) and for empty cells.
8    ///
9    /// Used by the B-tree paths that drop or overwrite a pointer cell —
10    /// `delete` and the shrinking branches of `upsert` /
11    /// `upsert_batch_sorted` (slice F of PRD #662). At the B-tree layer
12    /// this is synchronous: a higher transactional layer that needs
13    /// MVCC-aware reclamation must defer freeing through its own
14    /// snapshot machinery rather than calling these primitives during a
15    /// rollback-capable window.
16    fn free_chain_if_pointer(&self, stored: &[u8]) -> BTreeResult<()> {
17        if let Some(head) = value_layout::pointer_head(stored) {
18            OverflowChain::new(&self.pager)
19                .free(head)
20                .map_err(value_layout::ValueLayoutError::from)?;
21        }
22        Ok(())
23    }
24    /// Create a new B+ tree using the given pager
25    pub fn new(pager: Arc<Pager>) -> Self {
26        Self {
27            pager,
28            root_page_id: RwLock::new(0),
29            rightmost_leaf: RwLock::new(None),
30            writer: parking_lot::ReentrantMutex::new(()),
31        }
32    }
33
34    /// Create a B+ tree with an existing root
35    pub fn with_root(pager: Arc<Pager>, root_page_id: u32) -> Self {
36        Self {
37            pager,
38            root_page_id: RwLock::new(root_page_id),
39            rightmost_leaf: RwLock::new(None),
40            writer: parking_lot::ReentrantMutex::new(()),
41        }
42    }
43
44    /// Get the root page ID
45    pub fn root_page_id(&self) -> u32 {
46        *self
47            .root_page_id
48            .read()
49            .unwrap_or_else(|poisoned| poisoned.into_inner())
50    }
51
52    /// Check if tree is empty
53    pub fn is_empty(&self) -> bool {
54        self.root_page_id() == 0
55    }
56
57    /// Get value for a key
58    pub fn get(&self, key: &[u8]) -> BTreeResult<Option<Vec<u8>>> {
59        let root_id = self.root_page_id();
60        if root_id == 0 {
61            return Ok(None);
62        }
63
64        // Descend from root to the target leaf
65        let (leaf_id, _) = self.find_leaf(root_id, key)?;
66
67        // D3 — Lehman-Yao right-link traversal.
68        //
69        // A concurrent insert may have split the leaf between our descent
70        // and this read. If the key we're searching for is greater than the
71        // current page's high_key (max stored key), the key lives on a
72        // right-sibling page — follow the right-link instead of re-descending
73        // from the root. Cap at MAX_RIGHTLINK_HOPS to guard against corrupt
74        // link chains.
75        const MAX_RIGHTLINK_HOPS: usize = 32;
76        let mut current_id = leaf_id;
77        for _ in 0..MAX_RIGHTLINK_HOPS {
78            let page = self.pager.read_page(current_id)?;
79
80            match search_leaf(&page, key)? {
81                SearchResult::Found(pos) => {
82                    let (_, stored) = read_leaf_cell(&page, pos)?;
83                    let value = value_layout::decode(&self.pager, &stored)?;
84                    return Ok(Some(value));
85                }
86                SearchResult::NotFound(_) => {
87                    // Check if a split pushed our key to the right sibling
88                    let right = leaf_right_sibling(&page);
89                    if right != 0 {
90                        if let Some(high_key) = leaf_high_key(&page)? {
91                            if key > high_key.as_slice() {
92                                // Key belongs on a right sibling — follow link
93                                current_id = right;
94                                continue;
95                            }
96                        }
97                    }
98                    // Key genuinely not present
99                    return Ok(None);
100                }
101            }
102        }
103
104        // Exhausted hop limit — fall back to root descent (corrupt link chain)
105        let (leaf_id, _) = self.find_leaf(self.root_page_id(), key)?;
106        let page = self.pager.read_page(leaf_id)?;
107        match search_leaf(&page, key)? {
108            SearchResult::Found(pos) => {
109                let (_, stored) = read_leaf_cell(&page, pos)?;
110                let value = value_layout::decode(&self.pager, &stored)?;
111                Ok(Some(value))
112            }
113            SearchResult::NotFound(_) => Ok(None),
114        }
115    }
116
117    /// Insert a key-value pair.
118    ///
119    /// Applies the value-layout spill ladder before touching the tree:
120    /// the 256 MB ceiling rejects here without any allocation; values
121    /// at or below `OVERFLOW_THRESHOLD` go inline raw; larger values may
122    /// inline compressed or spill into an [`crate::storage::engine::overflow::OverflowChain`].
123    /// The encoded bytes are then committed through [`Self::insert_encoded`].
124    pub fn insert(&self, key: &[u8], value: &[u8]) -> BTreeResult<()> {
125        let _writer = self.writer.lock();
126        if key.len() > MAX_KEY_SIZE {
127            return Err(BTreeError::KeyTooLarge(key.len()));
128        }
129        let stored = value_layout::encode(&self.pager, value)?;
130        self.insert_encoded(key, &stored)
131    }
132
133    /// Insert a key with bytes already produced by [`value_layout::encode`].
134    ///
135    /// Internal entry point used by `bulk_insert_sorted` so its fast
136    /// path can pre-encode the whole batch (eagerly allocating overflow
137    /// pages, surfacing oversize rows up front) and still re-use the
138    /// generic insert machinery for fallback. Public callers should go
139    /// through [`Self::insert`] which encodes for them.
140    pub(crate) fn insert_encoded(&self, key: &[u8], value: &[u8]) -> BTreeResult<()> {
141        let _writer = self.writer.lock();
142        let root_id = self.root_page_id();
143
144        // Empty tree - create root leaf
145        if root_id == 0 {
146            let mut page = self.pager.allocate_page(PageType::BTreeLeaf)?;
147            clear_leaf_cells(&mut page);
148            insert_into_leaf(&mut page, key, value)?;
149            init_leaf_links(&mut page, 0, 0);
150            page.update_checksum();
151            let new_root = page.page_id();
152            self.pager.write_page(new_root, page)?;
153            *self.root_page_id.write().map_err(|e| {
154                BTreeError::LockPoisoned(format!("insert: root_page_id write lock: {e}"))
155            })? = new_root;
156            return Ok(());
157        }
158
159        // D2 fastpath: if key > cached rightmost high_key, go directly to
160        // the cached leaf without descending from the root. This cuts
161        // tree descent entirely for monotonic append workloads (timeseries,
162        // auto-increment entity IDs).
163        let cached = {
164            self.rightmost_leaf
165                .read()
166                .map_err(|e| BTreeError::LockPoisoned(format!("rightmost_leaf read: {e}")))?
167                .clone()
168        };
169        if let Some((cached_page_id, ref high_key)) = cached {
170            if key > high_key.as_slice() {
171                let mut page = self.pager.read_page(cached_page_id)?;
172                // Confirm this page is still a valid leaf with a right-link
173                // of 0 (i.e., it IS the rightmost leaf — not mid-tree after
174                // a concurrent split moved the boundary).
175                let right_sibling = leaf_right_sibling(&page);
176                if right_sibling == 0 {
177                    // Still the rightmost leaf. Try fast append.
178                    if can_insert_leaf(&page, key, value) {
179                        insert_into_leaf(&mut page, key, value)?;
180                        // Skip update_checksum here — pager.write_page
181                        // below recomputes it. Saves one CRC32 on 16 KiB
182                        // per insert on the hot append path.
183                        let page_id = page.page_id();
184                        // Update cached high_key
185                        *self.rightmost_leaf.write().map_err(|e| {
186                            BTreeError::LockPoisoned(format!("rightmost_leaf write: {e}"))
187                        })? = Some((page_id, key.to_vec()));
188                        self.pager.write_page(page_id, page)?;
189                        return Ok(());
190                    }
191                    // Leaf is full — split. After split, invalidate cache
192                    // (the new rightmost leaf is the split result).
193                    let (new_leaf, separator_key) = self.split_leaf(&mut page, key, value)?;
194                    let new_leaf_id = new_leaf.page_id();
195                    page.update_checksum();
196                    let page_id = page.page_id();
197                    self.pager.write_page(page_id, page.clone())?;
198                    // Cache the new rightmost leaf after split
199                    self.cache_rightmost_leaf(&new_leaf)?;
200                    // Get parent path for the split propagation.
201                    // The left child is the original cached leaf.
202                    let (_, path) = self.find_leaf(root_id, &separator_key)?;
203                    self.insert_into_parent(path, cached_page_id, &separator_key, new_leaf_id)?;
204                    return Ok(());
205                }
206                // right_sibling != 0: cached page is no longer rightmost.
207                // Fall through to the full find_leaf path below and
208                // refresh the cache.
209                *self.rightmost_leaf.write().map_err(|e| {
210                    BTreeError::LockPoisoned(format!("rightmost_leaf write: {e}"))
211                })? = None;
212            }
213        }
214
215        // Find the leaf and path to it
216        let (leaf_id, path) = self.find_leaf(root_id, key)?;
217        let mut page = self.pager.read_page(leaf_id)?;
218
219        // Check for duplicate
220        if let SearchResult::Found(_) = search_leaf(&page, key)? {
221            return Err(BTreeError::DuplicateKey);
222        }
223
224        // Try to insert into leaf
225        if can_insert_leaf(&page, key, value) {
226            insert_into_leaf(&mut page, key, value)?;
227            // Skip update_checksum — pager.write_page recomputes it.
228            let page_id = page.page_id();
229            // If this is the rightmost leaf (right_sibling == 0), cache it.
230            if leaf_right_sibling(&page) == 0 {
231                self.cache_rightmost_leaf(&page)?;
232            }
233            self.pager.write_page(page_id, page)?;
234            return Ok(());
235        }
236
237        // Need to split the leaf
238        let (new_leaf, separator_key) = self.split_leaf(&mut page, key, value)?;
239        let new_leaf_id = new_leaf.page_id();
240        page.update_checksum();
241        let page_id = page.page_id();
242        self.pager.write_page(page_id, page.clone())?;
243        // Cache the new rightmost leaf (split result gets the larger keys)
244        self.cache_rightmost_leaf(&new_leaf)?;
245
246        // Propagate split up the tree
247        self.insert_into_parent(path, page_id, &separator_key, new_leaf_id)?;
248
249        Ok(())
250    }
251
252    /// Insert or update a key. If the key already exists and the new
253    /// value has the same length as the old, the value bytes are
254    /// overwritten in place — no structural changes, no rebalance,
255    /// one leaf page write.
256    ///
257    /// Falls back to `delete + insert` when the key is missing or the
258    /// new value has a different length. Callers like
259    /// `persist_entities_to_pager` that re-serialize a mutated entity
260    /// with a fixed-width schema almost always hit the fast path,
261    /// eliminating the `BTree::delete + rebalance` cost that previously
262    /// dominated UPDATE workloads (~50% of `bulk_update` CPU).
263    pub fn upsert(&self, key: &[u8], value: &[u8]) -> BTreeResult<()> {
264        let _writer = self.writer.lock();
265        let root_id = self.root_page_id();
266        if root_id == 0 {
267            return self.insert(key, value);
268        }
269
270        // Encode the new value once. The in-place fast path compares
271        // encoded lengths so the leaf cell stays in the on-disk format
272        // documented in `value_layout`; without this the in-place write
273        // would scribble raw bytes over a flag-prefixed cell.
274        let new_stored = value_layout::encode(&self.pager, value)?;
275
276        let (leaf_id, _path) = self.find_leaf(root_id, key)?;
277        let mut page = self.pager.read_page(leaf_id)?;
278
279        if let SearchResult::Found(pos) = search_leaf(&page, key)? {
280            let (_, old_value) = read_leaf_cell(&page, pos)?;
281            if new_stored.len() <= old_value.len() {
282                // Shrinking (or same-size) overwrite. If the old cell
283                // owned an overflow chain, free it before the new bytes
284                // overwrite the pointer header — otherwise the chain
285                // is unreachable but still allocated (slice F).
286                self.free_chain_if_pointer(&old_value)?;
287                overwrite_leaf_value(&mut page, pos, &new_stored)?;
288                let page_id = page.page_id();
289                self.pager.write_page(page_id, page)?;
290                return Ok(());
291            }
292            // Grow case: fall through to delete+insert for a proper
293            // reallocation + potential rebalance.
294            let _ = self.delete(key);
295            return self.insert_encoded(key, &new_stored);
296        }
297
298        // Key is new — plain insert path. Reuse the bytes we already
299        // encoded above instead of re-running LZ4 / re-allocating.
300        self.insert_encoded(key, &new_stored)
301    }
302
303    /// Batch upsert for sorted keys landing in a small set of leaves.
304    ///
305    /// Walks to each leaf once, applies every same-or-shrink in-place
306    /// overwrite that belongs there, writes the page once, then moves
307    /// on. Keys that miss or whose new value grows fall back to the
308    /// generic single-key `upsert` path — correctness identical,
309    /// structural re-layout handled there.
310    ///
311    /// Callers pass lex-sorted `(key, value)` pairs. For the entity
312    /// UPDATE path, IDs are big-endian so u64 ordering = lex ordering.
313    pub fn upsert_batch_sorted(&self, items: &[(Vec<u8>, Vec<u8>)]) -> BTreeResult<()> {
314        let _writer = self.writer.lock();
315        if items.is_empty() {
316            return Ok(());
317        }
318        let root_id = self.root_page_id();
319        if root_id == 0 {
320            for (k, v) in items {
321                self.upsert(k, v)?;
322            }
323            return Ok(());
324        }
325
326        let mut current_leaf: Option<(u32, Page, bool)> = None; // (leaf_id, page, dirty)
327        for (key, value) in items {
328            // Locate the leaf for this key. If it matches the leaf we
329            // already have in hand, reuse the in-memory page.
330            let (leaf_id, _path) = self.find_leaf(root_id, key)?;
331            if current_leaf.as_ref().map(|(id, _, _)| *id) != Some(leaf_id) {
332                if let Some((id, page, dirty)) = current_leaf.take() {
333                    if dirty {
334                        self.pager.write_page(id, page)?;
335                    }
336                }
337                let page = self.pager.read_page(leaf_id)?;
338                current_leaf = Some((leaf_id, page, false));
339            }
340
341            // Encode the new value through the spill ladder so the
342            // length comparison is between encoded bytes (matching the
343            // on-disk cell format). Compute lazily: only the in-place
344            // branch needs it, and an oversize value gets caught by the
345            // single-key `upsert` fallback below.
346            let applied_in_place = {
347                let (_, page, dirty) = current_leaf.as_mut().expect("set above");
348                if let SearchResult::Found(pos) = search_leaf(page, key)? {
349                    let (_, old_value) = read_leaf_cell(page, pos)?;
350                    match value_layout::encode(&self.pager, value) {
351                        Ok(new_stored) if new_stored.len() <= old_value.len() => {
352                            // Shrinking overwrite: free the old chain
353                            // before the pointer header is scribbled over
354                            // (slice F).
355                            self.free_chain_if_pointer(&old_value)?;
356                            overwrite_leaf_value(page, pos, &new_stored)?;
357                            *dirty = true;
358                            true
359                        }
360                        _ => false,
361                    }
362                } else {
363                    false
364                }
365            };
366            if !applied_in_place {
367                if let Some((id, page, dirty)) = current_leaf.take() {
368                    if dirty {
369                        self.pager.write_page(id, page)?;
370                    }
371                }
372                self.upsert(key, value)?;
373            }
374        }
375
376        if let Some((id, page, dirty)) = current_leaf.take() {
377            if dirty {
378                self.pager.write_page(id, page)?;
379            }
380        }
381        Ok(())
382    }
383
384    /// Bulk insert for sorted key-value pairs.
385    ///
386    /// Optimized for monotonically increasing keys (e.g. entity IDs):
387    /// - Walks to the target leaf ONCE, then appends many entries
388    ///   before re-walking.
389    /// - Writes each leaf only once per batch (amortized over many inserts).
390    ///
391    /// Falls back to per-entity `insert` on splits.
392    pub fn bulk_insert_sorted(&self, items: &[(Vec<u8>, Vec<u8>)]) -> BTreeResult<()> {
393        let _writer = self.writer.lock();
394        if items.is_empty() {
395            return Ok(());
396        }
397        // Key check is a hard fail (oversized keys would corrupt the
398        // ordering invariant). Value oversize, by contrast, is skipped
399        // per-row so a single huge row in the middle of a batch cannot
400        // poison the rest — the caller still gets a signal because we
401        // return the first ValueTooLarge after the rest have landed.
402        for (key, _) in items {
403            if key.len() > MAX_KEY_SIZE {
404                return Err(BTreeError::KeyTooLarge(key.len()));
405            }
406        }
407
408        // Pre-encode every value through the spill ladder. Oversized
409        // rows are dropped from the encoded batch but remembered so we
410        // can surface the first violation at the end of the call.
411        // Encoding here also allocates overflow pages eagerly, which
412        // mirrors single-key `insert` behaviour and keeps the bulk fast
413        // path below opaque to value shape.
414        let mut encoded: Vec<(&[u8], Vec<u8>)> = Vec::with_capacity(items.len());
415        let mut first_oversize: Option<usize> = None;
416        for (key, value) in items {
417            match value_layout::encode(&self.pager, value) {
418                Ok(stored) => encoded.push((key.as_slice(), stored)),
419                Err(value_layout::ValueLayoutError::ValueTooLarge(n)) => {
420                    if first_oversize.is_none() {
421                        first_oversize = Some(n);
422                    }
423                }
424                Err(other) => return Err(other.into()),
425            }
426        }
427
428        let items: Vec<(&[u8], &[u8])> = encoded.iter().map(|(k, v)| (*k, v.as_slice())).collect();
429
430        // After a filled leaf is written to disk, the next ascending
431        // key almost certainly belongs on the right-sibling leaf
432        // (leaves are linked via `leaf_right_sibling`). Cache the
433        // just-written leaf id so the next outer iteration can hop
434        // the sibling pointer in one page read instead of a full
435        // root-to-leaf walk. Any fallback to `self.insert` (which
436        // may split the tree) invalidates the hint.
437        let mut last_filled_leaf: Option<u32> = None;
438        let mut i = 0;
439        while i < items.len() {
440            let root_id = self.root_page_id();
441            if root_id == 0 {
442                // Empty tree — use single insert to create root.
443                // Bytes are already encoded, so go through the internal
444                // entry point that skips the spill ladder.
445                let (k, v) = items[i];
446                self.insert_encoded(k, v)?;
447                i += 1;
448                last_filled_leaf = None;
449                continue;
450            }
451
452            // Try the right-sibling hop first. Valid when: (a) we have
453            // a cached last-filled leaf, (b) it points to a non-zero
454            // sibling, (c) the current key > sibling's last key (or
455            // the sibling is empty). Any mismatch → fall back to
456            // `find_leaf`.
457            let hint_leaf_id: Option<u32> = if let Some(prev) = last_filled_leaf {
458                let prev_page = self.pager.read_page(prev)?;
459                let sibling = leaf_right_sibling(&prev_page);
460                if sibling != 0 {
461                    let sib_page = self.pager.read_page(sibling)?;
462                    match leaf_high_key(&sib_page)? {
463                        None => Some(sibling), // empty sibling — free real estate
464                        Some(hk)
465                            if leaf_right_sibling(&sib_page) == 0 && items[i].0 > hk.as_slice() =>
466                        {
467                            Some(sibling)
468                        }
469                        Some(_) => None,
470                    }
471                } else {
472                    None
473                }
474            } else {
475                None
476            };
477
478            let (leaf_id, _path) = match hint_leaf_id {
479                Some(id) => (id, Vec::new()),
480                None => self.find_leaf(root_id, items[i].0)?,
481            };
482            let mut page = self.pager.read_page(leaf_id)?;
483
484            // Snapshot the leaf's last key (via the O(1) slot lookup)
485            // and current free bytes. Both drive the append fast path
486            // below. The old O(M) forward-walk that this block used to
487            // do was made redundant by the slotted layout.
488            let mut last_key_in_leaf: Option<Vec<u8>> = {
489                let cell_count = page.cell_count() as usize;
490                if cell_count == 0 {
491                    None
492                } else {
493                    Some(read_leaf_cell(&page, cell_count - 1)?.0)
494                }
495            };
496
497            // Fast path for monotonically-ascending keys: append new
498            // cells at the tail of the cell-data area and push new
499            // slot pointers onto the slot array. Every key must be
500            // strictly greater than `last_key_in_leaf` and must still
501            // fit in the leaf's free bytes.
502            let mut inserted = 0usize;
503            while i + inserted < items.len() {
504                let (key, value) = items[i + inserted];
505
506                // Strictly-ascending check, doubles as duplicate
507                // detection without a leaf binary search.
508                if let Some(lk) = &last_key_in_leaf {
509                    match key.cmp(lk.as_slice()) {
510                        Ordering::Greater => {}
511                        Ordering::Equal => return Err(BTreeError::DuplicateKey),
512                        Ordering::Less => break,
513                    }
514                }
515
516                // Free-space check: account for every slot appended in this
517                // batch even though `page.cell_count()` is only updated once
518                // at the end. Otherwise we can overrun the slot array into the
519                // cell area near the end of the page and persist bad offsets.
520                let cell_size = 4 + key.len() + value.len();
521                let slot_end_after =
522                    LEAF_SLOT_ARRAY_OFFSET + (page.cell_count() as usize + inserted + 1) * 2;
523                let cells_start = leaf_cells_start(&page);
524                if slot_end_after + cell_size > cells_start {
525                    break;
526                }
527
528                // Append the cell at the tail of the cell-data area
529                // and push a new slot pointer. `page.cell_count()`
530                // still reflects the state before this inserted batch,
531                // so the new slot index is `cell_count + inserted`.
532                let cell_offset = cells_start - cell_size;
533                {
534                    let data = page.as_bytes_mut();
535                    data[cell_offset..cell_offset + 2]
536                        .copy_from_slice(&(key.len() as u16).to_le_bytes());
537                    data[cell_offset + 2..cell_offset + 4]
538                        .copy_from_slice(&(value.len() as u16).to_le_bytes());
539                    data[cell_offset + 4..cell_offset + 4 + key.len()].copy_from_slice(key);
540                    data[cell_offset + 4 + key.len()..cell_offset + cell_size]
541                        .copy_from_slice(value);
542                }
543                page.set_free_end(cell_offset as u16);
544
545                let new_slot_index = page.cell_count() as usize + inserted;
546                let slot_pos = leaf_slot_offset_for(new_slot_index);
547                {
548                    let data = page.as_bytes_mut();
549                    data[slot_pos..slot_pos + 2]
550                        .copy_from_slice(&(cell_offset as u16).to_le_bytes());
551                }
552
553                last_key_in_leaf = Some(key.to_vec());
554                inserted += 1;
555            }
556
557            if inserted > 0 {
558                let new_count = page.cell_count() as usize + inserted;
559                page.set_cell_count(new_count as u16);
560                page.set_free_start((LEAF_SLOT_ARRAY_OFFSET + new_count * 2) as u16);
561                page.update_checksum();
562                self.cache_rightmost_leaf(&page)?;
563                self.pager.write_page(leaf_id, page)?;
564                i += inserted;
565                // Remember this leaf; the next outer iteration will
566                // try its right sibling before re-walking from root.
567                last_filled_leaf = Some(leaf_id);
568            } else {
569                // Couldn't fit even one entry via the fast path —
570                // either the leaf is full, or the next key belongs
571                // in the middle of the leaf. Delegate to the generic
572                // insert path which handles both splitting and
573                // mid-leaf positioning. Splits can invalidate the
574                // leaf-chain topology so drop the hint.
575                let (k, v) = items[i];
576                self.insert_encoded(k, v)?;
577                i += 1;
578                last_filled_leaf = None;
579            }
580        }
581
582        // Valid rows have all landed by this point; surface the first
583        // oversize so callers still see a `ValueTooLarge` signal
584        // without losing the rest of the batch (AC #5 — one oversized
585        // row in the middle is rejected but the rest land).
586        if let Some(n) = first_oversize {
587            return Err(BTreeError::ValueTooLarge(n));
588        }
589
590        Ok(())
591    }
592
593    /// Delete a key
594    pub fn delete(&self, key: &[u8]) -> BTreeResult<bool> {
595        let _writer = self.writer.lock();
596        let root_id = self.root_page_id();
597        if root_id == 0 {
598            return Ok(false);
599        }
600
601        // D3 — Lehman-Yao right-link traversal for delete.
602        // A split may have moved the key to a right sibling between our
603        // descent and the actual page read. Follow right-links before giving up.
604        let (leaf_id, path) = self.find_leaf(root_id, key)?;
605        let mut current_id = leaf_id;
606        const MAX_RIGHTLINK_HOPS: usize = 32;
607        for _ in 0..MAX_RIGHTLINK_HOPS {
608            let mut page = self.pager.read_page(current_id)?;
609            match search_leaf(&page, key)? {
610                SearchResult::Found(pos) => {
611                    // Found — delete from this page (may differ from leaf_id).
612                    // If the cell carries a spill pointer, return every
613                    // overflow page to the free list before the leaf cell
614                    // goes away; otherwise the chain leaks (slice F).
615                    let (_, stored) = read_leaf_cell(&page, pos)?;
616                    self.free_chain_if_pointer(&stored)?;
617                    delete_from_leaf(&mut page, pos)?;
618                    page.update_checksum();
619                    let page_id = page.page_id();
620                    self.pager.write_page(page_id, page.clone())?;
621
622                    *self
623                        .rightmost_leaf
624                        .write()
625                        .unwrap_or_else(|e| e.into_inner()) = None;
626
627                    if page.cell_count() == 0 && page_id == root_id {
628                        self.pager.free_page(root_id)?;
629                        *self.root_page_id.write().map_err(|e| {
630                            BTreeError::LockPoisoned(format!(
631                                "delete: root_page_id write lock: {e}"
632                            ))
633                        })? = 0;
634                    } else {
635                        self.rebalance_leaf(current_id, path)?;
636                    }
637                    return Ok(true);
638                }
639                SearchResult::NotFound(_) => {
640                    let right = leaf_right_sibling(&page);
641                    if right != 0 {
642                        if let Some(high_key) = leaf_high_key(&page)? {
643                            if key > high_key.as_slice() {
644                                current_id = right;
645                                continue;
646                            }
647                        }
648                    }
649                    return Ok(false);
650                }
651            }
652        }
653        Ok(false)
654    }
655
656    /// Create a cursor starting at the first entry
657    pub fn cursor_first(&self) -> BTreeResult<BTreeCursor> {
658        let root_id = self.root_page_id();
659        if root_id == 0 {
660            return Ok(BTreeCursor {
661                leaf_page_id: 0,
662                position: 0,
663                pager: self.pager.clone(),
664                prefetched_next: false,
665            });
666        }
667
668        // Find leftmost leaf
669        let first_leaf = self.find_first_leaf(root_id)?;
670
671        Ok(BTreeCursor {
672            leaf_page_id: first_leaf,
673            position: 0,
674            pager: self.pager.clone(),
675            prefetched_next: false,
676        })
677    }
678
679    /// Create a cursor starting at or after the given key
680    pub fn cursor_seek(&self, key: &[u8]) -> BTreeResult<BTreeCursor> {
681        let root_id = self.root_page_id();
682        if root_id == 0 {
683            return Ok(BTreeCursor {
684                leaf_page_id: 0,
685                position: 0,
686                pager: self.pager.clone(),
687                prefetched_next: false,
688            });
689        }
690
691        let (leaf_id, _) = self.find_leaf(root_id, key)?;
692        let page = self.pager.read_page(leaf_id)?;
693
694        let position = match search_leaf(&page, key)? {
695            SearchResult::Found(pos) => pos,
696            SearchResult::NotFound(pos) => pos,
697        };
698
699        Ok(BTreeCursor {
700            leaf_page_id: leaf_id,
701            position,
702            pager: self.pager.clone(),
703            prefetched_next: false,
704        })
705    }
706
707    /// Range scan from start_key to end_key (inclusive)
708    pub fn range(&self, start_key: &[u8], end_key: &[u8]) -> BTreeResult<Vec<(Vec<u8>, Vec<u8>)>> {
709        let mut results = Vec::new();
710        let mut cursor = self.cursor_seek(start_key)?;
711
712        while let Some((key, value)) = cursor.next()? {
713            if key.as_slice() > end_key {
714                break;
715            }
716            results.push((key, value));
717        }
718
719        Ok(results)
720    }
721
722    /// Count entries in the tree
723    pub fn count(&self) -> BTreeResult<usize> {
724        let root_id = self.root_page_id();
725        if root_id == 0 {
726            return Ok(0);
727        }
728
729        let mut count = 0;
730        let mut cursor = self.cursor_first()?;
731        while cursor.next()?.is_some() {
732            count += 1;
733        }
734
735        Ok(count)
736    }
737
738    // ==================== Internal Methods ====================
739
740    fn cache_rightmost_leaf(&self, page: &Page) -> BTreeResult<()> {
741        // A split's incoming key may land in its left half. Caching that
742        // key as the right half's maximum sends later inserts to the wrong
743        // leaf, even with a single writer (#2376).
744        let cached = if leaf_right_sibling(page) == 0 {
745            leaf_high_key(page)?.map(|high| (page.page_id(), high))
746        } else {
747            None
748        };
749        *self.rightmost_leaf.write().map_err(|error| {
750            BTreeError::LockPoisoned(format!("rightmost_leaf write: {error}"))
751        })? = cached;
752        Ok(())
753    }
754
755    /// Find the leaf page containing the key
756    fn find_leaf(&self, page_id: u32, key: &[u8]) -> BTreeResult<(u32, Vec<u32>)> {
757        let mut current_id = page_id;
758        let mut path = Vec::new();
759
760        // A u32 page address space cannot contain a valid B-tree this deep.
761        // Bound corrupt child cycles before they grow the descent path.
762        for _ in 0..64 {
763            let page = self.pager.read_page(current_id)?;
764
765            match page.page_type()? {
766                PageType::BTreeLeaf => {
767                    return Ok((current_id, path));
768                }
769                PageType::BTreeInterior => {
770                    path.push(current_id);
771                    current_id = find_child(&page, key)?;
772                }
773                other => {
774                    return Err(BTreeError::Corrupted(format!(
775                        "Unexpected page type in B-tree: {:?}",
776                        other
777                    )));
778                }
779            }
780        }
781        Err(BTreeError::Corrupted(
782            "B-tree descent exceeds maximum depth".into(),
783        ))
784    }
785
786    /// Find the leftmost leaf page
787    pub(crate) fn find_first_leaf(&self, page_id: u32) -> BTreeResult<u32> {
788        let mut current_id = page_id;
789
790        for _ in 0..64 {
791            let page = self.pager.read_page(current_id)?;
792
793            match page.page_type()? {
794                PageType::BTreeLeaf => return Ok(current_id),
795                PageType::BTreeInterior => {
796                    // Go to leftmost child
797                    current_id = find_first_child(&page)?;
798                }
799                _ => {
800                    return Err(BTreeError::Corrupted("Invalid page type".into()));
801                }
802            }
803        }
804        Err(BTreeError::Corrupted(
805            "leftmost-leaf descent exceeds maximum depth".into(),
806        ))
807    }
808
809    /// Split a leaf page
810    fn split_leaf(
811        &self,
812        page: &mut Page,
813        new_key: &[u8],
814        new_value: &[u8],
815    ) -> BTreeResult<(Page, Vec<u8>)> {
816        // Collect all entries including the new one
817        let mut entries: Vec<(Vec<u8>, Vec<u8>)> = Vec::new();
818        let cell_count = page.cell_count() as usize;
819
820        for i in 0..cell_count {
821            entries.push(read_leaf_cell(page, i)?);
822        }
823
824        // Insert new entry in sorted position
825        let insert_pos = entries.partition_point(|(k, _)| k.as_slice() < new_key);
826        entries.insert(insert_pos, (new_key.to_vec(), new_value.to_vec()));
827
828        // Split in half
829        let mid = entries.len() / 2;
830
831        // Create new leaf
832        let mut new_page = self.pager.allocate_page(PageType::BTreeLeaf)?;
833
834        // Update leaf links
835        let old_next = read_next_leaf(page);
836        init_leaf_links(&mut new_page, page.page_id(), old_next);
837        set_next_leaf(page, new_page.page_id());
838
839        // Rebuild both halves through the slotted layout so their
840        // cell_count, free_start and free_end headers are consistent.
841        write_leaf_entries(page, &entries[..mid])?;
842        write_leaf_entries(&mut new_page, &entries[mid..])?;
843
844        // Separator is first key of new leaf
845        let separator = entries[mid].0.clone();
846
847        new_page.update_checksum();
848        let new_page_id = new_page.page_id();
849        self.pager.write_page(new_page_id, new_page.clone())?;
850
851        Ok((new_page, separator))
852    }
853
854    /// Insert into parent after split
855    fn insert_into_parent(
856        &self,
857        mut path: Vec<u32>,
858        left_child: u32,
859        key: &[u8],
860        right_child: u32,
861    ) -> BTreeResult<()> {
862        // If path is empty, need new root
863        if path.is_empty() {
864            let mut new_root = self.pager.allocate_page(PageType::BTreeInterior)?;
865            // Single separator with two children — seed the page via the
866            // canonical slotted builder so the header is consistent.
867            write_interior_entries(&mut new_root, &[key.to_vec()], &[left_child, right_child])?;
868
869            new_root.update_checksum();
870            let new_root_id = new_root.page_id();
871            self.pager.write_page(new_root_id, new_root)?;
872            *self.root_page_id.write().map_err(|e| {
873                BTreeError::LockPoisoned(format!(
874                    "insert_into_parent: root_page_id write lock: {e}"
875                ))
876            })? = new_root_id;
877            return Ok(());
878        }
879
880        // Insert into parent — path is non-empty (checked above)
881        let parent_id = path.pop().ok_or_else(|| {
882            BTreeError::Corrupted("insert_into_parent: path unexpectedly empty".into())
883        })?;
884        let mut parent = self.pager.read_page(parent_id)?;
885
886        // Can we fit?
887        if can_insert_interior(&parent, key) {
888            insert_into_interior(&mut parent, key, left_child, right_child)?;
889            parent.update_checksum();
890            self.pager.write_page(parent_id, parent)?;
891            return Ok(());
892        }
893
894        // Need to split interior node
895        let (new_interior, separator) =
896            self.split_interior(&mut parent, key, left_child, right_child)?;
897        parent.update_checksum();
898        self.pager.write_page(parent_id, parent.clone())?;
899
900        // Propagate up
901        self.insert_into_parent(path, parent.page_id(), &separator, new_interior.page_id())
902    }
903
904    /// Split an interior node
905    fn split_interior(
906        &self,
907        page: &mut Page,
908        new_key: &[u8],
909        left_child: u32,
910        right_child: u32,
911    ) -> BTreeResult<(Page, Vec<u8>)> {
912        let (mut keys, mut children) = read_interior_keys_children(page)?;
913        let key_insert_pos = keys.partition_point(|key| key.as_slice() < new_key);
914        let child_insert_pos = children
915            .iter()
916            .position(|child| *child == left_child)
917            .unwrap_or(key_insert_pos);
918
919        keys.insert(key_insert_pos, new_key.to_vec());
920        children.insert(child_insert_pos + 1, right_child);
921
922        // The promoted separator does not stay in either side; each
923        // resulting page is rebuilt through the shared slotted writer.
924        let mid = keys.len() / 2;
925        let separator = keys[mid].clone();
926
927        // Create new interior node
928        let mut new_page = self.pager.allocate_page(PageType::BTreeInterior)?;
929
930        write_interior_entries(page, &keys[..mid], &children[..mid + 1])?;
931        write_interior_entries(&mut new_page, &keys[mid + 1..], &children[mid + 1..])?;
932
933        new_page.update_checksum();
934        let new_page_id = new_page.page_id();
935        self.pager.write_page(new_page_id, new_page.clone())?;
936
937        Ok((new_page, separator))
938    }
939
940    fn rebalance_leaf(&self, leaf_id: u32, path: Vec<u32>) -> BTreeResult<()> {
941        if path.is_empty() {
942            return Ok(());
943        }
944
945        let root_id = self.root_page_id();
946        if leaf_id == root_id {
947            return Ok(());
948        }
949
950        let mut leaf = self.pager.read_page(leaf_id)?;
951        let mut leaf_entries = read_leaf_entries(&leaf)?;
952        let min_bytes = leaf_min_bytes();
953
954        let parent_id = *path.last().ok_or_else(|| {
955            BTreeError::Corrupted("rebalance_leaf: path unexpectedly empty".into())
956        })?;
957        let mut parent = self.pager.read_page(parent_id)?;
958        let (mut parent_keys, mut parent_children) = read_interior_keys_children(&parent)?;
959
960        let child_index = parent_children
961            .iter()
962            .position(|&id| id == leaf_id)
963            .ok_or_else(|| BTreeError::Corrupted("Leaf missing from parent".into()))?;
964
965        if child_index > 0 {
966            if let Some((first_key, _)) = leaf_entries.first() {
967                if parent_keys.get(child_index - 1).map(|k| k.as_slice())
968                    != Some(first_key.as_slice())
969                {
970                    parent_keys[child_index - 1] = first_key.clone();
971                    write_interior_entries(&mut parent, &parent_keys, &parent_children)?;
972                    parent.update_checksum();
973                    self.pager.write_page(parent_id, parent.clone())?;
974                }
975            }
976        }
977
978        if leaf_entries_size(&leaf_entries) >= min_bytes {
979            return Ok(());
980        }
981
982        if child_index > 0 {
983            let left_id = parent_children[child_index - 1];
984            let mut left = self.pager.read_page(left_id)?;
985            let mut left_entries = read_leaf_entries(&left)?;
986            let mut borrowed = false;
987
988            while leaf_entries_size(&leaf_entries) < min_bytes {
989                let Some(entry) = left_entries.pop() else {
990                    break;
991                };
992                if leaf_entries_size(&left_entries) < min_bytes {
993                    left_entries.push(entry);
994                    break;
995                }
996                leaf_entries.insert(0, entry);
997                borrowed = true;
998            }
999
1000            if borrowed {
1001                write_leaf_entries(&mut left, &left_entries)?;
1002                left.update_checksum();
1003                self.pager.write_page(left_id, left)?;
1004
1005                write_leaf_entries(&mut leaf, &leaf_entries)?;
1006                leaf.update_checksum();
1007                self.pager.write_page(leaf_id, leaf)?;
1008
1009                if let Some((first_key, _)) = leaf_entries.first() {
1010                    parent_keys[child_index - 1] = first_key.clone();
1011                    write_interior_entries(&mut parent, &parent_keys, &parent_children)?;
1012                    parent.update_checksum();
1013                    self.pager.write_page(parent_id, parent)?;
1014                }
1015
1016                return Ok(());
1017            }
1018        }
1019
1020        if child_index + 1 < parent_children.len() {
1021            let right_id = parent_children[child_index + 1];
1022            let mut right = self.pager.read_page(right_id)?;
1023            let mut right_entries = read_leaf_entries(&right)?;
1024            let mut borrowed = false;
1025
1026            while leaf_entries_size(&leaf_entries) < min_bytes {
1027                if right_entries.is_empty() {
1028                    break;
1029                }
1030                let entry = right_entries.remove(0);
1031                if leaf_entries_size(&right_entries) < min_bytes {
1032                    right_entries.insert(0, entry);
1033                    break;
1034                }
1035                leaf_entries.push(entry);
1036                borrowed = true;
1037            }
1038
1039            if borrowed {
1040                write_leaf_entries(&mut right, &right_entries)?;
1041                right.update_checksum();
1042                self.pager.write_page(right_id, right)?;
1043
1044                write_leaf_entries(&mut leaf, &leaf_entries)?;
1045                leaf.update_checksum();
1046                self.pager.write_page(leaf_id, leaf)?;
1047
1048                if let Some((first_key, _)) = right_entries.first() {
1049                    parent_keys[child_index] = first_key.clone();
1050                    write_interior_entries(&mut parent, &parent_keys, &parent_children)?;
1051                    parent.update_checksum();
1052                    self.pager.write_page(parent_id, parent)?;
1053                }
1054
1055                return Ok(());
1056            }
1057        }
1058
1059        if child_index > 0 {
1060            let left_id = parent_children[child_index - 1];
1061            let mut left = self.pager.read_page(left_id)?;
1062            let mut left_entries = read_leaf_entries(&left)?;
1063
1064            left_entries.extend(leaf_entries);
1065            write_leaf_entries(&mut left, &left_entries)?;
1066
1067            let next_leaf = read_next_leaf(&leaf);
1068            set_next_leaf(&mut left, next_leaf);
1069            if next_leaf != 0 {
1070                let mut next = self.pager.read_page(next_leaf)?;
1071                set_prev_leaf(&mut next, left_id);
1072                next.update_checksum();
1073                self.pager.write_page(next_leaf, next)?;
1074            }
1075
1076            left.update_checksum();
1077            self.pager.write_page(left_id, left)?;
1078            self.pager.free_page(leaf_id)?;
1079
1080            parent_keys.remove(child_index - 1);
1081            parent_children.remove(child_index);
1082            write_interior_entries(&mut parent, &parent_keys, &parent_children)?;
1083            parent.update_checksum();
1084            self.pager.write_page(parent_id, parent)?;
1085
1086            let mut parent_path = path;
1087            parent_path.pop();
1088            return self.rebalance_interior(parent_id, parent_path);
1089        }
1090
1091        if child_index + 1 < parent_children.len() {
1092            let right_id = parent_children[child_index + 1];
1093            let right = self.pager.read_page(right_id)?;
1094            let right_entries = read_leaf_entries(&right)?;
1095
1096            leaf_entries.extend(right_entries);
1097            write_leaf_entries(&mut leaf, &leaf_entries)?;
1098
1099            let next_leaf = read_next_leaf(&right);
1100            set_next_leaf(&mut leaf, next_leaf);
1101            if next_leaf != 0 {
1102                let mut next = self.pager.read_page(next_leaf)?;
1103                set_prev_leaf(&mut next, leaf_id);
1104                next.update_checksum();
1105                self.pager.write_page(next_leaf, next)?;
1106            }
1107
1108            leaf.update_checksum();
1109            self.pager.write_page(leaf_id, leaf)?;
1110            self.pager.free_page(right_id)?;
1111
1112            parent_keys.remove(child_index);
1113            parent_children.remove(child_index + 1);
1114            write_interior_entries(&mut parent, &parent_keys, &parent_children)?;
1115            parent.update_checksum();
1116            self.pager.write_page(parent_id, parent)?;
1117
1118            let mut parent_path = path;
1119            parent_path.pop();
1120            return self.rebalance_interior(parent_id, parent_path);
1121        }
1122
1123        Ok(())
1124    }
1125
1126    fn rebalance_interior(&self, node_id: u32, mut path: Vec<u32>) -> BTreeResult<()> {
1127        let root_id = self.root_page_id();
1128        let mut node = self.pager.read_page(node_id)?;
1129        let (mut node_keys, mut node_children) = read_interior_keys_children(&node)?;
1130        let min_bytes = interior_min_bytes();
1131
1132        if node_id == root_id {
1133            if node_keys.is_empty() {
1134                let next_root = node_children.first().copied().unwrap_or(0);
1135                self.pager.free_page(node_id)?;
1136                *self.root_page_id.write().map_err(|e| {
1137                    BTreeError::LockPoisoned(format!(
1138                        "rebalance_interior: root_page_id write lock: {e}"
1139                    ))
1140                })? = next_root;
1141            }
1142            return Ok(());
1143        }
1144
1145        if interior_entries_size(&node_keys) >= min_bytes {
1146            return Ok(());
1147        }
1148
1149        let parent_id = match path.pop() {
1150            Some(id) => id,
1151            None => return Ok(()),
1152        };
1153        let mut parent = self.pager.read_page(parent_id)?;
1154        let (mut parent_keys, mut parent_children) = read_interior_keys_children(&parent)?;
1155
1156        let child_index = parent_children
1157            .iter()
1158            .position(|&id| id == node_id)
1159            .ok_or_else(|| BTreeError::Corrupted("Interior missing from parent".into()))?;
1160
1161        if child_index > 0 {
1162            let left_id = parent_children[child_index - 1];
1163            let mut left = self.pager.read_page(left_id)?;
1164            let (mut left_keys, mut left_children) = read_interior_keys_children(&left)?;
1165
1166            if let Some(borrow_key) = left_keys.last().cloned() {
1167                let borrow_size = interior_key_size(&borrow_key);
1168                if interior_entries_size(&left_keys).saturating_sub(borrow_size) >= min_bytes {
1169                    let parent_key = parent_keys[child_index - 1].clone();
1170                    let borrowed_key = left_keys.pop().ok_or_else(|| {
1171                        BTreeError::Corrupted(
1172                            "rebalance_interior: left_keys empty after check".into(),
1173                        )
1174                    })?;
1175                    let borrowed_child = left_children.pop().ok_or_else(|| {
1176                        BTreeError::Corrupted("rebalance_interior: left_children empty".into())
1177                    })?;
1178
1179                    node_keys.insert(0, parent_key);
1180                    node_children.insert(0, borrowed_child);
1181                    parent_keys[child_index - 1] = borrowed_key;
1182
1183                    write_interior_entries(&mut left, &left_keys, &left_children)?;
1184                    left.update_checksum();
1185                    self.pager.write_page(left_id, left)?;
1186
1187                    write_interior_entries(&mut node, &node_keys, &node_children)?;
1188                    node.update_checksum();
1189                    self.pager.write_page(node_id, node)?;
1190
1191                    write_interior_entries(&mut parent, &parent_keys, &parent_children)?;
1192                    parent.update_checksum();
1193                    self.pager.write_page(parent_id, parent)?;
1194
1195                    return Ok(());
1196                }
1197            }
1198        }
1199
1200        if child_index + 1 < parent_children.len() {
1201            let right_id = parent_children[child_index + 1];
1202            let mut right = self.pager.read_page(right_id)?;
1203            let (mut right_keys, mut right_children) = read_interior_keys_children(&right)?;
1204
1205            if let Some(borrow_key) = right_keys.first().cloned() {
1206                let borrow_size = interior_key_size(&borrow_key);
1207                if interior_entries_size(&right_keys).saturating_sub(borrow_size) >= min_bytes {
1208                    let parent_key = parent_keys[child_index].clone();
1209                    let new_parent_key = right_keys.remove(0);
1210                    let borrowed_child = right_children.remove(0);
1211
1212                    node_keys.push(parent_key);
1213                    node_children.push(borrowed_child);
1214                    parent_keys[child_index] = new_parent_key;
1215
1216                    write_interior_entries(&mut right, &right_keys, &right_children)?;
1217                    right.update_checksum();
1218                    self.pager.write_page(right_id, right)?;
1219
1220                    write_interior_entries(&mut node, &node_keys, &node_children)?;
1221                    node.update_checksum();
1222                    self.pager.write_page(node_id, node)?;
1223
1224                    write_interior_entries(&mut parent, &parent_keys, &parent_children)?;
1225                    parent.update_checksum();
1226                    self.pager.write_page(parent_id, parent)?;
1227
1228                    return Ok(());
1229                }
1230            }
1231        }
1232
1233        if child_index > 0 {
1234            let left_id = parent_children[child_index - 1];
1235            let mut left = self.pager.read_page(left_id)?;
1236            let (mut left_keys, mut left_children) = read_interior_keys_children(&left)?;
1237            let parent_key = parent_keys.remove(child_index - 1);
1238            parent_children.remove(child_index);
1239
1240            left_keys.push(parent_key);
1241            left_keys.extend(node_keys);
1242            left_children.extend(node_children);
1243
1244            write_interior_entries(&mut left, &left_keys, &left_children)?;
1245            left.update_checksum();
1246            self.pager.write_page(left_id, left)?;
1247            self.pager.free_page(node_id)?;
1248
1249            write_interior_entries(&mut parent, &parent_keys, &parent_children)?;
1250            parent.update_checksum();
1251            self.pager.write_page(parent_id, parent)?;
1252
1253            return self.rebalance_interior(parent_id, path);
1254        }
1255
1256        if child_index + 1 < parent_children.len() {
1257            let right_id = parent_children[child_index + 1];
1258            let right = self.pager.read_page(right_id)?;
1259            let (right_keys, right_children) = read_interior_keys_children(&right)?;
1260            let parent_key = parent_keys.remove(child_index);
1261            parent_children.remove(child_index + 1);
1262
1263            node_keys.push(parent_key);
1264            node_keys.extend(right_keys);
1265            node_children.extend(right_children);
1266
1267            write_interior_entries(&mut node, &node_keys, &node_children)?;
1268            node.update_checksum();
1269            self.pager.write_page(node_id, node)?;
1270            self.pager.free_page(right_id)?;
1271
1272            write_interior_entries(&mut parent, &parent_keys, &parent_children)?;
1273            parent.update_checksum();
1274            self.pager.write_page(parent_id, parent)?;
1275
1276            return self.rebalance_interior(parent_id, path);
1277        }
1278
1279        Ok(())
1280    }
1281}