-
-
Notifications
You must be signed in to change notification settings - Fork 479
feat: state cache for ePBS #8868
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 all commits
26595a7
72eaedf
51b1859
35c6aaf
a89490e
ae8e627
2472145
e85deda
d5112cf
870d285
ce64de9
87dd2f6
e5d8634
98dfa02
dc12447
00ccfc5
73dd120
5987849
7efbcd8
e85b422
51869b6
bed3356
71f3b63
6c8a717
3deb79d
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,7 +1,7 @@ | ||
| import path from "node:path"; | ||
| import {ChainForkConfig} from "@lodestar/config"; | ||
| import {KeyValue} from "@lodestar/db"; | ||
| import {CheckpointWithPayloadStatus, IForkChoice} from "@lodestar/fork-choice"; | ||
| import {CheckpointWithPayloadStatus, IForkChoice, PayloadStatus, ProtoBlock} from "@lodestar/fork-choice"; | ||
| import {ForkSeq, SLOTS_PER_EPOCH} from "@lodestar/params"; | ||
| import {computeEpochAtSlot, computeStartSlotAtEpoch} from "@lodestar/state-transition"; | ||
| import {Epoch, Slot} from "@lodestar/types"; | ||
|
|
@@ -66,6 +66,7 @@ export async function archiveBlocks( | |
| // NOTE: The finalized block will be exactly the first block of `epoch` or previous | ||
| const finalizedPostDeneb = finalizedCheckpoint.epoch >= config.DENEB_FORK_EPOCH; | ||
| const finalizedPostFulu = finalizedCheckpoint.epoch >= config.FULU_FORK_EPOCH; | ||
| const finalizedPostGloas = finalizedCheckpoint.epoch >= config.GLOAS_FORK_EPOCH; | ||
|
|
||
| const finalizedCanonicalBlockRoots: BlockRootSlot[] = finalizedCanonicalBlocks.map((block) => ({ | ||
| slot: block.slot, | ||
|
|
@@ -103,6 +104,16 @@ export async function archiveBlocks( | |
| ); | ||
| logger.verbose("Migrated dataColumnSidecars from hot DB to cold DB", {...logCtx, migratedEntries}); | ||
| } | ||
|
|
||
| if (finalizedPostGloas) { | ||
| const migratedEntries = await migrateExecutionPayloadEnvelopesFromHotToColdDb( | ||
| config, | ||
| db, | ||
| logger, | ||
| finalizedCanonicalBlocks | ||
| ); | ||
| logger.verbose("Migrated executionPayloadEnvelopes from hot DB to cold DB", {...logCtx, migratedEntries}); | ||
| } | ||
| } | ||
|
|
||
| // deleteNonCanonicalBlocks | ||
|
|
@@ -144,6 +155,11 @@ export async function archiveBlocks( | |
| await db.dataColumnSidecar.deleteMany(nonCanonicalBlockRoots); | ||
| logger.verbose("Deleted non canonical dataColumnSidecars from hot DB", logCtx); | ||
| } | ||
|
|
||
| if (finalizedPostGloas) { | ||
| await db.executionPayloadEnvelope.batchDelete(nonCanonicalBlockRoots); | ||
| logger.verbose("Deleted non canonical executionPayloadEnvelopes from hot DB", logCtx); | ||
| } | ||
| } | ||
|
|
||
| // Delete expired blobs | ||
|
|
@@ -372,6 +388,48 @@ async function migrateDataColumnSidecarsFromHotToColdDb( | |
| return migratedWrappedDataColumns; | ||
| } | ||
|
|
||
| async function migrateExecutionPayloadEnvelopesFromHotToColdDb( | ||
| config: ChainForkConfig, | ||
| db: IBeaconDb, | ||
| logger: Logger, | ||
| canonicalBlocks: ProtoBlock[] | ||
| ): Promise<number> { | ||
| let migratedEnvelopes = 0; | ||
|
|
||
| const payloadBlocks = canonicalBlocks.filter( | ||
| (block) => config.getForkSeq(block.slot) >= ForkSeq.gloas && block.payloadStatus === PayloadStatus.FULL | ||
| ); | ||
| if (payloadBlocks.length === 0) return 0; | ||
| const blocks = payloadBlocks.map((block) => ({slot: block.slot, root: fromHex(block.blockRoot)})); | ||
|
|
||
| const envelopeEntries: KeyValue<Slot, Uint8Array>[] = []; | ||
| const migratedRoots: Uint8Array[] = []; | ||
|
|
||
| const envelopeBytesArray = await Promise.all( | ||
| blocks.map((block) => db.executionPayloadEnvelope.getBinary(block.root)) | ||
| ); | ||
|
|
||
| for (let i = 0; i < blocks.length; i++) { | ||
| const bytes = envelopeBytesArray[i]; | ||
| if (bytes !== null) { | ||
| envelopeEntries.push({key: blocks[i].slot, value: bytes}); | ||
| migratedRoots.push(blocks[i].root); | ||
| } else { | ||
| logger.debug("Payload in forkchoice but missing in db", {slot: blocks[i].slot, root: toRootHex(blocks[i].root)}); | ||
|
Member
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. If we have a FULL node in fork choice but we don't have payload in db.executionPayloadEnvelope, it sounds like an error not debug to me?
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. we have a convention to make it
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. agree, this doesn't need to be logged at |
||
| } | ||
| } | ||
|
ensi321 marked this conversation as resolved.
|
||
|
|
||
| if (envelopeEntries.length > 0) { | ||
| await Promise.all([ | ||
| db.executionPayloadEnvelopeArchive.batchPutBinary(envelopeEntries), | ||
| db.executionPayloadEnvelope.batchDelete(migratedRoots), | ||
| ]); | ||
| migratedEnvelopes = envelopeEntries.length; | ||
| } | ||
|
|
||
| return migratedEnvelopes; | ||
| } | ||
|
|
||
| /** | ||
| * ``` | ||
| * class SignedBeaconBlock(Container): | ||
|
|
||
This file was deleted.
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.
more of a question, do we actually wanna persist full states? since for checkpoint sync we always wanna use post block state (without payload applied) what's the use case for this?
instead of storing 2 states we could also just load post block state and apply payload if needed
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.
yes that could be a good improvement
right now in in checkpoint state cache it has no context of payload
whenever it goes out of memory windows (of 3 epochs), it persists whatever it has
the down side of not persisting payload state is when we have a finalized checkpoint of > 3 epochs ago, we may not have the state, we have to load post block Uint8Array state, deserialize and apply payload