execution/cache, db/state/execctx, execution/exec: applied-progress watermark for stale-snapshot cache fills - #22357
Closed
yperbasis wants to merge 1 commit into
Conversation
…atermark gates stale-snapshot cache fills A deletion tombstone or no-code marker is an ordinary LRU entry: cache pressure can evict it while a pre-delete snapshot is still alive, and that snapshot's read-fill then finds the key absent and resurrects the deleted value as a live cache hit. Per-key defenses cannot close this — pinning has no bounded lifetime and a stamp cannot distinguish a deleted old value from a live unchanged one. StateCache gains a per-domain applied-progress watermark — tiny, unevictable — raised by the flush cache-apply, lowered by Unwind, zeroed by Clear. Every addr-keyed fill (SD read-fill, warmup getter, addr→codeHash populate) compares its own snapshot's DomainProgress against it and skips when behind, so a stale snapshot cannot fill regardless of the marker's fate. Subsumes the warmup getter's nil-progress guard: no oracle, no fills.
This was referenced Jul 9, 2026
yperbasis
added a commit
that referenced
this pull request
Jul 14, 2026
Stack the tombstone fix on the tombstone-free StateCache follow-ups so it uses their atomic PutIfAbsent read-fill ordering. Fold #22357's applied-progress watermark into this branch. Stale RPC and warmup snapshots now skip fills even after cache pressure evicts a deletion marker; production-path integration coverage exercises account and code resurrection after eviction.
Member
Author
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #22356 — the eviction-resurrection hole found by a Codex review of #22159 (#22159 (comment)).
What changed
Applied-progress watermark —
StateCachegains a per-domainatomic.Uint64watermark of flush-applied commits:NoteApplied(CAS-max) is called by the flush cache-apply for every update it lands (an account deletion also notes CodeDomain for its no-code marker),Unwindlowers it to the unwind point,Clearzeroes it. Tiny and unevictable — the property the per-key tombstones lack.Every addr-keyed fill proves snapshot freshness — the SD read-fill, the warmup getter and
codeHashForAddr's addr→codeHash populate compare their own snapshot'sDomainProgressagainst the watermark and skip the fill when behind. A pre-delete snapshot is by construction behind the delete-commit the cache absorbed, so its fills are rejected whether or not the tombstone survived eviction. Tombstones/markers remain the immediate-coherence fast path; the watermark is the backstop. The gate subsumes the warmup getter's nil-progress guard: a getter without a progress oracle cannot prove freshness and never fills.Deliberately ungated — the content-addressed code-size fill (
PutCodeSizeByHash): keyed by codeHash, whose size is immutable, so a stale snapshot cannot make it lie.Cost — negatives already paid the keys-table
LastKey(≈290 ns per #22159's benchmarks) for their stamp; it is now reused for the gate. Positive fills gain one such call, cold-miss-only. Exec, warmup and per-request RPC readers run on fresh snapshots and pass the gate unchanged.Testing
TDD: red on the pre-fix code.
TestReadFill_DoesNotResurrectAfterMarkerEviction(accounts + code) — the Codex repro through the real read path: commit@10, pre-delete RO snapshot, delete@20, pressure-evict the marker, straddling fill; a fresh read returned the deleted value before, empty after.TestCachePopulatingGetterStaleSnapshotDoesNotFill— the warmup-getter gate in isolation.TestStateCache_AppliedProgressWatermark— the watermark follows applies up (monotonically), unwinds down, and Clear to zero, per domain.TestCachePopulatingGetterNilProgressNeverFills— no oracle → no fills at all (previously: negatives only).Verification:
go build ./...;execution/cache(/coherence),db/state/execctx,execution/exec,execution/types/accountssuites, with-raceon the first two;execution/testsandexecution/execmoduleshort suites plus the reorg engine tests (TestReorgLongBlocks,TestChainTxReorgs,TestLongerForkBlocks);make lintclean.