Fixed new bshrub roots and related bug fixing

It turned out by implicitly handling root allocation in
lfsr_btree_commit_, we were never allowing lfsr_bshrub_commit to
intercept new roots as new bshrubs. Fixing this required moving the
root allocation logic up into lfsr_btree_commit.

This resulted in quite a bit of small bug fixing because it turns out if
you can never create non-inlined bshrubs you never test non-inlined
bshrubs:

- Our previous rbyd.weight == btree.weight check for if we've reached
  the root no longer works, changed to an explicit check that the blocks
  match. Fortunately, now that new roots set trunk=0 new roots are no
  longer a problematic case.

- We need to only evict when we calculate an accurate estimate, the
  previous code had a bug where eviction occurred early based only on the
  progged-since-last-estimate.

- We need to manually set bshrub.block=mdir.block on new bshrubs,
  otherwise the lfsr_bshrub_isbshrub check fails in mdir commit staging.

Also updated btree/bshrub following code in the dbg scripts, which
mostly meant making them accept both BRANCH and SHRUBBRANCH tags as
btree/bshrub branches. Conveniently very little code needs to change
to extend btree read operations to support bshrubs.
This commit is contained in:
Christopher Haster
2023-11-20 23:10:30 -06:00
parent 6bd00caf93
commit 4793d2f144
5 changed files with 120 additions and 77 deletions
+99 -68
View File
@@ -4016,9 +4016,10 @@ static lfs_ssize_t lfsr_btree_commit_(lfs_t *lfs,
lfsr_rbyd_t parent = {.trunk=0, .weight=0};
lfsr_srid_t rid;
// are we root?
if (rbyd.trunk == 0 || rbyd.weight == btree->weight) {
// are we root and shrub? yield root updates to shrub commit
if (shrub) {
if (rbyd.block == btree->block || rbyd.trunk == 0) {
// new root? shrub root? yield creation of new roots to
// higher-level bshrub/btree logic
if (shrub || rbyd.trunk == 0) {
*btree = rbyd;
if (attrs_) {
*attrs_ = attrs;
@@ -4029,14 +4030,6 @@ static lfs_ssize_t lfsr_btree_commit_(lfs_t *lfs,
return 0;
}
// need a new root? this happens if we split
if (rbyd.trunk == 0) {
int err = lfsr_rbyd_alloc(lfs, &rbyd);
if (err) {
return err;
}
}
// mark btree as unerased in case of failure, our btree rbyd and
// root rbyd can diverge if there's a split, but we would have
// marked the old root as unerased earlier anyways
@@ -4512,15 +4505,42 @@ static int lfsr_btree_commit(lfs_t *lfs, lfsr_btree_t *btree,
lfsr_attr_t scratch_attrs[4];
uint8_t scratch_buf[2*LFSR_BRANCH_DSIZE];
lfs_ssize_t attr_count_ = lfsr_btree_commit_(lfs, btree, false,
// try to commit to the btree
int err = lfsr_btree_commit_(lfs, btree, false,
scratch_attrs, scratch_buf,
attrs, attr_count,
NULL, NULL);
if (attr_count_ < 0) {
return attr_count_;
&attrs, &attr_count);
if (err) {
return err;
}
// needs a new root?
if (attr_count > 0) {
// TODO do we need to be this careful with backup copies?
lfsr_rbyd_t rbyd;
err = lfsr_rbyd_alloc(lfs, &rbyd);
if (err) {
return err;
}
// TODO should we just use rbyd commit? it allocates _another_
// redundant copy which is a bit much...
err = lfsr_rbyd_appendattrs(lfs, &rbyd, -1, -1,
attrs, attr_count);
if (err) {
LFS_ASSERT(err != LFS_ERR_RANGE);
return err;
}
err = lfsr_rbyd_appendcksum(lfs, &rbyd);
if (err) {
LFS_ASSERT(err != LFS_ERR_RANGE);
return err;
}
*btree = rbyd;
}
LFS_ASSERT(attr_count_ == 0);
LFS_ASSERT(btree->trunk != 0);
return 0;
}
@@ -6762,65 +6782,76 @@ static int lfsr_bshrub_commit(lfs_t *lfs, lfsr_mdir_t *mdir,
return estimate;
}
bshrub->progged = estimate;
// do we overflow shrub_size/2? the 1/2 here prevents runaway
// performance when the shrub is near full
if (bshrub->progged > lfs->cfg->shrub_size/2) {
goto evict;
}
}
// do we overflow shrub_size/2? the 1/2 here prevents runaway
// performance when the shrub is near full
if (bshrub->progged > lfs->cfg->shrub_size/2) {
// TODO am I missing a simpler function here? at least use
// lfsr_rbyd_commit once it doesn't maintain a copy...
// convert to btree
err = lfsr_rbyd_alloc(lfs, &bshrub->rbyd_);
if (err) {
return err;
}
err = lfsr_rbyd_appendcompactrbyd(lfs, &bshrub->rbyd_, false,
-1, -1, &bshrub->rbyd);
if (err) {
LFS_ASSERT(err != LFS_ERR_RANGE);
return err;
}
err = lfsr_rbyd_compact(lfs, &bshrub->rbyd_, false,
sizeof(uint32_t));
if (err) {
LFS_ASSERT(err != LFS_ERR_RANGE);
return err;
}
err = lfsr_rbyd_appendattrs(lfs, &bshrub->rbyd_, -1, -1,
attrs, attr_count);
if (err) {
LFS_ASSERT(err != LFS_ERR_RANGE);
return err;
}
err = lfsr_rbyd_appendcksum(lfs, &bshrub->rbyd_);
if (err) {
LFS_ASSERT(err != LFS_ERR_RANGE);
return err;
}
bshrub->rbyd = bshrub->rbyd_;
// otherwise commit to shrub like normal
} else {
int err = lfsr_mdir_commit(lfs, mdir, LFSR_ATTRS(
LFSR_ATTR(mdir->mid,
BSHRUBCOMMIT, 0, BSHRUBCOMMIT(
bshrub, attrs, attr_count))));
if (err) {
return err;
}
bshrub->progged += progged;
// if our shrub is a new root, we need to set the correct block
LFS_ASSERT(bshrub->rbyd.trunk == 0
|| bshrub->rbyd.block == mdir->u.rbyd.block);
if (bshrub->rbyd.trunk == 0) {
bshrub->rbyd.block = mdir->u.rbyd.block;
}
// commit to shrub
err = lfsr_mdir_commit(lfs, mdir, LFSR_ATTRS(
LFSR_ATTR(mdir->mid,
BSHRUBCOMMIT, 0, BSHRUBCOMMIT(
bshrub, attrs, attr_count))));
if (err) {
return err;
}
bshrub->progged += progged;
}
LFS_ASSERT(bshrub->rbyd.trunk != 0);
return 0;
evict:;
// TODO am I missing a simpler function here? at least use
// lfsr_rbyd_commit once it doesn't maintain a copy...
// convert to btree
err = lfsr_rbyd_alloc(lfs, &bshrub->rbyd_);
if (err) {
return err;
}
err = lfsr_rbyd_appendcompactrbyd(lfs, &bshrub->rbyd_, false,
-1, -1, &bshrub->rbyd);
if (err) {
LFS_ASSERT(err != LFS_ERR_RANGE);
return err;
}
err = lfsr_rbyd_compact(lfs, &bshrub->rbyd_, false,
sizeof(uint32_t));
if (err) {
LFS_ASSERT(err != LFS_ERR_RANGE);
return err;
}
err = lfsr_rbyd_appendattrs(lfs, &bshrub->rbyd_, -1, -1,
attrs, attr_count);
if (err) {
LFS_ASSERT(err != LFS_ERR_RANGE);
return err;
}
err = lfsr_rbyd_appendcksum(lfs, &bshrub->rbyd_);
if (err) {
LFS_ASSERT(err != LFS_ERR_RANGE);
return err;
}
bshrub->rbyd = bshrub->rbyd_;
LFS_ASSERT(bshrub->rbyd.trunk != 0);
return 0;
}
static lfs_scmp_t lfsr_bshrub_namelookup(lfs_t *lfs, const lfsr_mdir_t *mdir,