v0.2.1
Fixed — durability (silent data loss path)
-
BufferManager::try_evict_lruwas evicting dirty cache
images. The inline-overflow eviction picked victims based on
Arc::strong_count == 1alone — it did not check the dirty
map. A blob that had been mutated (pin → write → mark_dirty → drop pin) could be picked as a victim by the next cache-miss
load, leaving the dirty entry orphaned (cache image gone, dirty
map still pointing at the now-missing guid). Downstream the
next checkpoint'ssnapshot_bytes(guid)returnedNoneand
the round /Tree::checkpointsilentlycontinue-d past it;
in memory mode the cache mutation was lost outright, in
persistent mode the WAL truncate gate stuck closed forever
(dirty_count never reached zero).try_evict_lrunow matchestry_evict_cold's contract: skip
any entry whose guid is indirtyorpending_deletes. Both
the victim-selection loop and theremove_ifpredicate
re-check under the relevant lock, guarding against a fresh
mark_dirtylanding between scan and remove. -
Checkpoint paths no longer silently drop a missing cache
image.Tree::checkpointand the background round's phase 2
used toif let Some(bytes) = snapshot_bytes(guid) { ... }
and silently fall through onNone. They now treat that case
as the invariant-I1 violation it is: restore both drained
snapshots and returnError::Internal("checkpoint: dirty entry lost cache image — invariant I1 violated"). Better to
fail loud than truncate the WAL while data is still pending. -
Regression test:
lru_eviction_skips_dirty_entriesin
src/store/buffer_manager.rsexercises capacity-2 cache with
one dirty + one clean entry, asserts the clean entry is the
victim of inline overflow and the dirty cache image survives.
Internal
release.yml: dropped therelease-notes/v$VERSION.md
curated-note branch — CHANGELOG is now the single source for
GitHub Release body content.