scripts: Avoid rereading shrub blocks

This extends Rbyd.fetch to accept another rbyd, in which case we inherit
the RAM-backed block without rereading it from disk. This avoids an
issue where shrubs can become corrupted if the disk is being
simultaneously written and debugged.

Normally we can detect the checksum mismatch and toss out the rbyd
during fetch, but shrub pointers don't include a checksum since they
assume the containing rbyd has already been checksummed.

It's interesting to note this even avoids the memory copy thanks to
Python's reference counting.
This commit is contained in:
Christopher Haster
2024-11-08 02:24:56 -06:00
parent 0260f0bcee
commit a0ab7bda26
4 changed files with 82 additions and 42 deletions

View File

@@ -616,14 +616,14 @@ class Rbyd:
self.trunk)
@classmethod
def fetch(cls, f, block_size, blocks, trunk=None, cksum=None):
if isinstance(blocks, int):
blocks = (blocks,)
if len(blocks) > 1:
def fetch(cls, f, block_size, block, trunk=None, cksum=None):
# multiple blocks?
if (not isinstance(block, int)
and not isinstance(block, Rbyd)
and len(block) > 1):
# fetch all blocks
rbyds = [cls.fetch(f, block_size, block, trunk, cksum)
for block in blocks]
for block in block]
# determine most recent revision
i = 0
for i_, rbyd in enumerate(rbyds):
@@ -640,17 +640,27 @@ class Rbyd:
rbyds[(i+1+j) % len(rbyds)].block
for j in range(len(rbyds)-1))
return rbyd
# block may be an rbyd, in which case we inherit the
# already-read data
#
# this helps avoid race conditions with cksums and shrubs
if isinstance(block, Rbyd):
# inherit the trunk too I guess?
if trunk is None:
trunk = block.trunk
block, data = block.block, block.data
else:
# block may encode a trunk
block = blocks[0]
block = block[0] if not isinstance(block, int) else block
if isinstance(block, tuple):
if trunk is None:
trunk = block[1]
block = block[0]
# seek to the block
f.seek(block * block_size)
data = f.read(block_size)
# seek to the block
f.seek(block * block_size)
data = f.read(block_size)
# fetch the rbyd
rev = fromle32(data[0:4])
@@ -1262,7 +1272,7 @@ def main(disk, mroots=None, *,
# inlined bshrub?
elif tag == TAG_BSHRUB:
weight, trunk = fromshrub(data)
btree = Rbyd.fetch(f, block_size, mdir.block, trunk)
btree = Rbyd.fetch(f, block_size, mdir, trunk)
shrub = True
# indirect btree?