db/state: cache findShortenedKey per commitmentValTransformDomain merge#21303
Open
yperbasis wants to merge 3 commits into
Open
db/state: cache findShortenedKey per commitmentValTransformDomain merge#21303yperbasis wants to merge 3 commits into
yperbasis wants to merge 3 commits into
Conversation
…main merge Profiling tip-mode `[snapshots] aggregated step=X took=Ys` bursts on mainnet identified `DomainRoTx.commitmentValTransformDomain → BranchData.ReplacePlainKeys` → findShortenedKey → BtIndex.Get as ~52 % of merge CPU. The merged storage and account files are searched once per plain-key reference per commitment branch, and on mainnet hot contracts (USDC, WETH, Uniswap routers, etc.) recur across many branches in the same merge range — same full key hashed many times into the same merged file's B-tree. Add two per-closure maps that memoise the storage- and account-key lookups within one commitmentValTransformDomain invocation. The merged file is read-only for the lifetime of the closure, so (fullKey → offset) is invariant; cache reset isn't needed and bounding the size would only matter for very high-cardinality merge ranges (unbounded growth is bounded naturally by the unique-key count in the input). Lost-key path is left uncached since it's a Crit-logged error condition. Aims to shrink aggregation wall time (10-15s observed on this hardware) and therefore the FCU latency elevation that overlaps it.
Contributor
There was a problem hiding this comment.
Pull request overview
This PR optimizes commitment-domain merges by memoizing findShortenedKey results within a single commitmentValTransformDomain transformer, reducing repeated B-tree lookups when the same account/storage plain keys recur across many commitment branches in the same merge range.
Changes:
- Add per-merge in-memory caches for
(fullKey → offset)lookups for both storage and account keys. - Use the caches inside the branch value replacer to avoid repeated
findShortenedKey → BtIndex.Getcalls.
Comments suppressed due to low confidence (1)
db/state/domain_committed.go:410
- In the account replacer, the Crit log prints
hex.EncodeToString(shortened), butshortenedis the shared output buffer and may not correspond to the key being processed (it can contain a previous replacement). For accurate diagnostics, log the inputkey(and keepauxBufas the full key) instead.
dt.d.logger.Crit("valTransform: replacement for full account key was not found",
"step", fmt.Sprintf("%d-%d", keyFromTxNum/dt.d.stepSize, keyEndTxNum/dt.d.stepSize),
"shortened", hex.EncodeToString(shortened), "toReplace", hex.EncodeToString(auxBuf))
return nil, fmt.Errorf("replacement not found for account %x", auxBuf)
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
…nsformDomain Crit The "shortened" field in the "replacement not found" Crit logs was printing the function-scope `shortened` buffer, which is overwritten on every successful EncodeReferenceKey call and therefore holds the previous successful replacement (or the zero-initialised buffer) at the moment the failure log fires — unrelated to the key being diagnosed. Log the input `key` (the shortened reference from the branch) instead. Pre-existing issue, surfaced by Copilot review on #21303. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Giulio2002
approved these changes
May 24, 2026
Collaborator
Giulio2002
left a comment
There was a problem hiding this comment.
LGTM — bounded maintenance change (63 lines)
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.
Summary
Memoise
findShortenedKeylookups within a singlecommitmentValTransformDomainclosure. The merged storage and account files are read-only for the lifetime of the closure, so(fullKey → offset)is invariant — and on mainnet, hot contracts (USDC, WETH, Uniswap routers, etc.) recur across many commitment branches in the same merge range, so the same full keys are hashed into the same merged B-tree many times.Why
CPU profiling on mainnet tip identified
DomainRoTx.commitmentValTransformDomain → BranchData.ReplacePlainKeys → findShortenedKey → BtIndex.Getas ~52 % of merge CPU during[snapshots] aggregatedbursts. That's the per-branch plain-key-reference re-pointing pass — for every branch in the merge range, every account/storage key referenced inside it has to be re-located in the newly-merged file's B-tree.Verified locally on mainnet (live tip, single-step aggregation)
Same datadir, same NVMe, back-to-back runs:
[snapshots] aggregated took=(single step)commit=peak during burstDomainRoTx.mergeFilescum CPU during burstcommitmentValTransformDomaincum CPUInside the replacer closure post-patch,
runtime.mapaccess2_faststr(cache hits) is 19.94 % vsfindShortenedKey(cache misses) 76.79 % — most lookups now hit the per-merge map.findShortenedKey → BtIndex.Getno longer appears in the top-25 cumulative.Wall-time only drops ~15 % even though
commitmentValTransformDomainfalls ~65 % because the merge runs multiple domains in parallel viaerrgroup.Go— commitment is one domain among several. The per-merge CPU pressure reduction is what shows up in the FCU side: the burst's amplification of concurrent FCU commits goes from ~+370 ms over baseline (569 vs ~200) to ~+115 ms (314 vs ~200).Implementation notes
map[string]uint64instances (storage and account). No cross-merge state.string(auxBuf)copies bytes into the map key — safe against the reusedkeyBufslice.Test plan
go test -short ./db/state/...passesmake lintclean (twice — non-deterministic)make erigonbuildstook=dropped and concurrent FCU peak commit dropped (numbers above)🤖 Generated with Claude Code