Skip to content

db, execution, rpcdaemon: coherent version-keyed state cache - #22532

Draft
yperbasis wants to merge 2 commits into
mainfrom
yperbasis/coherent-state-cache
Draft

db, execution, rpcdaemon: coherent version-keyed state cache#22532
yperbasis wants to merge 2 commits into
mainfrom
yperbasis/coherent-state-cache

Conversation

@yperbasis

@yperbasis yperbasis commented Jul 16, 2026

Copy link
Copy Markdown
Member

Split out of #21293 (FcuBackgroundCommit groundwork). Defaults are unchanged — the 0MB128MB retention default flip stays in #22269.

Announce the version committed readers will observe

State-change notifications are dispatched pre-commit (from the SD overlay). Dispatcher.Dispatch gains a preCommit flag and announces the post-commit PlainStateVersion — the flush that follows bumps the version exactly once (TemporalMemBatch.flushLocked) — so version-keyed cache roots match exactly the readers that should see them: post-commit transactions hit the new canonical root, pre-commit transactions keep the previous one. The committed-tx dispatch path (Hook.SendNotifications) passes false.

Coherent version-keyed cache (db/kv/kvcache)

Roots are keyed by PlainStateVersion. Batch-fed storage and code entries use the key shapes readers look up (address+location; code by address — see state.CachedReader3). A view that outlives KeepViews version advances falls back to its own tx snapshot instead of erroring too old ViewID; reads through non-latest views are not cached (they bypass eviction accounting and would grow without bound). Retained roots share the configured memory budgets: entries in older roots are cleared on version advance. Canonical roots start from their own batch, with no carry-over: the state-change producers don't announce every mutation, so a carried entry could stay stale — at a deliberate hit-rate cost (hot-but-unchanged keys miss once per key per block). Completing the producers and restoring carry-over is tracked in #22276. Code-cache hits now refresh the code eviction list (they previously moved entries in the state list, corrupting LRU order). A zero budget skips the cache machinery entirely — no global-lock churn on the read path.

Standalone rpcdaemon wiring

The standalone daemon uses the coherent cache at every configured budget, in remote and --datadir modes alike (previously 0MB and --datadir fell back to SimpleCache, which serves the newest batch's accounts to any reader regardless of its tx snapshot). Positive budgets retain entries; 0MB retains none and disables new-block waiting, so reads fall back to the caller's tx snapshot while remaining coherent.

Tests

TestStateChangeVersionMatchesCommitted pins announce-vs-committed version parity across {fg, bg} × {1-by-1, batched} (the batched case crosses the initial-cycle threshold, covering mid-FCU version bumps). kvcache tests pin fresh roots, retained-root aggregate budgets, reader-shaped feed keys, the evicted-view fallback, non-latest no-cache, and zero-budget no-retention. TestZeroBudgetRemoteCachePinsCommittedState pins snapshot-consistent reads with entry retention disabled.

The execmoduletester now calls ExecModule.WaitIdle after InsertChain: state-change events are dispatched pre-commit, so returning on the notification stream alone let bg-commit tests read pre-commit state (blockgen.GenerateChain on m.DB would build on a stale genesis). That fix is what lets TestNotificationDispatchBackgroundCommit drop its skip — successive FCUs are serialized via the ExecModule semaphore, so the skip's stated blocker no longer exists.

Known follow-ups

#22527 (same-version re-announce keeps stale entries at >0MB after a failed/crashed commit — should land before #22269), #22276 (producer completeness / carry-over warmth), #22277 (erigon_cacheCheck false positives, pre-existing).

Split out of #21293. Announce the post-commit PlainStateVersion from the
pre-commit dispatch, rework the Coherent kvcache around fresh
version-keyed roots with reader-shaped keys, and wire the standalone
rpcdaemon to the coherent cache at every budget. The execmoduletester
waits for the FCU background commit after InsertChain so bg-commit
tests read committed state.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces a coherent, version-keyed state cache model (keyed by PlainStateVersion) and updates notification dispatch so state-change batches can announce the version readers will observe post-commit (via a new preCommit flag). It also aligns standalone rpcdaemon cache wiring across remote and --datadir modes, and adds/extends tests to pin the new announce-vs-committed version contract and cache behaviors (including zero-budget coherence).

Changes:

  • Extend notification dispatch API (preCommit flag) so pre-commit overlay dispatch announces the post-commit PlainStateVersion.
  • Rework kvcache.Coherent to be version-keyed and reader-shaped (address+location for storage, address for code), with explicit behavior for evicted roots and zero-budget operation.
  • Update standalone rpcdaemon to consistently use the coherent cache (even at 0MB), plus add tests for version parity and zero-budget snapshot-consistent reads.

Reviewed changes

Copilot reviewed 14 out of 14 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
rpc/rpchelper/helper.go Simplifies history cached reader wiring by relying on CacheView.GetAsOf in the interface.
execution/stagedsync/stageloop/stageloop.go Extends notification sender contract with preCommit flag; clarifies committed-tx requirement for Hook.SendNotifications.
execution/execmodule/notification_dispatcher.go Implements preCommit behavior: announces post-commit PlainStateVersion for overlay dispatch.
execution/execmodule/forkchoice.go Marks overlay-driven dispatch as preCommit=true.
execution/execmodule/execmoduletester/exec_module_tester.go Ensures tests wait for commit completion via ExecModule.WaitIdle after InsertChain.
execution/execmodule/exec_module_test.go Adds version parity test and removes background-commit skip based on updated serialization guarantees.
db/kv/kvcache/cache.go Core coherent cache changes: version-keyed roots, reader key shapes, eviction/root-lifetime handling, and zero-budget fast-paths.
db/kv/kvcache/cache_test.go Adds targeted tests for fresh roots, shared budgets, key shapes, eviction fallback, non-latest no-cache, and zero-budget behavior.
db/kv/kvcache/simple.go Makes SimpleView satisfy updated CacheView interface (adds GetAsOf stub).
cmd/rpcdaemon/cli/config.go Ensures standalone rpcdaemon consistently uses coherent cache across modes; disables new-block waiting for zero budgets.
cmd/rpcdaemon/cli/config_test.go Adds regression test ensuring zero-budget remote cache remains snapshot-consistent (no retention).
llms-full.txt Updates CLI help text for --state.cache semantics.
docs/site/static/llms-full.txt Mirrors updated CLI help text for documentation site static copy.
docs/site/docs/fundamentals/modules/rpc-daemon.md Mirrors updated --state.cache help in rpcdaemon docs.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread db/kv/kvcache/cache.go
Comment on lines 222 to 225
// if nothing has progressed just return the existing root
if c.latestStateVersionID == stateVersionID && rootExists {
return r
}
pull Bot pushed a commit to Dustin4444/erigon that referenced this pull request Jul 18, 2026
Renames `SimpleCache` → `LatestBatchCache` (with `NewSimple` →
`NewLatestBatchCache`, `SimpleView` → `LatestBatchView`, `simple.go` →
`latest_batch.go`).

"Simple" described the implementation rather than the contract, and
suggested Simple and Coherent sit on a complexity spectrum. They
actually sit on opposite sides of a semantic fork:

- **`Coherent`** — snapshot-consistency first: entries are keyed by
`PlainStateVersion`, so a reader stays consistent with its own committed
view and never sees fresher data.
- **`LatestBatchCache`** — freshness first: version-blind, it serves the
most recent announced state-change batch on top of the caller's tx,
deliberately showing announced-but-not-yet-committed account data;
staleness is bounded to one batch by FCU serialization.

That fork is what decides which cache is safe where (in-process txpool
vs RPC reads — see erigontech#22532 / erigontech#22269), and the new name carries it:
content (the latest batch), lifetime (replaced wholesale per batch), and
the trade-off (by contrast with `Coherent`). Also updates the type doc
comment to lead with the contract.

One cleanup rider in the same spirit: the dead `Evict() int { return 0
}` stub is removed (no callers, not part of the `Cache` interface — a
`DummyCache`-era leftover).

Mechanical rename + dead-code removal, no behavior change (TDD not
applicable).

---------

Co-authored-by: Alex Sharov <AskAlexSharov@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants