Context
The state-cache / branch-cache consolidation (#21380 cleaned core + #21386 state cache, stacked on the lock-free metrics work #21663) populates caches at flush time and invalidates them on unwind. Two related properties of that model need to be made explicit and enforced uniformly across all caches.
The model
-
Caches are validated by (txNum, epoch), not step. A cache entry carries the txNum its bytes are valid as of, plus the epoch (unwind generation) it was written in. Step granularity is too coarse: an unwind to a txNum inside the latest step needs txNum precision. The flush-time population path must therefore carry the per-key txNum, not the file step.
-
Unwind is O(1) and must not iterate the cache. Unwinding to (txNum, epoch) bumps the epoch and lowers an unwindFloor; entries are then dropped lazily on read (an entry from a superseded epoch whose txNum is above the floor is stale). A full cache scan-and-evict on every unwind defeats this — it is O(n) per unwind and does not disambiguate a txNum reused across forks. The state cache (GenericCache) already does this; the branch cache must too (its current UnwindTo iterates the tail).
-
All caches honor the same model. The branch cache, the state cache, and any future managed-residency cache must share this (txNum, epoch) + lazy-drop discipline so unwind correctness is uniform and cheap.
kv-interface tidy-up
WithFlushCallback / FlushConfig.DomainCallbacks currently expose (k, v, step). They must expose the per-key txNum (the value's write txNum) so flush-time cache population is tx-based.
- Consolidate / tidy the
FlushOption surface while doing so.
Refs
#21380 (consolidation core), #21386 (state cache), #21663 (lock-free metrics).
Context
The state-cache / branch-cache consolidation (#21380 cleaned core + #21386 state cache, stacked on the lock-free metrics work #21663) populates caches at flush time and invalidates them on unwind. Two related properties of that model need to be made explicit and enforced uniformly across all caches.
The model
Caches are validated by
(txNum, epoch), not step. A cache entry carries the txNum its bytes are valid as of, plus the epoch (unwind generation) it was written in. Step granularity is too coarse: an unwind to a txNum inside the latest step needs txNum precision. The flush-time population path must therefore carry the per-key txNum, not the file step.Unwind is O(1) and must not iterate the cache. Unwinding to
(txNum, epoch)bumps the epoch and lowers anunwindFloor; entries are then dropped lazily on read (an entry from a superseded epoch whose txNum is above the floor is stale). A full cache scan-and-evict on every unwind defeats this — it is O(n) per unwind and does not disambiguate a txNum reused across forks. The state cache (GenericCache) already does this; the branch cache must too (its currentUnwindToiterates the tail).All caches honor the same model. The branch cache, the state cache, and any future managed-residency cache must share this
(txNum, epoch)+ lazy-drop discipline so unwind correctness is uniform and cheap.kv-interface tidy-up
WithFlushCallback/FlushConfig.DomainCallbackscurrently expose(k, v, step). They must expose the per-key txNum (the value's write txNum) so flush-time cache population is tx-based.FlushOptionsurface while doing so.Refs
#21380 (consolidation core), #21386 (state cache), #21663 (lock-free metrics).