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
feat: add historical state regen #6033
base: unstable
Are you sure you want to change the base?
Conversation
Performance Report✔️ no performance regression detected 🚀🚀 Significant benchmark improvement detected
Full benchmark results
|
packages/beacon-node/src/chain/historicalState/getHistoricalState.ts
Outdated
Show resolved
Hide resolved
Running into issues opening the db in the worker thread. It seems the leveldb way of allowing concurrency is have the consumer pass the pointer to the db handle to another thread. That's not currently possible with the leveldb package as it currently exists. |
Converting to draft until the multithreaded database access issue is resolved |
Codecov Report
Additional details and impacted files@@ Coverage Diff @@
## unstable #6033 +/- ##
=============================================
- Coverage 76.60% 60.14% -16.46%
=============================================
Files 248 407 +159
Lines 25908 46464 +20556
Branches 1448 1534 +86
=============================================
+ Hits 19846 27947 +8101
- Misses 6032 18487 +12455
Partials 30 30 |
} | ||
|
||
async function resolveStateIdOrNull( |
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.
I like this being renamed. good call!
return state; | ||
} | ||
|
||
export async function getStateResponseWithRegen( |
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.
Should this be combined with getStateResponse
. can pass a third arg allowRegen
that passes to the getStateByStateRoot
, getStateBySlot
and gates getHistoricalStateBySlot
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.
sounds good
if (stateId === "head") { | ||
// TODO: This is not OK, head and headState must be fetched atomically |
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.
Two questions:
-
is the issue about mutability in case there is a slot boundary while this resolves? Or is the issue that
getHeadState
can mutate state during its run? -
If there is risk, should we leave this comment here, maybe move to
getStateResponse
or perhaps convert it to aTODO
to make sure this doesn't get papered over?
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.
I think the issue before was that the calls were unlinked and also async.
eg:
const head = chain.forkChoice.getHead();
// what happens if the head changes before this next line
const headState = await chain.getHeadState();
Then they were unlinked but sync (no problem, stale comment).
const head = chain.forkChoice.getHead();
// no chance for the head to change before this next line
const headState = chain.getHeadState();
Now they are linked / looked up by state root
const head = chain.forkChoice.getHead();
// ...
// the head may change, but the head _at the time of request_ can still be fetched
const headState = chain.getStateByRoot(head.stateRoot);
} | ||
|
||
return stateRes; | ||
export function deserializeBeaconStateSerialized(config: ChainForkConfig, data: Uint8Array): allForks.BeaconState { |
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.
export function deserializeBeaconStateSerialized(config: ChainForkConfig, data: Uint8Array): allForks.BeaconState { | |
export function deserializeBeaconState(config: ChainForkConfig, data: Uint8Array): allForks.BeaconState { |
|
||
const db = new BeaconDb(config, await LevelDbController.create({name: workerData.dbLocation}, {logger})); | ||
|
||
const abortController = new AbortController(); |
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.
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.
Why do we actually need this AbortController here if it will not be synchronized with the main thread. The kill signal comes from the outside
config, | ||
metrics, | ||
logger: logger.child({module: LoggerModule.chain}), | ||
}); |
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.
Should the abort controller above get passed into HistoricalStateRegen.init so this shutdown is coordinated with the rest of the application? Im a bit fuzzy on how cleanup happens so this may not be correct though....
const closeMetrics = collectNodeJSMetrics(metricsRegister, "lodestar_historical_state_worker_"); | ||
abortController.signal.addEventListener("abort", closeMetrics, {once: true}); | ||
|
||
stateTransitionMetrics = { | ||
epochTransitionTime: metricsRegister.histogram({ | ||
name: "lodestar_historical_state_stfn_epoch_transition_seconds", |
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.
Doesn't lodestar_historical_state_worker_
get prepended to all of these metric names? If so we will have some super long names and the lodestar_historical_state_
will be redundant on all of them
export function getBlocksBetween(from: number, to: number, db: IBeaconDb): AsyncIterable<SignedBeaconBlock> { | ||
return db.blockArchive.valuesStream({gt: from, lte: to}); | ||
} |
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.
Is there a specific reason you broke this out? Can we just directly call db.blockArchive.valuesStream
in the for await
below and save a stack frame?
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 reason, just started the implementation of getHistoricalState in pseudocode and this stuck around. I can remove
workerData, | ||
} as ConstructorParameters<typeof Worker>[1]); | ||
|
||
// eslint-disable-next-line @typescript-eslint/no-explicit-any |
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.
I didn't notice that this needs to be here. Deleted with no issue
Any update on the status of this PR? Are we blocked on it somewhere externally? |
waiting on me to fix this in response to @matthewkeil 's review |
Motivation
Description
TODO