Commit Graph

105 Commits

Author SHA1 Message Date
Christopher Haster 8732904ef6 Implemented lfsr_btree_pop and btree merges
B-tree remove/merge is the most annoying part of B-trees.

The implementation here follows the same ideas implemented in push/split:
1. Defer splits/merges until compaction.
2. Assume our split/merge will succeed and play it out into the rbyd.
3. On the first sign of failure, revert any unnecessary changes by
   appending deletes.
4. Do all of this in a single commit to avoid issues with single-prog
   blocks.

Mapping this onto B-tree merge, the condition that triggers merge is
when our rbyd is <1/4 the block_size after compaction, and the condition
that aborts a merge is when our rbyd is >1/2 the block_size, since that
would trigger a split on a later compact.

Weaving this into lfsr_btree_commit is a bit subtle, but relatively
straightforward all things considered.

One downside is it's not physically possible to try merging with both
siblings, so we have to choose just one to attempt a merge. We handle
the corner case of merging the last sibling in a block explicitly, and
in theory the other sibling will eventually trigger a merge during its
own compaction.

Extra annoying are the corner cases with merges in the root rbyd that
make the root rbyd degenerate. We really should avoid a compaction in
this case, as otherwise we would erase a block that we immediately
inline at a significant cost. However determining if our root rbyd is
degenerate is tricky. We can determine a degenerate root with children
by checking if our rbyd's weight matches the B-tree's weight when we
merge. But determining a degenerate root that is a leaf requires
manually looking up both children in lfsr_btree_pop to see if they will
result in a degenerate root. Ugh.

On the bright side, this does all seem to be working now. Which
completes the last of the core B-tree algorithms.
2023-03-17 14:29:02 -05:00
Christopher Haster a897b875d3 Implemented lfsr_btree_update and added more tests
This was a rather simple exercise. lfsr_btree_commit does most of the
work already, so all this needed was setting up the pending attributes
correctly.

Also:
- Tweaked dbgrbyd.py's tree rendering to match dbgbtree.py's.
- Added a print to each B-tree test to help find the resulting B-tree
  when debugging.
2023-03-17 14:20:40 -05:00
Christopher Haster ce18bec66d Added sparse btree tests, fixed found bugs (grow/shrink bisect)
- After a B-tree split, when we're append pending attributes, it's
  possible for the id chosen for bisection to be itself modified by
  pending grows/shrinks. This needs to be accounted for in the two
  passes for the two children.

But this means our tests are working.
2023-03-17 14:20:09 -05:00
Christopher Haster 0a3c6b39c1 Implement generalized btree push, note the boundary conditions when id=weight
This really just required care around calculating the expected B-tree id
and rbyd id (which are different!).

B-tree append, aka B-tree push with id=weight, is actually the outlier.
We need a B-tree id that can identify the rbyd we're appending to, but
this id itself doesn't exist in the tree yet, which can be a bit tricky.
2023-03-17 14:20:09 -05:00
Christopher Haster 88e3db98a9 Rough implementation of btree append
This involves many, many hacks, but is enough to test the concept
and start looking at how it interacts with different block sizes.

Note only append (lfsr_btree_push on the end) is implemented, and it
makes some assumption about how the ids can interact when splitting
rbyds.
2023-03-17 14:20:09 -05:00