Skip to content

execution/cache: a failed RwTx commit leaves applied-but-never-committed state in StateCache #23006

Description

@yperbasis

Problem

The cache-apply runs before the MDBX commit: SharedDomains.Commit flushes and applies the pending updates to the shared StateCache (advancing appliedEnd), and the caller then commits the RwTx (runForkchoiceFlushCommit in the FCU flow; SetHead and the integration/custom-trace commit paths have the same shape). If that commit fails and the process survives:

  1. The DB rolls back to the pre-block state.
  2. The cache keeps the post-block values, with appliedEnd advanced — applied-but-never-committed state, presented as the authority.
  3. Erigon re-executes the same block. A fresh SharedDomains has an empty mem overlay, so reads hit the poisoned cache entries and execution runs on top of the block's own post-state — wrong results until a commitment mismatch forces an unwind (which does clean the poison: epoch bump + floor + lowered appliedEnd).
  4. Embedded RPC at latest serves the never-committed values from cache hits in the meantime.

Fills are only over-rejected (new views sit below the inflated appliedEnd), so the fill-admission gate from #22444 is not bypassed — the gap is that the authority itself is untruthful after a failed commit.

This is the third shape in the #22356 family: not a stale fill (forward, fixed by #22444) and not a dead-fork refill (#22463), but applied-but-never-committed state that is neither unwound nor evicted.

Qualifiers

  • Pre-existing: the flush-time cache writes predate execution, db: bind StateCache fills to transaction views and reject stale fills #22444 (that PR changed the fill side, not the apply timing), so this is main's exposure too.
  • Severity is gated by recovery behavior: an MDBX commit failure at FCU is often fatal to the process, and a process death clears the in-memory cache. The hazard exists only where the node survives the error and retries.

Fix directions

  1. Failed commit = unwind of the never-committed range (preferred near-term): on the commit-error path, call Applier.Unwind(pre-block txNum) — poison evicted via epoch/floor, appliedEnd lowered, admission consistent. Small and local to the commit call sites; needs a red test of the shape: apply → fail the commit → assert hits return nothing and appliedEnd is lowered.
  2. Apply only after a successful commit (structural): moves the authority update behind durability. Interacts with the background-commit design in execution: FCU background-commit worker + generation-chain reader consistency #21414 (the apply would move to the bg worker and lag the next FCU, which chains reads through the mem overlay), so it needs joint design with that stack.

Refs: #22444 (fill admission and the apply/appliedEnd vocabulary), #22463 (unwind direction), #21414 (background commit widens the window where the ordering of apply vs commit matters).

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions