commitment: refactor fold() into smaller methods#20152
Conversation
Extract foldDelete, foldPropagate, foldBranch from 258-line fold(). Add Benchmark_HexPatriciaHashed_fold for targeted measurement. No behavior changes — all tests pass, race-clean, same performance. fold() 258 lines → ~50 lines + 3 focused methods. Co-Authored-By: Shuo <shuo@erigon.dev>
yperbasis
left a comment
There was a problem hiding this comment.
Bug: foldPropagate now evicts cache (not in original)
foldPropagate calls collectDeleteUpdate(updateKey, row, true) — the true means "evict cache". But the original propagate case had no cache eviction:
Original (hex_patricia_hashed.go:2020-2024 on main):
if hph.branchBefore[row] {
_, err := hph.branchEncoder.CollectUpdate(hph.ctx, updateKey, 0, hph.touchMap[row], 0, RetrieveCellNoop)
if err != nil {
return fmt.Errorf("failed to encode leaf node update: %w", err)
}
// ← no EvictBranch here
}
Refactored (foldPropagate):
if err := hph.collectDeleteUpdate(updateKey, row, true); err != nil { // ← true = evict
This should be false. The spec document even identified this as a risk in Task 2:
▎ "Note: the delete case has the cache eviction, propagate case does not — verify whether propagate should also evict"
In practice this is likely benign (the branch is being deleted anyway so evicting stale cache is harmless), but the PR states "No behavior changes" — and this is one. Fix: change true to false in the
foldPropagate call.
Should not check in plan documents
docs/plans/completed/20260325-fold-refactor-objective.md (35 lines) and docs/plans/completed/20260325-fold-refactor-spec.md (371 lines) are development process artifacts. The PR description already captures the
rationale and approach. Checking 406 lines of planning docs into the repository adds maintenance burden without serving future readers — anyone looking at this code will read the commit message and PR, not a
plans directory. Remove these files from the PR.
Nits (non-blocking)
- "save" commit message — the second commit (1f05bbc) has the message "save". Squash or rewrite before merge.
- childNibble naming in foldPropagate — nice improvement over the original's implicit shadowing of nibble. The original code declared nibble := bits.TrailingZeros16(...) which shadowed the outer nibble int16
parameter. Using childNibble makes the intent explicit. - feedBranchHashesToKeccak trace logging — the original non-deferred path logged empty(%d, %x, depth=%d) for trailing empty cells written to keccak2, but feedBranchHashesToKeccak (used by the deferred path)
doesn't. This matches the original behavior (the deferred path never had that logging), so no issue — just noting it for clarity. - Epilogue consolidation is correct — verified that max(upDepth-1, 0) is semantically equivalent to the original if upDepth > 0 { upDepth-1 } else { 0 } pattern across all three cases, including type safety
(upDepth is int16, untyped 0 promotes correctly). - nibblesLeftAfterUpdate recomputation is correct — foldBranch recomputes via bits.OnesCount16(hph.afterMap[row]), which is the same computation afterMapUpdateKind performed. Only the branch case used it, so
discarding it with _ in fold() is fine.
|
@yperbasis deleted branch should be evicted from the cache, its a fix for existing bug. |
yperbasis
left a comment
There was a problem hiding this comment.
Minor nits
- Unused loop variable j in prepareBranchCells:
for bitset, j := hph.afterMap[row], 0; bitset != 0; j++ { - j is never read. Pre-existing issue from the original, but worth cleaning since we're touching this code. Replace with _ or remove.
- Double computation of nibblesLeftAfterUpdate: fold() discards it (updateKind, _ := afterMapUpdateKind(...)), then foldBranch recomputes it via bits.OnesCount16(hph.afterMap[row]). Not a bug — the values are
identical — but you could pass it through to avoid the redundant call.
Break the 258-line
fold()into focused methods for readability and maintainability.Changes
Extract three methods from
fold():foldDelete()— handle deletion propagation (~40 lines)foldPropagate()— single-child fuse/extension (~80 lines)foldBranch()— multi-child branch encoding (~100 lines)fold()becomes a ~50-line dispatcher.Added
Benchmark_HexPatriciaHashed_foldfor targeted measurement.Correctness
Performance
No regression — profiled with
-benchtime=10s:foldcumulative: 4.61s → 4.43s (identical)Co-Authored-By: Shuo shuo@erigon.dev