-
-
Notifications
You must be signed in to change notification settings - Fork 457
feat: cache the last 2 PayloadEnvelopeInputs #9260
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 62 commits
a791964
3958681
a912bc8
03c4349
d3d74a9
40b5f9f
da7cc41
e49b699
813cb52
845bec9
1d15ece
c3d429d
afbb9c1
311eeca
e2d3ad2
c3c73b4
bc621c3
f4d4021
63e5271
06ab9c1
6552bc8
e10d287
5421487
b5937ec
a0bbecf
941a710
532ffcc
16941ff
6b08376
8967ae5
5b1e344
9a9fe6b
b07b363
bf7009e
d2424d5
9bf5103
08b2712
e62a7a1
8acf7c8
24a3da5
a04a4d0
2fa1c4f
b89af0b
c2ef8aa
78a7201
f8c73af
e399921
56f686c
7728204
11f9d16
2054188
2a7d8e6
372f485
6218a8e
e450f52
232e294
e7759b0
c06c946
5ae6863
783d8d3
89a60b7
ba90d3d
d7bdb55
4370934
a8b0d5e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,6 @@ | ||
| import {CheckpointWithHex} from "@lodestar/fork-choice"; | ||
| import {computeStartSlotAtEpoch} from "@lodestar/state-transition"; | ||
| import {RootHex} from "@lodestar/types"; | ||
| import {RootHex, Slot} from "@lodestar/types"; | ||
| import {Logger} from "@lodestar/utils"; | ||
| import {Metrics} from "../../metrics/metrics.js"; | ||
| import {SerializedCache} from "../../util/serializedCache.js"; | ||
|
|
@@ -21,8 +21,20 @@ export type SeenPayloadEnvelopeInputModules = { | |
| /** | ||
| * Cache for tracking PayloadEnvelopeInput instances, keyed by beacon block root. | ||
| * | ||
| * Created during block import when a block is processed. | ||
| * Pruned on finalization and after payload is written to DB. | ||
| * Created during block import when a block is processed. Two pruning paths: | ||
| * - `prepareNextSlot` calls `pruneBelow(headParentSlot)` every slot once the head we'll build | ||
| * on is known. | ||
| * - `onFinalized` calls `pruneBelow(finalizedSlot)` on every finalization for bulk cleanup. | ||
| * | ||
| * Steady state (linear chain, healthy progression): the cache holds ~2 entries — the head | ||
| * (parent for next-slot production) and its parent (proposer-boost-reorg fallback). It can | ||
| * transiently hold more during forks, range-sync bursts, or when `prepareNextSlot` skips | ||
| * ticks; subsequent ticks settle it back. | ||
| * | ||
| * Consumers that miss the cache fall back to DB (`chain.getParentExecutionRequests` / | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. not yet, will be in the next range sync PR |
||
| * `getExecutionPayloadEnvelope`). The authoritative view of "does this block / payload exist | ||
| * in the canonical chain" is `forkChoice` — this cache is a latency optimisation for the | ||
| * synchronous fast path, not a source of truth. | ||
| */ | ||
| export class SeenPayloadEnvelopeInput { | ||
| private readonly chainEvents: ChainEventEmitter; | ||
|
|
@@ -58,16 +70,7 @@ export class SeenPayloadEnvelopeInput { | |
| } | ||
|
|
||
| private onFinalized = (checkpoint: CheckpointWithHex): void => { | ||
| // Prune all entries with slot < finalized slot | ||
| const finalizedSlot = computeStartSlotAtEpoch(checkpoint.epoch); | ||
| let deletedCount = 0; | ||
| for (const [, input] of this.payloadInputs) { | ||
| if (input.slot < finalizedSlot) { | ||
| this.evictPayloadInput(input); | ||
| deletedCount++; | ||
| } | ||
| } | ||
| this.logger?.debug("SeenPayloadEnvelopeInput.onFinalized deleted cached entries", {deletedCount}); | ||
| this.pruneBelow(computeStartSlotAtEpoch(checkpoint.epoch)); | ||
| }; | ||
|
|
||
| add(props: CreateFromBlockProps): PayloadEnvelopeInput { | ||
|
|
@@ -88,17 +91,21 @@ export class SeenPayloadEnvelopeInput { | |
| return this.payloadInputs.get(blockRootHex)?.hasPayloadEnvelope() ?? false; | ||
| } | ||
|
|
||
| prune(blockRootHex: RootHex): void { | ||
| const payloadInput = this.payloadInputs.get(blockRootHex); | ||
| if (payloadInput) { | ||
| this.evictPayloadInput(payloadInput); | ||
| } | ||
| } | ||
|
|
||
| size(): number { | ||
| return this.payloadInputs.size; | ||
| } | ||
|
|
||
| pruneBelow(slot: Slot): void { | ||
| let deletedCount = 0; | ||
| for (const [, input] of this.payloadInputs) { | ||
| if (input.slot < slot) { | ||
| this.evictPayloadInput(input); | ||
| deletedCount++; | ||
| } | ||
| } | ||
| this.logger?.debug("SeenPayloadEnvelopeInput.pruneBelow deleted entries", {slot, deletedCount}); | ||
| } | ||
|
|
||
| private evictPayloadInput(payloadInput: PayloadEnvelopeInput): void { | ||
| this.serializedCache.delete(payloadInput.getSerializedCacheKeys()); | ||
| this.payloadInputs.delete(payloadInput.blockRootHex); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -786,15 +786,15 @@ export class BeaconStateView implements IBeaconStateViewLatestFork { | |
| /** | ||
| * Spec: https://github.com/ethereum/consensus-specs/blob/v1.7.0-alpha.5/specs/gloas/validator.md#executionpayload | ||
| */ | ||
| getExpectedWithdrawalsForFullParent(envelope: gloas.SignedExecutionPayloadEnvelope): capella.Withdrawal[] { | ||
| getExpectedWithdrawalsForFullParent(executionRequests: electra.ExecutionRequests): capella.Withdrawal[] { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. is function signature change here needed? I guess it's fine to keep in minimal but not clear why this PR changes it
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. it's for range sync in produceBlockBody https://github.com/ChainSafe/lodestar/pull/9242/changes#diff-c331f7b8e7121839c55f5f20f21d1efe1375b69c6fbbe5db12a4baf1ed4acf5dR656 |
||
| const fork = this.config.getForkSeq(this.cachedState.slot); | ||
| if (fork < ForkSeq.gloas) { | ||
| throw new Error("getExpectedWithdrawalsForFullParent is not available before Gloas"); | ||
| } | ||
| // Make a copy of the state to avoid mutability issues | ||
| const stateCopy = this.cachedState.clone(true) as CachedBeaconStateGloas; | ||
| // Apply parent payload before computing withdrawals | ||
| applyParentExecutionPayload(stateCopy, envelope.message.executionRequests); | ||
| applyParentExecutionPayload(stateCopy, executionRequests); | ||
|
|
||
| return getExpectedWithdrawals(fork, stateCopy).expectedWithdrawals; | ||
| } | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
do we need to call fork choice here again?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
no need, we don't change anything here