Add state caches that persist throughuout blocks#18868
Merged
Merged
Conversation
- Remove code cache creation in exec3.go to prevent stale cache data between block generation and execution phases - Simplify ValidateAndPrepare to only clear on actual hash mismatch - Remove code cache integration from domain_shared.go GetLatest (cache management should happen at higher level) This fixes TestDeleteRecreateSlotsAcrossManyBlocks which was failing due to stale cached code being returned after selfdestruct/recreate. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…appings This improves the code cache design: - Level 1: addr→codeHash (262144 entries, mutable, cleared on reorg) - Level 2: codeHash→code (2048 entries, immutable, never cleared) Multiple addresses can share the same code (common with proxies/clones), and code hash is immutable so the same hash always means same code. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Prints cache sizes and hit rates at the end of each block: - addr cache: hits/total (hit%) size - code cache: hits/total (hit%) size Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Don't populate cache for addresses already dirty in current block - Capture dirty state at start of GetCode/GetCodeSize before getStateObject - This prevents re-caching code after Selfdestruct removes the entry - Fixes TestCVE2020_26265 and TestSelfDestructReceive with cache enabled - Increase DefaultCodeCacheSize to 10_000 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
| codeHash = c.CodeHash.Value() | ||
| } | ||
|
|
||
| if !c.CodeHash.IsZero() { |
Collaborator
There was a problem hiding this comment.
seems like 2 times calling IsZero()
Collaborator
|
@Giulio2002 only question: |
Contributor
Author
drop all caches. all invalid blocks clear all caches. we have a correctness check in the form of the block hash stored in the state cache |
AskAlexSharov
approved these changes
Feb 2, 2026
AskAlexSharov
left a comment
Collaborator
There was a problem hiding this comment.
overall looks good
Collaborator
|
Maybe ProcessFrozenBlocks also need it? |
Contributor
Author
too complex |
Collaborator
What is the right way to perf-test this caches? |
mh0lt
added a commit
that referenced
this pull request
Jun 5, 2026
…emove the schedule-time ValidateAndPrepare purge The state cache carried a per-domain blockHash and was scrubbed by ValidateAndPrepare before every block. In the parallel executor that call sits in processRequest — a *schedule* step, not an apply step — copied from the serial path (#18868). With a 32-deep pipeline and heavy retry traffic the single blockHash almost never equals the next call's parentHash, so it took the wipe branch ~100% of the time: measured storage-cache purge_rate ~100%, hit ~35% during catch-up, the cache wiped every block. Make the cache what it should be — a SharedDomains implementation detail, populated only at flush (committed, fork-agnostic state) and invalidated only on unwind. Coherence is now txNum/epoch based, no block awareness and no diffset: - Each GenericCache entry carries (txNum, epoch). A read is valid iff it was written in the current epoch OR its txNum is at/below unwindFloor (predates every unwind). - Unwind(txNum) bumps the epoch and lowers the floor — O(1), no scan. Stale entries are dropped lazily on their next read. txNum slots are reused across forks, so the epoch (not the txNum) tells a dead fork's write from the live fork's at the same txNum. - CodeCache clears its small mutable addr layers on unwind; immutable content-addressed code is kept. Wiring: FlushWithCallback delivers txNum (cache stamps it; branchCache derives step); read-population and read-ahead stamp the step's txNum upper bound. The three exec-flow ValidateAndPrepare calls are removed; the unwind path calls stateCache.Unwind(txNum) unconditionally (diffset-free, matching the overlay's maxtx prune — diffsets aren't generated below the reorg window, so the old changeSet-gated cache revert left a stale gap). RevertWithDiffset/blockHash/ClearWithHash and the fork-validation cache scrub are removed. (DB-level diffset retirement is a follow-on.) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
mh0lt
added a commit
that referenced
this pull request
Jun 5, 2026
…emove the schedule-time ValidateAndPrepare purge The state cache carried a per-domain blockHash and was scrubbed by ValidateAndPrepare before every block. In the parallel executor that call sits in processRequest — a *schedule* step, not an apply step — copied from the serial path (#18868). With a 32-deep pipeline and heavy retry traffic the single blockHash almost never equals the next call's parentHash, so it took the wipe branch ~100% of the time: measured storage-cache purge_rate ~100%, hit ~35% during catch-up, the cache wiped every block. Make the cache what it should be — a SharedDomains implementation detail, populated only at flush (committed, fork-agnostic state) and invalidated only on unwind. Coherence is now txNum/epoch based, no block awareness and no diffset: - Each GenericCache entry carries (txNum, epoch). A read is valid iff it was written in the current epoch OR its txNum is at/below unwindFloor (predates every unwind). - Unwind(txNum) bumps the epoch and lowers the floor — O(1), no scan. Stale entries are dropped lazily on their next read. txNum slots are reused across forks, so the epoch (not the txNum) tells a dead fork's write from the live fork's at the same txNum. - CodeCache clears its small mutable addr layers on unwind; immutable content-addressed code is kept. Wiring: FlushWithCallback delivers txNum (cache stamps it; branchCache derives step); read-population and read-ahead stamp the step's txNum upper bound. The three exec-flow ValidateAndPrepare calls are removed; the unwind path calls stateCache.Unwind(txNum) unconditionally (diffset-free, matching the overlay's maxtx prune — diffsets aren't generated below the reorg window, so the old changeSet-gated cache revert left a stale gap). RevertWithDiffset/blockHash/ClearWithHash and the fork-validation cache scrub are removed. (DB-level diffset retirement is a follow-on.) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.
Performance: Size-capped execution caches for chain-tip processing
Summary
This PR introduces a suite of size-capped caches designed to improve chain-tip block execution performance by reducing redundant database reads. The caches are optimized for data locality and use a simple "stop growing when full" eviction policy rather than LRU, which avoids the overhead of tracking access patterns while still providing cache benefits for hot data.
Key Design Decisions
Size-capped caches that don't evict
All caches in this PR are capped by byte size and do not evict entries when full - they simply stop accepting new entries. This design:
Cache Components
StateCache (execution/cache/state_cache.go) - Unified cache for domain data:
CodeCache (execution/cache/code_cache.go) - Two-level cache for contract code:
address → maphash(code)(mutable, cleared on reorg)maphash(code) → code(immutable, never cleared)GenericCache (execution/cache/generic_cache.go) - Bounded concurrent cache:
maphash.Map