Skip to content

perf(blockchain): make ChainHeadSpecProvider lock-free#11618

Merged
LukaszRozmej merged 2 commits into
NethermindEth:masterfrom
obchain:feature/11367-chainhead-spec-lockfree
May 18, 2026
Merged

perf(blockchain): make ChainHeadSpecProvider lock-free#11618
LukaszRozmej merged 2 commits into
NethermindEth:masterfrom
obchain:feature/11367-chainhead-spec-lockfree

Conversation

@obchain
Copy link
Copy Markdown
Contributor

@obchain obchain commented May 15, 2026

Resolves #11367

Changes

  • ChainHeadSpecProvider.GetCurrentHeadSpec is now lock-free. The cached (headerNumber, releaseSpec) pair is wrapped in an immutable CachedSpec record and published through a single reference field with Volatile.Read / Volatile.Write.
  • Removed the Lock _lock, the paired _lastHeader / _headerSpec fields, and the read-then-double-checked-write block.
  • Behaviour is preserved: a single reference read returns a fully constructed snapshot, so readers never observe a torn (number, spec) pair, and concurrent writers race to publish equivalent values for the same headerNumber (GetSpec(header) is deterministic).

Types of changes

What types of changes does your code introduce?

  • Optimization

Testing

Requires testing

  • Yes

If yes, did you write tests?

  • Yes

Notes on testing

ChainHeadSpecProviderTests (5 cases): basic resolution, single-resolve on repeated same-head call, re-resolve on head change, 32 readers x 5000 iterations against a stable head, and a head-rotation race with 16 workers asserting every observed spec belongs to the published mapping. No production hot-path callers needed updating.

Documentation

Requires documentation update

  • No

Requires explanation in Release Notes

  • No

Replace lock + paired field updates with an immutable
(Number, Spec) record published via Volatile.Read/Volatile.Write.
GetSpec is deterministic per (number, header) so last-writer-wins
is safe. Closes NethermindEth#11367.
- Drop redundant single-call test (subsumed by the repeat-call cache test)
- Add null-header fallback test for the ForkActivation path
- Rework the head-rotation race test: cancellation-driven loop with stricter
  set-membership assertion so a torn pair surfaces as a non-matching reference
- Note in ChainHeadSpecProvider why CachedSpec must remain a record (class)
  rather than a record struct (16-byte writes are not atomic)

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

@damian-orzechowski @wurdum is this fine for Arbitrum? I remember we had some issues around that

@svlachakis svlachakis self-requested a review May 18, 2026 08:42
@LukaszRozmej LukaszRozmej merged commit 17a7f47 into NethermindEth:master May 18, 2026
680 of 684 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

TxPool: fix overly aggressive tx locking

5 participants