Skip to main content

reddb_engine/storage/btree/
impl.rs

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