Skip to content

db/state, execution/commitment: fix parallel-commitment worker reads + add COMMITMENT_PARALLEL toggle - #22354

Merged
AskAlexSharov merged 5 commits into
mainfrom
mh/parallel-commitment-file-pin
Jul 12, 2026
Merged

db/state, execution/commitment: fix parallel-commitment worker reads + add COMMITMENT_PARALLEL toggle#22354
AskAlexSharov merged 5 commits into
mainfrom
mh/parallel-commitment-file-pin

Conversation

@mh0lt

@mh0lt mh0lt commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

What

Splits the parallel-commitment Go changes out of #22141 so they land in main
independently, leaving that PR as a CI-only change. Three commits:

  1. defer parallel trie selection to EnableParaTrieDB — with parallel
    commitment enabled, every SharedDomains selected the parallel trie, but only
    DB-backed consumers (exec, builder, squeeze, backtester) wire the per-worker
    TrieContextFactory it needs; integrity checks, RPC-created domains, and test
    harnesses panicked with ParallelPatriciaHashed.Process requires a TrieContextFactory. The context now starts on the sequential trie and upgrades
    when EnableParaTrieDB provides the DB.

  2. pin file generation for parallel-commitment worker reads — workers each
    opened a fresh BeginTemporalRo, pinning a possibly-newer aggregator visible-file
    generation than the main commitment tx. A deleted/reincarnated account then read
    AccountsDomain=empty (in-memory overlay) but CodeDomain=stale (worker's newer
    file view), failing the ERIGON_ASSERT code-hash consistency check and wedging the
    node. Adds a files-pin API (AggregatorRoTx.PinAggregatorFilesPin,
    kv.TemporalFilesPin); ComputeCommitment pins the main tx's generation and opens
    worker txns from it. The pin is restricted to the ParallelPatriciaHashed fold
    factory (which computes the root); serial/streaming warmup keeps its independent
    snapshot, so the serial path is behaviorally identical.

  3. add COMMITMENT_PARALLEL env togglestate_schema.go reads
    COMMITMENT_PARALLEL via dbg.EnvBool (mirrors EXEC3_PARALLEL). Defaults
    false
    — behaviour on main is unchanged unless the env var / CLI flag turns it
    on. The integration command's BoolVars now default to the env-derived value
    instead of clobbering it at registration.

Why

The parallel-commitment CI axis added in #22141 exposed the two correctness bugs
above. They belong in main on their own so #22141 stays a pure CI change; once this
merges, #22141 re-merges main and the Go drops out of its diff.

Testing

  • make erigon integration — builds.
  • make lint — 0 issues.
  • go test ./db/state/ -run 'TestAggregator|VisibleFrom|FilesPin|Pin' — pass (incl.
    the new aggregator_visible_from_test.go).
  • ERIGON_COMMITMENT_PARALLEL=true go test ./execution/commitment/... ./execution/execmodule/... ./db/state/ — pass (exercises the DB-backed parallel-commitment path that previously panicked/wedged).

With COMMITMENT_PARALLEL set globally, every SharedDomains selected the
parallel trie, but only DB-backed consumers (exec, builder, squeeze,
backtester) wire the per-worker TrieContextFactory it needs — integrity
checks, RPC-created domains, and test harnesses failed with
'ParallelPatriciaHashed.Process requires a TrieContextFactory'.

The context now starts on the sequential trie and upgrades to the
selected parallel/streaming variant when EnableParaTrieDB provides the
DB. SeekCommitment may restore state before the DB is wired, so the
upgrade adopts the already-restored trie as the parallel template
instead of re-encoding (SetState re-reads a sole-account root through
the not-yet-installed context). Touching before the upgrade panics:
keys collected on the sequential buffer would be dropped.

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 fixes correctness issues in the parallel commitment pipeline by (1) deferring selection of parallel/streaming tries until a DB-backed TrieContextFactory is available, (2) pinning the aggregator visible-file generation so parallel worker reads stay generation-consistent with the main commitment transaction, and (3) adding an env-driven toggle (COMMITMENT_PARALLEL, default off) for enabling parallel commitment without changing default behavior on main.

Changes:

  • Add a “pending variant” mechanism so contexts start on the sequential trie and upgrade to parallel/streaming only when EnableParaTrieDB wires the DB.
  • Introduce a refcounted file-generation pin (AggregatorRoTx.PinAggregatorFilesPin, kv.TemporalFilesPin) and use it to open parallel worker temporal txns on the same snapshot as the main commitment tx.
  • Initialize statecfg.ExperimentalParallelCommitment from dbg.EnvBool("COMMITMENT_PARALLEL", false) and ensure CLI flag defaults don’t overwrite the env-derived value.

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
execution/commitment/parallel_patricia_hashed.go Adds AdoptRootTrie to carry restored state across sequential→parallel trie upgrade.
execution/commitment/commitmentdb/commitment_context.go Defers parallel/streaming trie selection until DB is available; pins file-generation for worker reads; adds worker-tx helper.
db/state/statecfg/state_schema.go Reads COMMITMENT_PARALLEL env var to initialize ExperimentalParallelCommitment (default false).
db/state/aggregator.go Adds AggregatorFilesPin and pin-based BeginFilesRo spawning to keep reads on a consistent visible generation.
db/state/aggregator_visible_from_test.go Adds a test asserting pinned worker reads stay on the source visible generation.
db/kv/temporal/kv_temporal.go Implements Pin() kv.TemporalFilesPin for temporal txns, enabling pinned worker BeginTemporalRo.
db/kv/membatchwithdb/memory_mutation.go Forwards optional pinning capability through overlay tx wrappers.
db/kv/kv_interface.go Introduces the TemporalFilesPin interface (optional capability via type assertion).
cmd/integration/commands/flags.go Uses env-derived defaults when registering experimental commitment flags.

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

Comment thread db/kv/membatchwithdb/memory_mutation.go Outdated
Comment on lines +118 to +129
// Pin forwards the files-pin capability to the underlying tx so that parallel
// commitment workers reading through an overlay view still pin the same file
// generation as the main read — domain (Accounts/Code/Storage) reads fall
// through the overlay to the underlying tx, so pinning it keeps worker reads
// consistent. Returns nil when the underlying tx can't pin files, letting the
// caller fall back to an independent snapshot.
func (m *MemoryMutation) Pin() kv.TemporalFilesPin {
if p, ok := m.db.(interface{ Pin() kv.TemporalFilesPin }); ok {
return p.Pin()
}
return nil
}
Comment thread execution/commitment/commitmentdb/commitment_context.go
Comment thread db/state/aggregator_visible_from_test.go Outdated
awskii and others added 2 commits July 9, 2026 13:39
…el-commitment worker reads

Parallel-commitment workers each opened a fresh BeginTemporalRo, pinning
whatever aggregator visible-file generation was current — possibly newer than
the main commitment tx. A deleted/reincarnated account then read
AccountsDomain=empty (from the in-memory overlay) but CodeDomain=stale (from the
worker's newer file view), failing the ERIGON_ASSERT code-hash consistency check
and wedging the node. Serial commitment reads through one tx and stayed
consistent, so this only surfaced once CI began exercising the
parallel-commitment axis.

Add a files-pin API (AggregatorRoTx.Pin -> AggregatorFilesPin,
kv.TemporalFilesPin): ComputeCommitment pins the main tx's file generation and
opens worker read txns from it, so all concurrent workers observe the snapshot
the in-memory overlay was built against. Forwarded through the block-overlay
wrapper for the builder path; falls back (with a warning) for backends that
can't pin files. Worker txns read only state domains via aggtx, so they need
just the pinned file snapshot (no forkaggs). The pin is restricted to the
ParallelPatriciaHashed fold factory (which computes the root and had the torn
Accounts/Code read); serial/streaming warmup keeps its independent snapshot, so
the serial path is behaviorally identical to before the pin.
state_schema.go reads COMMITMENT_PARALLEL via dbg.EnvBool (envLookup
auto-prepends ERIGON_), mirroring EXEC3_PARALLEL, so parallel commitment can be
toggled without a code change. Defaults false — behaviour is unchanged unless
the env var (or the existing CLI flag) turns it on. The integration command's
BoolVars now default to the env-derived value instead of clobbering it to the
flag default at registration.
@mh0lt
mh0lt force-pushed the mh/parallel-commitment-file-pin branch from 6db3f5a to 80033cc Compare July 9, 2026 13:40
…omments

MemoryMutation.Pin read m.db without the RLock its documented mu contract
requires (UpdateTxn writes m.db under Lock) — a latent race. Take the read lock.

Also correct comments the review surfaced: the warmup default branch is
serial-only (streaming is a *ParallelPatriciaHashed and takes the pinned branch
above), the RootTrie doc had drifted onto AdoptRootTrie leaving RootTrie
undocumented, and the pin test named a non-existent BeginFilesRoFrom.

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

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

Comment on lines +102 to +106
// AdoptRootTrie replaces the template with a trie that already carries state
// (e.g. restored before the variant upgrade); the previous trie must not be used after.
func (p *ParallelPatriciaHashed) AdoptRootTrie(root *HexPatriciaHashed) {
p.template = root
}

@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.

Reviewed at 5f51209. Ran the new files-pin test plus ERIGON_COMMITMENT_PARALLEL=true runs of commitmentdb/execctx at the PR head — all pass. Findings, none blocking:

  1. Merge-main follow-up: #22353 (landed after this branched) added tx.blocktx = db.beginBlockFilesRo() to DB.BeginTemporalRo. temporalFilesPin.BeginTemporalRo hand-duplicates the tx construction and will merge cleanly without it — harmless while blockFiles is dormant, but pin-created worker txns will silently lack the block-files view once it's enabled. When merging main, mirror the assignment (or factor a shared tx constructor so future fields can't drift).

  2. Latent lifetime hazard: the factory stored via trie.SetTrieContextFactory captures workerPin, which the deferred Close() releases when ComputeCommitment returns; any later invocation of the stored factory nil-derefs in AggregatorFilesPin.BeginFilesRo (p.v.refcnt.Add(1) on nil). Unreachable today — the factory only runs inside Process, and StreamingCommitter.StartScheduler has no production callers — but streaming's background eager folds will hit it. Cheap hardening: defer trie.SetTrieContextFactory(nil) in ComputeCommitment (turns future misuse into the existing loud "requires a TrieContextFactory" error), or nil-guard the closed pin with a clear panic message.

  3. Test gap: the deferred-selection fix has a dedicated test for the wired path (TestSharedDomains_ParallelFlag_RootEquivalence) but none for the unwired consumer contract that actually broke (parallel flag on, no EnableParaTrieDB → stays sequential, commitment succeeds). I verified it manually at the PR head; a small test next to the equivalence test would pin it.

  4. Minor: the anonymous interface{ Pin() kv.TemporalFilesPin } is spelled out twice (memory_mutation.go and filesPinner in commitment_context.go) — a named optional-capability interface in kv next to TemporalFilesPin would keep the contract in one place. Also aggregator_visible_from_test.go would read better as aggregator_files_pin_test.go to match its content.

@awskii

awskii commented Jul 10, 2026

Copy link
Copy Markdown
Member

my version didnt passed ci though #22360

@AskAlexSharov
AskAlexSharov added this pull request to the merge queue Jul 12, 2026
Merged via the queue into main with commit d184f8a Jul 12, 2026
92 checks passed
@AskAlexSharov
AskAlexSharov deleted the mh/parallel-commitment-file-pin branch July 12, 2026 09:25
AskAlexSharov added a commit that referenced this pull request Jul 16, 2026
The base branch regenerates the {0,1} step while src still holds those files
open. POSIX allows rename-over-open-file so Linux and macOS pass, but Windows
denies it and the test fails in both matrix legs:

  renaming: rename v1.0-accounts.0-1.kv.<n>.tmp v1.0-accounts.0-1.kv:
  Access is denied.

Only add the {1,2} step: publishing a newer generation is all the test needs,
and the pin assertions are unchanged.

This is the same fix main already carries via #22354 and belongs to the base
branch (#22141), not here; it is ported so this PR's own CI is reviewable.
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.

5 participants