]> git.hungrycats.org Git - bees/commit
hash: close lost-update window in flush_dirty_extent
authorZygo Blaxell <bees@furryterror.org>
Sat, 5 Sep 2026 04:02:11 +0000 (00:02 -0400)
committerZygo Blaxell <bees@furryterror.org>
Sat, 5 Sep 2026 04:04:15 +0000 (00:04 -0400)
commitaf33cb0d4627866d73bb992a1b762ccdd84f4bf9
tree9f5245d7cddd34a028bce893710b71c91f436c36
parentec02e6044d250c63705e40a80b88b514b883eb3d
hash: close lost-update window in flush_dirty_extent

flush_dirty_extent() copied the extent under its lock, released the
lock for the pwrite, then re-locked and cleared the dirty flag.  A
set_extent_dirty_locked() that landed during the write window was
therefore erased: the on-disk copy predates the modification, and with
the flag cleared neither the writeback loop nor the shutdown flush
revisits the extent until something else dirties it.

This is benign in practice.  The hash table is a lossy best-effort
cache, every hit is content-verified before dedupe, and the flush at
exit already accepts losing updates from Tasks still running during the
final pass.  It is also easy to close.

Clear the dirty flag before taking the snapshot, while the extent lock
is still held.  A modification during the write window sets the flag
again, so the extent is written on a later pass; the worst case is an
occasional duplicate extent write.

Do not restore the flag if the write fails.  Re-marking the extent dirty
would retry the same write on every flush pass (a pass runs whenever any
hash update signals the writeback thread), so a persistently failing
write (ENOSPC on the hash table filesystem, for example) becomes a burst
of failing pwrites and logged exceptions per pass.  Leave the extent
clean instead: the on-disk copy stays stale until the next update to
that extent dirties it again, the same best-effort loss accepted at
shutdown, and each failure is logged once per dirtying.

Rate limit failed attempts as well.  flush_dirty_extents() only slept
after a successful write, so a slow pass over a table whose writes all
fail degenerated into back-to-back failures.  Charge a failed attempt
the same 128 KiB slot as a successful one so the failure rate is bounded
by the same writeback rate limit.

Assisted-by: Claude-Code:claude-fable-5-1
src/bees-hash.cc