@lodestar/beacon-node 1.41.0-dev.0df187678b → 1.41.0-dev.165a02f873
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/chain/blocks/blockInput/blockInput.d.ts +5 -0
- package/lib/chain/blocks/blockInput/blockInput.d.ts.map +1 -1
- package/lib/chain/blocks/blockInput/blockInput.js +24 -0
- package/lib/chain/blocks/blockInput/blockInput.js.map +1 -1
- package/lib/chain/blocks/blockInput/types.d.ts +5 -0
- package/lib/chain/blocks/blockInput/types.d.ts.map +1 -1
- package/lib/chain/blocks/writeBlockInputToDb.d.ts.map +1 -1
- package/lib/chain/blocks/writeBlockInputToDb.js +1 -12
- package/lib/chain/blocks/writeBlockInputToDb.js.map +1 -1
- package/lib/chain/chain.js +2 -1
- package/lib/chain/chain.js.map +1 -1
- package/lib/chain/produceBlock/computeNewStateRoot.d.ts +0 -1
- package/lib/chain/produceBlock/computeNewStateRoot.d.ts.map +1 -1
- package/lib/chain/produceBlock/computeNewStateRoot.js +4 -3
- package/lib/chain/produceBlock/computeNewStateRoot.js.map +1 -1
- package/lib/chain/seenCache/seenGossipBlockInput.d.ts +5 -1
- package/lib/chain/seenCache/seenGossipBlockInput.d.ts.map +1 -1
- package/lib/chain/seenCache/seenGossipBlockInput.js +21 -8
- package/lib/chain/seenCache/seenGossipBlockInput.js.map +1 -1
- package/lib/metrics/metrics/lodestar.d.ts +1 -0
- package/lib/metrics/metrics/lodestar.d.ts.map +1 -1
- package/lib/metrics/metrics/lodestar.js +4 -0
- package/lib/metrics/metrics/lodestar.js.map +1 -1
- package/lib/network/network.d.ts.map +1 -1
- package/lib/network/network.js +1 -1
- package/lib/network/network.js.map +1 -1
- package/lib/util/serializedCache.d.ts +4 -4
- package/lib/util/serializedCache.d.ts.map +1 -1
- package/lib/util/serializedCache.js +6 -4
- package/lib/util/serializedCache.js.map +1 -1
- package/package.json +15 -15
- package/src/chain/blocks/blockInput/blockInput.ts +35 -0
- package/src/chain/blocks/blockInput/types.ts +5 -0
- package/src/chain/blocks/writeBlockInputToDb.ts +1 -18
- package/src/chain/chain.ts +2 -2
- package/src/chain/produceBlock/computeNewStateRoot.ts +4 -3
- package/src/chain/seenCache/seenGossipBlockInput.ts +38 -10
- package/src/metrics/metrics/lodestar.ts +4 -0
- package/src/network/network.ts +2 -1
- package/src/util/serializedCache.ts +7 -5
|
@@ -4,14 +4,14 @@
|
|
|
4
4
|
* This is a thin wrapper around WeakMap
|
|
5
5
|
*/
|
|
6
6
|
export declare class SerializedCache {
|
|
7
|
-
map
|
|
7
|
+
private map;
|
|
8
8
|
get(obj: object): Uint8Array | undefined;
|
|
9
9
|
set(obj: object, serialized: Uint8Array): void;
|
|
10
10
|
/**
|
|
11
|
-
*
|
|
12
|
-
* Must only be called after all DB writes that
|
|
11
|
+
* Delete cached serialized entries for the provided object references.
|
|
12
|
+
* Must only be called after all DB writes that read from this cache for these objects have completed,
|
|
13
13
|
* otherwise cached serialized bytes will be unavailable and data will be re-serialized unnecessarily.
|
|
14
14
|
*/
|
|
15
|
-
|
|
15
|
+
delete(objs: object[]): void;
|
|
16
16
|
}
|
|
17
17
|
//# sourceMappingURL=serializedCache.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"serializedCache.d.ts","sourceRoot":"","sources":["../../src/util/serializedCache.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,qBAAa,eAAe;IAC1B,
|
|
1
|
+
{"version":3,"file":"serializedCache.d.ts","sourceRoot":"","sources":["../../src/util/serializedCache.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,qBAAa,eAAe;IAC1B,OAAO,CAAC,GAAG,CAA8C;IAEzD,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS;IAIxC,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,UAAU,EAAE,UAAU,GAAG,IAAI;IAI9C;;;;OAIG;IACH,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI;CAK7B"}
|
|
@@ -12,12 +12,14 @@ export class SerializedCache {
|
|
|
12
12
|
this.map.set(obj, serialized);
|
|
13
13
|
}
|
|
14
14
|
/**
|
|
15
|
-
*
|
|
16
|
-
* Must only be called after all DB writes that
|
|
15
|
+
* Delete cached serialized entries for the provided object references.
|
|
16
|
+
* Must only be called after all DB writes that read from this cache for these objects have completed,
|
|
17
17
|
* otherwise cached serialized bytes will be unavailable and data will be re-serialized unnecessarily.
|
|
18
18
|
*/
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
delete(objs) {
|
|
20
|
+
for (const obj of objs) {
|
|
21
|
+
this.map.delete(obj);
|
|
22
|
+
}
|
|
21
23
|
}
|
|
22
24
|
}
|
|
23
25
|
//# sourceMappingURL=serializedCache.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"serializedCache.js","sourceRoot":"","sources":["../../src/util/serializedCache.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,OAAO,eAAe;
|
|
1
|
+
{"version":3,"file":"serializedCache.js","sourceRoot":"","sources":["../../src/util/serializedCache.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,OAAO,eAAe;IAClB,GAAG,GAAgC,IAAI,OAAO,EAAE,CAAC;IAEzD,GAAG,CAAC,GAAW;QACb,OAAO,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAC3B,CAAC;IAED,GAAG,CAAC,GAAW,EAAE,UAAsB;QACrC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,UAAU,CAAC,CAAC;IAChC,CAAC;IAED;;;;OAIG;IACH,MAAM,CAAC,IAAc;QACnB,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;YACvB,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QACvB,CAAC;IACH,CAAC;CACF"}
|
package/package.json
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"bugs": {
|
|
12
12
|
"url": "https://github.com/ChainSafe/lodestar/issues"
|
|
13
13
|
},
|
|
14
|
-
"version": "1.41.0-dev.
|
|
14
|
+
"version": "1.41.0-dev.165a02f873",
|
|
15
15
|
"type": "module",
|
|
16
16
|
"exports": {
|
|
17
17
|
".": {
|
|
@@ -134,18 +134,18 @@
|
|
|
134
134
|
"@libp2p/peer-id": "^6.0.4",
|
|
135
135
|
"@libp2p/prometheus-metrics": "^5.0.13",
|
|
136
136
|
"@libp2p/tcp": "^11.0.12",
|
|
137
|
-
"@lodestar/api": "^1.41.0-dev.
|
|
138
|
-
"@lodestar/config": "^1.41.0-dev.
|
|
139
|
-
"@lodestar/db": "^1.41.0-dev.
|
|
140
|
-
"@lodestar/fork-choice": "^1.41.0-dev.
|
|
141
|
-
"@lodestar/light-client": "^1.41.0-dev.
|
|
142
|
-
"@lodestar/logger": "^1.41.0-dev.
|
|
143
|
-
"@lodestar/params": "^1.41.0-dev.
|
|
144
|
-
"@lodestar/reqresp": "^1.41.0-dev.
|
|
145
|
-
"@lodestar/state-transition": "^1.41.0-dev.
|
|
146
|
-
"@lodestar/types": "^1.41.0-dev.
|
|
147
|
-
"@lodestar/utils": "^1.41.0-dev.
|
|
148
|
-
"@lodestar/validator": "^1.41.0-dev.
|
|
137
|
+
"@lodestar/api": "^1.41.0-dev.165a02f873",
|
|
138
|
+
"@lodestar/config": "^1.41.0-dev.165a02f873",
|
|
139
|
+
"@lodestar/db": "^1.41.0-dev.165a02f873",
|
|
140
|
+
"@lodestar/fork-choice": "^1.41.0-dev.165a02f873",
|
|
141
|
+
"@lodestar/light-client": "^1.41.0-dev.165a02f873",
|
|
142
|
+
"@lodestar/logger": "^1.41.0-dev.165a02f873",
|
|
143
|
+
"@lodestar/params": "^1.41.0-dev.165a02f873",
|
|
144
|
+
"@lodestar/reqresp": "^1.41.0-dev.165a02f873",
|
|
145
|
+
"@lodestar/state-transition": "^1.41.0-dev.165a02f873",
|
|
146
|
+
"@lodestar/types": "^1.41.0-dev.165a02f873",
|
|
147
|
+
"@lodestar/utils": "^1.41.0-dev.165a02f873",
|
|
148
|
+
"@lodestar/validator": "^1.41.0-dev.165a02f873",
|
|
149
149
|
"@multiformats/multiaddr": "^13.0.1",
|
|
150
150
|
"datastore-core": "^11.0.2",
|
|
151
151
|
"datastore-fs": "^11.0.2",
|
|
@@ -168,7 +168,7 @@
|
|
|
168
168
|
"@libp2p/interface-internal": "^3.0.12",
|
|
169
169
|
"@libp2p/logger": "^6.2.2",
|
|
170
170
|
"@libp2p/utils": "^7.0.12",
|
|
171
|
-
"@lodestar/spec-test-util": "^1.41.0-dev.
|
|
171
|
+
"@lodestar/spec-test-util": "^1.41.0-dev.165a02f873",
|
|
172
172
|
"@types/js-yaml": "^4.0.5",
|
|
173
173
|
"@types/qs": "^6.9.7",
|
|
174
174
|
"@types/tmp": "^0.2.3",
|
|
@@ -185,5 +185,5 @@
|
|
|
185
185
|
"beacon",
|
|
186
186
|
"blockchain"
|
|
187
187
|
],
|
|
188
|
-
"gitHead": "
|
|
188
|
+
"gitHead": "08a97ee173fe65d3c757a279a740741ca8bf1502"
|
|
189
189
|
}
|
|
@@ -105,6 +105,7 @@ abstract class AbstractBlockInput<F extends ForkName = ForkName, TData extends D
|
|
|
105
105
|
}
|
|
106
106
|
|
|
107
107
|
abstract addBlock(props: AddBlock<F>): void;
|
|
108
|
+
abstract getSerializedCacheKeys(): object[];
|
|
108
109
|
|
|
109
110
|
hasBlock(): boolean {
|
|
110
111
|
return this.state.hasBlock;
|
|
@@ -243,6 +244,10 @@ export class BlockInputPreData extends AbstractBlockInput<ForkPreDeneb, null> {
|
|
|
243
244
|
);
|
|
244
245
|
}
|
|
245
246
|
}
|
|
247
|
+
|
|
248
|
+
getSerializedCacheKeys(): object[] {
|
|
249
|
+
return [this.state.block];
|
|
250
|
+
}
|
|
246
251
|
}
|
|
247
252
|
|
|
248
253
|
// Blobs DA
|
|
@@ -526,6 +531,20 @@ export class BlockInputBlobs extends AbstractBlockInput<ForkBlobsDA, deneb.BlobS
|
|
|
526
531
|
getBlobs(): deneb.BlobSidecars {
|
|
527
532
|
return this.getAllBlobsWithSource().map(({blobSidecar}) => blobSidecar);
|
|
528
533
|
}
|
|
534
|
+
|
|
535
|
+
getSerializedCacheKeys(): object[] {
|
|
536
|
+
const objects: object[] = [];
|
|
537
|
+
|
|
538
|
+
if (this.state.hasBlock) {
|
|
539
|
+
objects.push(this.state.block);
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
for (const {blobSidecar} of this.blobsCache.values()) {
|
|
543
|
+
objects.push(blobSidecar);
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
return objects;
|
|
547
|
+
}
|
|
529
548
|
}
|
|
530
549
|
|
|
531
550
|
function blockAndBlobArePaired(block: SignedBeaconBlock<ForkBlobsDA>, blobSidecar: deneb.BlobSidecar): boolean {
|
|
@@ -906,6 +925,18 @@ export class BlockInputColumns extends AbstractBlockInput<ForkColumnsDA, fulu.Da
|
|
|
906
925
|
}
|
|
907
926
|
return Promise.resolve(this.getSampledColumns());
|
|
908
927
|
}
|
|
928
|
+
|
|
929
|
+
getSerializedCacheKeys(): object[] {
|
|
930
|
+
const objects: object[] = [];
|
|
931
|
+
|
|
932
|
+
if (this.state.hasBlock) {
|
|
933
|
+
objects.push(this.state.block);
|
|
934
|
+
}
|
|
935
|
+
|
|
936
|
+
objects.push(...this.getAllColumns());
|
|
937
|
+
|
|
938
|
+
return objects;
|
|
939
|
+
}
|
|
909
940
|
}
|
|
910
941
|
|
|
911
942
|
type BlockInputNoDataState = {
|
|
@@ -967,4 +998,8 @@ export class BlockInputNoData extends AbstractBlockInput<ForkPostGloas, null> {
|
|
|
967
998
|
return (this.state.block.message.body as gloas.BeaconBlockBody).signedExecutionPayloadBid.message
|
|
968
999
|
.blobKzgCommitments;
|
|
969
1000
|
}
|
|
1001
|
+
|
|
1002
|
+
getSerializedCacheKeys(): object[] {
|
|
1003
|
+
return [this.state.block];
|
|
1004
|
+
}
|
|
970
1005
|
}
|
|
@@ -151,6 +151,11 @@ export interface IBlockInput<F extends ForkName = ForkName, TData extends DAData
|
|
|
151
151
|
|
|
152
152
|
/** Only safe to call when `hasBlockAndAllData` is true */
|
|
153
153
|
getTimeComplete(): number;
|
|
154
|
+
/**
|
|
155
|
+
* Return object references used as keys in `SerializedCache` that can be safely removed
|
|
156
|
+
* once this block input lifecycle has completed.
|
|
157
|
+
*/
|
|
158
|
+
getSerializedCacheKeys(): object[];
|
|
154
159
|
|
|
155
160
|
waitForBlock(timeout: number, signal?: AbortSignal): Promise<SignedBeaconBlock<F>>;
|
|
156
161
|
waitForAllData(timeout: number, signal?: AbortSignal): Promise<TData>;
|
|
@@ -3,13 +3,7 @@ import {SignedBeaconBlock} from "@lodestar/types";
|
|
|
3
3
|
import {fromHex, toRootHex} from "@lodestar/utils";
|
|
4
4
|
import {getBlobKzgCommitments} from "../../util/dataColumns.js";
|
|
5
5
|
import {BeaconChain} from "../chain.js";
|
|
6
|
-
import {
|
|
7
|
-
IBlockInput,
|
|
8
|
-
IDataColumnsInput,
|
|
9
|
-
isBlockInputBlobs,
|
|
10
|
-
isBlockInputColumns,
|
|
11
|
-
isBlockInputNoData,
|
|
12
|
-
} from "./blockInput/index.js";
|
|
6
|
+
import {IBlockInput, IDataColumnsInput, isBlockInputBlobs, isBlockInputColumns} from "./blockInput/index.js";
|
|
13
7
|
import {BLOB_AVAILABILITY_TIMEOUT} from "./verifyBlocksDataAvailability.js";
|
|
14
8
|
|
|
15
9
|
/**
|
|
@@ -137,17 +131,6 @@ export async function persistBlockInput(this: BeaconChain, blockInput: IBlockInp
|
|
|
137
131
|
})
|
|
138
132
|
.finally(() => {
|
|
139
133
|
this.seenBlockInputCache.prune(blockInput.blockRootHex);
|
|
140
|
-
// Without forcefully clearing this cache, we would rely on WeakMap to evict memory which is not reliable.
|
|
141
|
-
// Clear here (after the DB write) so that writeBlockInputToDb can still use the cached serialized bytes.
|
|
142
|
-
//
|
|
143
|
-
// For Gloas (BlockInputNoData), the execution payload and columns arrive separately after the beacon block.
|
|
144
|
-
// Do NOT clear the cache here — it must remain available for writeDataColumnsToDb when the payload arrives.
|
|
145
|
-
// The cache is cleared in the Gloas payload persistence path instead.
|
|
146
|
-
if (!isBlockInputNoData(blockInput)) {
|
|
147
|
-
// TODO: enhance this SerializedCache for Gloas because payload may not come
|
|
148
|
-
// see https://github.com/ChainSafe/lodestar/pull/8974#discussion_r2885598229
|
|
149
|
-
this.serializedCache.clear();
|
|
150
|
-
}
|
|
151
134
|
this.logger.debug("Pruned block input", {
|
|
152
135
|
slot: blockInput.slot,
|
|
153
136
|
root: blockInput.blockRootHex,
|
package/src/chain/chain.ts
CHANGED
|
@@ -320,12 +320,14 @@ export class BeaconChain implements IBeaconChain {
|
|
|
320
320
|
|
|
321
321
|
this.beaconProposerCache = new BeaconProposerCache(opts);
|
|
322
322
|
this.checkpointBalancesCache = new CheckpointBalancesCache();
|
|
323
|
+
this.serializedCache = new SerializedCache();
|
|
323
324
|
this.seenBlockInputCache = new SeenBlockInput({
|
|
324
325
|
config,
|
|
325
326
|
custodyConfig: this.custodyConfig,
|
|
326
327
|
clock,
|
|
327
328
|
chainEvents: emitter,
|
|
328
329
|
signal,
|
|
330
|
+
serializedCache: this.serializedCache,
|
|
329
331
|
metrics,
|
|
330
332
|
logger,
|
|
331
333
|
});
|
|
@@ -412,8 +414,6 @@ export class BeaconChain implements IBeaconChain {
|
|
|
412
414
|
this.bls = bls;
|
|
413
415
|
this.emitter = emitter;
|
|
414
416
|
|
|
415
|
-
this.serializedCache = new SerializedCache();
|
|
416
|
-
|
|
417
417
|
this.getBlobsTracker = new GetBlobsTracker({
|
|
418
418
|
logger,
|
|
419
419
|
executionEngine: this.executionEngine,
|
|
@@ -61,7 +61,6 @@ export function computeNewStateRoot(
|
|
|
61
61
|
* Compute the state root after processing an execution payload envelope.
|
|
62
62
|
* Similar to `computeNewStateRoot` but for payload envelope processing.
|
|
63
63
|
*
|
|
64
|
-
* The `postBlockState` is mutated in place, callers must ensure it is not needed afterward.
|
|
65
64
|
*/
|
|
66
65
|
export function computeEnvelopeStateRoot(
|
|
67
66
|
metrics: Metrics | null,
|
|
@@ -74,13 +73,15 @@ export function computeEnvelopeStateRoot(
|
|
|
74
73
|
};
|
|
75
74
|
|
|
76
75
|
const processEnvelopeTimer = metrics?.blockPayload.executionPayloadEnvelopeProcessingTime.startTimer();
|
|
77
|
-
processExecutionPayloadEnvelope(postBlockState, signedEnvelope, false
|
|
76
|
+
const postEnvelopeState = processExecutionPayloadEnvelope(postBlockState, signedEnvelope, false, {
|
|
77
|
+
dontTransferCache: true,
|
|
78
|
+
});
|
|
78
79
|
processEnvelopeTimer?.();
|
|
79
80
|
|
|
80
81
|
const hashTreeRootTimer = metrics?.stateHashTreeRootTime.startTimer({
|
|
81
82
|
source: StateHashTreeRootSource.computeEnvelopeStateRoot,
|
|
82
83
|
});
|
|
83
|
-
const stateRoot =
|
|
84
|
+
const stateRoot = postEnvelopeState.hashTreeRoot();
|
|
84
85
|
hashTreeRootTimer?.();
|
|
85
86
|
|
|
86
87
|
return stateRoot;
|
|
@@ -17,6 +17,7 @@ import {Metrics} from "../../metrics/metrics.js";
|
|
|
17
17
|
import {MAX_LOOK_AHEAD_EPOCHS} from "../../sync/constants.js";
|
|
18
18
|
import {IClock} from "../../util/clock.js";
|
|
19
19
|
import {CustodyConfig} from "../../util/dataColumns.js";
|
|
20
|
+
import {SerializedCache} from "../../util/serializedCache.js";
|
|
20
21
|
import {
|
|
21
22
|
BlockInput,
|
|
22
23
|
BlockInputBlobs,
|
|
@@ -55,6 +56,7 @@ export type SeenBlockInputCacheModules = {
|
|
|
55
56
|
chainEvents: ChainEventEmitter;
|
|
56
57
|
signal: AbortSignal;
|
|
57
58
|
custodyConfig: CustodyConfig;
|
|
59
|
+
serializedCache: SerializedCache;
|
|
58
60
|
metrics: Metrics | null;
|
|
59
61
|
logger?: Logger;
|
|
60
62
|
};
|
|
@@ -101,6 +103,7 @@ export class SeenBlockInput {
|
|
|
101
103
|
private readonly clock: IClock;
|
|
102
104
|
private readonly chainEvents: ChainEventEmitter;
|
|
103
105
|
private readonly signal: AbortSignal;
|
|
106
|
+
private readonly serializedCache: SerializedCache;
|
|
104
107
|
private readonly metrics: Metrics | null;
|
|
105
108
|
private readonly logger?: Logger;
|
|
106
109
|
private blockInputs = new Map<RootHex, IBlockInput>();
|
|
@@ -109,19 +112,35 @@ export class SeenBlockInput {
|
|
|
109
112
|
// and the signature to ensure we only skip verification if both match
|
|
110
113
|
private verifiedProposerSignatures = new Map<Slot, Map<RootHex, BLSSignature>>();
|
|
111
114
|
|
|
112
|
-
constructor({
|
|
115
|
+
constructor({
|
|
116
|
+
config,
|
|
117
|
+
custodyConfig,
|
|
118
|
+
clock,
|
|
119
|
+
chainEvents,
|
|
120
|
+
signal,
|
|
121
|
+
serializedCache,
|
|
122
|
+
metrics,
|
|
123
|
+
logger,
|
|
124
|
+
}: SeenBlockInputCacheModules) {
|
|
113
125
|
this.config = config;
|
|
114
126
|
this.custodyConfig = custodyConfig;
|
|
115
127
|
this.clock = clock;
|
|
116
128
|
this.chainEvents = chainEvents;
|
|
117
129
|
this.signal = signal;
|
|
130
|
+
this.serializedCache = serializedCache;
|
|
118
131
|
this.metrics = metrics;
|
|
119
132
|
this.logger = logger;
|
|
120
133
|
|
|
121
134
|
if (metrics) {
|
|
122
|
-
metrics.seenCache.blockInput.blockInputCount.addCollect(() =>
|
|
123
|
-
metrics.seenCache.blockInput.blockInputCount.set(this.blockInputs.size)
|
|
124
|
-
|
|
135
|
+
metrics.seenCache.blockInput.blockInputCount.addCollect(() => {
|
|
136
|
+
metrics.seenCache.blockInput.blockInputCount.set(this.blockInputs.size);
|
|
137
|
+
metrics.seenCache.blockInput.serializedObjectCount.set(
|
|
138
|
+
Array.from(this.blockInputs.values()).reduce(
|
|
139
|
+
(count, blockInput) => count + blockInput.getSerializedCacheKeys().length,
|
|
140
|
+
0
|
|
141
|
+
)
|
|
142
|
+
);
|
|
143
|
+
});
|
|
125
144
|
}
|
|
126
145
|
|
|
127
146
|
this.chainEvents.on(ChainEvent.forkChoiceFinalized, this.onFinalized);
|
|
@@ -142,7 +161,10 @@ export class SeenBlockInput {
|
|
|
142
161
|
* Removes the single BlockInput from the cache
|
|
143
162
|
*/
|
|
144
163
|
remove(rootHex: RootHex): void {
|
|
145
|
-
this.blockInputs.
|
|
164
|
+
const blockInput = this.blockInputs.get(rootHex);
|
|
165
|
+
if (blockInput) {
|
|
166
|
+
this.evictBlockInput(blockInput);
|
|
167
|
+
}
|
|
146
168
|
}
|
|
147
169
|
|
|
148
170
|
/**
|
|
@@ -154,7 +176,7 @@ export class SeenBlockInput {
|
|
|
154
176
|
let deletedCount = 0;
|
|
155
177
|
while (blockInput) {
|
|
156
178
|
deletedCount++;
|
|
157
|
-
this.
|
|
179
|
+
this.evictBlockInput(blockInput);
|
|
158
180
|
blockInput = this.blockInputs.get(parentRootHex ?? "");
|
|
159
181
|
parentRootHex = blockInput?.parentRootHex;
|
|
160
182
|
}
|
|
@@ -165,10 +187,10 @@ export class SeenBlockInput {
|
|
|
165
187
|
onFinalized = (checkpoint: CheckpointWithHex) => {
|
|
166
188
|
let deletedCount = 0;
|
|
167
189
|
const cutoffSlot = computeStartSlotAtEpoch(checkpoint.epoch);
|
|
168
|
-
for (const [
|
|
190
|
+
for (const [, blockInput] of this.blockInputs) {
|
|
169
191
|
if (blockInput.slot < cutoffSlot) {
|
|
170
192
|
deletedCount++;
|
|
171
|
-
this.
|
|
193
|
+
this.evictBlockInput(blockInput);
|
|
172
194
|
}
|
|
173
195
|
}
|
|
174
196
|
this.logger?.debug(`BlockInputCache.onFinalized deleted ${deletedCount} cached BlockInputs`);
|
|
@@ -408,14 +430,20 @@ export class SeenBlockInput {
|
|
|
408
430
|
|
|
409
431
|
if (itemsToDelete > 0) {
|
|
410
432
|
const sorted = [...this.blockInputs.entries()].sort((a, b) => a[1].slot - b[1].slot);
|
|
411
|
-
for (const [
|
|
412
|
-
this.
|
|
433
|
+
for (const [, blockInput] of sorted) {
|
|
434
|
+
this.evictBlockInput(blockInput);
|
|
413
435
|
itemsToDelete--;
|
|
414
436
|
if (itemsToDelete <= 0) return;
|
|
415
437
|
}
|
|
416
438
|
}
|
|
417
439
|
pruneSetToMax(this.verifiedProposerSignatures, MAX_BLOCK_INPUT_CACHE_SIZE);
|
|
418
440
|
}
|
|
441
|
+
|
|
442
|
+
private evictBlockInput(blockInput: IBlockInput): void {
|
|
443
|
+
// Without forcefully clearing this cache, we would rely on WeakMap to evict memory which is not reliable
|
|
444
|
+
this.serializedCache.delete(blockInput.getSerializedCacheKeys());
|
|
445
|
+
this.blockInputs.delete(blockInput.blockRootHex);
|
|
446
|
+
}
|
|
419
447
|
}
|
|
420
448
|
|
|
421
449
|
enum SeenBlockInputCacheErrorCode {
|
|
@@ -1461,6 +1461,10 @@ export function createLodestarMetrics(
|
|
|
1461
1461
|
name: "lodestar_seen_block_input_cache_size",
|
|
1462
1462
|
help: "Number of cached BlockInputs",
|
|
1463
1463
|
}),
|
|
1464
|
+
serializedObjectCount: register.gauge({
|
|
1465
|
+
name: "lodestar_seen_block_input_cache_serialized_object_count",
|
|
1466
|
+
help: "Number of serialized objects retained by cached BlockInputs",
|
|
1467
|
+
}),
|
|
1464
1468
|
duplicateBlockCount: register.gauge<{source: BlockInputSource}>({
|
|
1465
1469
|
name: "lodestar_seen_block_input_cache_duplicate_block_count",
|
|
1466
1470
|
help: "Total number of duplicate blocks that pass validation and attempt to be cached but are known",
|
package/src/network/network.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* This is a thin wrapper around WeakMap
|
|
5
5
|
*/
|
|
6
6
|
export class SerializedCache {
|
|
7
|
-
map: WeakMap<object, Uint8Array> = new WeakMap();
|
|
7
|
+
private map: WeakMap<object, Uint8Array> = new WeakMap();
|
|
8
8
|
|
|
9
9
|
get(obj: object): Uint8Array | undefined {
|
|
10
10
|
return this.map.get(obj);
|
|
@@ -15,11 +15,13 @@ export class SerializedCache {
|
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
/**
|
|
18
|
-
*
|
|
19
|
-
* Must only be called after all DB writes that
|
|
18
|
+
* Delete cached serialized entries for the provided object references.
|
|
19
|
+
* Must only be called after all DB writes that read from this cache for these objects have completed,
|
|
20
20
|
* otherwise cached serialized bytes will be unavailable and data will be re-serialized unnecessarily.
|
|
21
21
|
*/
|
|
22
|
-
|
|
23
|
-
|
|
22
|
+
delete(objs: object[]): void {
|
|
23
|
+
for (const obj of objs) {
|
|
24
|
+
this.map.delete(obj);
|
|
25
|
+
}
|
|
24
26
|
}
|
|
25
27
|
}
|