Skip to content

db/kv/membatchwithdb: delegate HistorySeek / HistoryRange to backing tx#20624

Merged
AskAlexSharov merged 3 commits into
mainfrom
fix/memorymutation-historyseek-delegate
Apr 18, 2026
Merged

db/kv/membatchwithdb: delegate HistorySeek / HistoryRange to backing tx#20624
AskAlexSharov merged 3 commits into
mainfrom
fix/memorymutation-historyseek-delegate

Conversation

@mh0lt

@mh0lt mh0lt commented Apr 17, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Fix a panic(\"not supported\") that aborts post-Fusaka mainnet sync resumed from snapshots-only (chaindata wiped, snapshots kept).
  • MemoryMutation.HistorySeek and HistoryRange were placeholders; the correct delegation was already commented out. This PR just flips them to delegate to the backing tx, matching every sibling temporal-read method.

Repro

On a post-Fusaka mainnet datadir, keep snapshots/, wipe chaindata/, and start erigon. The first forkchoice-triggered execution resumes mid-block (startTxIndex > 0) and takes the partial-block receipt reconstruction path added in #20452. That calls receipts.DerivePriorReceipts, which on top of the RCacheV2 fast path (#20485) issues tx.HistorySeek(kv.RCacheDomain, …). When the containing executor runs through ExecModule.updateForkChoice, the tx is the block overlay (MemoryMutation), and HistorySeek panics:

panic: not supported

goroutine … [running]:
…/db/kv/membatchwithdb.(*MemoryMutation).HistorySeek
  .../db/kv/membatchwithdb/memory_mutation.go:872
…/db/rawdb.ReadReceiptCacheV2
  .../db/rawdb/accessors_chain.go:1276
…/execution/receipts.DerivePriorReceipts
  .../execution/receipts/derive.go:193
…/execution/stagedsync.(*serialExecutor).executeBlock.func2
  .../execution/stagedsync/exec3_serial.go:402
…
…/execution/execmodule.(*ExecModule).updateForkChoice
  .../execution/execmodule/forkchoice.go:473

Why delegation is correct (not a workaround)

  • Every other temporal-read method on MemoryMutation already delegates to the backing tx: GetLatest, GetAsOf, RangeAsOf, IndexRange, HistoryStartFrom. The two outliers just hadn't been wired.
  • The block overlay never writes to history / domain tables — its scope is block-level metadata (headers, TDs, bodies, canonical hashes, BAL bytes, see execution/execmodule/inserters.go). History writes flow through DomainBufferedWriter / SharedDomains, not through the overlay. grep -rnE \"indexKeys|historyKey|InvertedIndexWriter|DomainBufferedWriter\" db/kv/membatchwithdb/ is empty.
  • Any HistorySeek routed through the overlay is therefore asking for data that lives exclusively in committed DB state or frozen snapshot files. The base tx is authoritative; the overlay has nothing to add.
  • The original author even left the correct call commented out on the same line.

Test plan

  • make lint — 0 issues
  • make erigon — builds clean
  • go test -short ./db/kv/membatchwithdb/... — passes
  • Manual: post-Fusaka mainnet, wipe chaindata/ / keep snapshots/, restart — sync now progresses past the first forkchoice-triggered execution instead of panicking (next hit in our testing was the unrelated stale-read bug tracked in follow-up).

🤖 Generated with Claude Code

MemoryMutation.HistorySeek and HistoryRange were left as
`panic("not supported")` placeholders with the correct delegation
commented out. Every sibling temporal-read method on MemoryMutation
already delegates to the backing tx — GetLatest, GetAsOf, RangeAsOf,
IndexRange, HistoryStartFrom. The two outliers just hadn't been
wired when nothing called them.

A caller appeared in PR #20485 (RCacheV2 fast path in
DerivePriorReceipts): when a serial-executor block is reached via
ExecModule.updateForkChoice, reads flow through the block overlay
(MemoryMutation). ReadReceiptCacheV2 issues a HistorySeek on the
RCacheDomain to read a receipt for a txNum strictly older than the
block being processed. That panics the executor at
execution/stagedsync/exec3_serial.go:402 via DerivePriorReceipts.

Delegating to the backing tx is correct, not a workaround:

- The overlay never writes to history / domain tables
  (grep -rnE "indexKeys|historyKey|InvertedIndexWriter|DomainBufferedWriter"
  in db/kv/membatchwithdb/ is empty). The overlay's scope is
  block-level metadata: headers, TDs, bodies, canonical hashes,
  BAL bytes (see execution/execmodule/inserters.go).
- History writes take the DomainBufferedWriter / SharedDomains path,
  which is a separate layer. Any HistorySeek routed through the
  overlay is asking for data that lives exclusively in committed
  DB state or frozen snapshot files — the base tx is authoritative.
- The commented-out line in the original code already contained the
  correct implementation.

Repro: wipe chaindata/ while retaining snapshots/ (post-Fusaka
mainnet) and restart. The first forkchoice-triggered execution
resumes mid-block (startTxIndex > 0), hits
DerivePriorReceipts -> ReadReceiptCacheV2 -> HistorySeek -> panic.

After this change the overlay forwards both calls to m.db and the
sync makes progress.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

@yperbasis yperbasis left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggestions (minor, non-blocking)

  1. HistoryStartFrom precedent: it returns 0 for the detached case (line 888-893), while the new methods return an error. The error path is the right call here (silent zero would mask
    bugs for value-returning lookups), but worth a sanity check that no caller treats HistorySeek/HistoryRange errors as "panic" equivalents elsewhere.
  2. Message wording: "MemoryMutation: history read requires backing tx (detached overlay)" — fine, but you could distinguish HistorySeek vs HistoryRange in the message if you ever need
    to grep panicking call sites. Not important.

@AskAlexSharov AskAlexSharov enabled auto-merge April 18, 2026 10:14
@AskAlexSharov AskAlexSharov added this pull request to the merge queue Apr 18, 2026
Merged via the queue into main with commit a5fb044 Apr 18, 2026
67 of 68 checks passed
@AskAlexSharov AskAlexSharov deleted the fix/memorymutation-historyseek-delegate branch April 18, 2026 12:32
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.

3 participants