-
-
Notifications
You must be signed in to change notification settings - Fork 291
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
Review regen state cache strategy #2846
Comments
For lightclient proof serving we could use a dedicated cache that is enabled with a flag |
Current regen usage:
|
I agree this is a good time to review our cache and regen module, this was designed at the time testnets were unstable so it's quite conservative. Regen moduleIt's there to deal with abnormal network condition, currently the worse case is to generate a state from a finalized checkpoint, which never happen with the current good network condition. Hence I propose we generate from the justified checkpoint instead:
so I think it's safe enough to generate state from a justified checkpoint instead of a finalized checkpoint. Also we should review every use cases of it as detailed above. The cache
Since states of the same epoch are shared in memory, I propose to cache exactly 2 epochs (to be aligned with the regen module) for both caches leveraging the shared memory of persistent-merkle-tree
|
I've added some quick metrics to regen and run The StateContextCache is usually full all the time. During sync it's consistently at 96 states, when synced it varies between 70 and 96 based on the epoch's slot. CheckpointStateCache has 4 states, which makes sense too. Node heap is ~660MB. Then I've changed the cache params to |
A small analysis of state sizes. I'm using the performance states which are maxed out states with 250_000 validators
Source code: async function analyzeStateMemory(): Promise<void> {
await init("blst-native");
const tracker = new MemoryTracker();
tracker.logDiff("start");
const pubkeys = getPubkeys().pubkeys;
tracker.logDiff("getPubkeys()");
const defaultState = ssz.phase0.BeaconState.defaultValue();
tracker.logDiff(".defaultValue()");
const state = buildPerformanceStateAllForks(defaultState, pubkeys);
tracker.logDiff("build raw state");
addPendingAttestations(state as phase0.BeaconState);
tracker.logDiff("addPendingAtt");
const stateTB = ssz.phase0.BeaconState.createTreeBackedFromStruct(state as phase0.BeaconState);
tracker.logDiff("toTreeBacked");
const cached = allForks.createCachedBeaconState(config, stateTB);
tracker.logDiff("CachedBeaconState");
}
class MemoryTracker {
prev = process.memoryUsage();
logDiff(id: string): void {
const curr = process.memoryUsage();
const parts: string[] = [];
for (const key of Object.keys(this.prev) as (keyof NodeJS.MemoryUsage)[]) {
const prevVal = this.prev[key];
const currVal = curr[key];
const bytesDiff = currVal - prevVal;
const sign = bytesDiff < 0 ? "-" : bytesDiff > 0 ? "+" : " ";
parts.push(`${key} ${sign}${formatBytes(Math.abs(bytesDiff)).padEnd(15)}`);
}
this.prev = curr;
console.log(id.padEnd(20), parts.join(" "));
}
} |
This issue has been automatically marked as stale because it has not had recent activity. It will be closed in 15 days if no further activity occurs. Thank you for your contributions. |
|
From Paul (Lighthouse) in normal network conditions:
beacon_chain.canonical_head
Our state cache can reference up to 96 states. Thanks to structural sharing the total memory required is not 96 * state size tho it's a very high number.
Review the strategy to ensure that at least in normal conditions we keep just one state in memory.
The text was updated successfully, but these errors were encountered: