@lodestar/beacon-node 1.22.0 → 1.23.0-dev.0d1fd9c839
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/api/impl/beacon/pool/index.js +13 -22
- package/lib/api/impl/beacon/pool/index.js.map +1 -1
- package/lib/api/impl/beacon/state/index.d.ts +1 -1
- package/lib/api/impl/beacon/state/index.js +42 -3
- package/lib/api/impl/beacon/state/index.js.map +1 -1
- package/lib/api/impl/beacon/state/utils.d.ts +2 -1
- package/lib/api/impl/beacon/state/utils.js +1 -1
- package/lib/api/impl/beacon/state/utils.js.map +1 -1
- package/lib/api/impl/config/constants.d.ts +0 -1
- package/lib/api/impl/config/constants.js +1 -2
- package/lib/api/impl/config/constants.js.map +1 -1
- package/lib/api/impl/errors.d.ts +8 -0
- package/lib/api/impl/errors.js +7 -0
- package/lib/api/impl/errors.js.map +1 -1
- package/lib/api/impl/validator/index.js +9 -4
- package/lib/api/impl/validator/index.js.map +1 -1
- package/lib/api/rest/base.d.ts +1 -0
- package/lib/api/rest/base.js +31 -4
- package/lib/api/rest/base.js.map +1 -1
- package/lib/api/rest/index.js +1 -0
- package/lib/api/rest/index.js.map +1 -1
- package/lib/chain/archiver/archiveBlocks.js +4 -5
- package/lib/chain/archiver/archiveBlocks.js.map +1 -1
- package/lib/chain/archiver/archiveStates.d.ts +3 -2
- package/lib/chain/archiver/archiveStates.js +8 -4
- package/lib/chain/archiver/archiveStates.js.map +1 -1
- package/lib/chain/archiver/index.d.ts +3 -1
- package/lib/chain/archiver/index.js +3 -2
- package/lib/chain/archiver/index.js.map +1 -1
- package/lib/chain/blocks/importBlock.js +3 -10
- package/lib/chain/blocks/importBlock.js.map +1 -1
- package/lib/chain/blocks/verifyBlocksExecutionPayloads.js +2 -1
- package/lib/chain/blocks/verifyBlocksExecutionPayloads.js.map +1 -1
- package/lib/chain/chain.d.ts +2 -41
- package/lib/chain/chain.js +26 -17
- package/lib/chain/chain.js.map +1 -1
- package/lib/chain/forkChoice/index.js +1 -2
- package/lib/chain/forkChoice/index.js.map +1 -1
- package/lib/chain/historicalState/getHistoricalState.d.ts +2 -1
- package/lib/chain/historicalState/getHistoricalState.js.map +1 -1
- package/lib/chain/historicalState/worker.js +1 -1
- package/lib/chain/historicalState/worker.js.map +1 -1
- package/lib/chain/initState.d.ts +1 -5
- package/lib/chain/initState.js +2 -30
- package/lib/chain/initState.js.map +1 -1
- package/lib/chain/interface.d.ts +2 -1
- package/lib/chain/lightClient/proofs.d.ts +2 -2
- package/lib/chain/lightClient/proofs.js +3 -2
- package/lib/chain/lightClient/proofs.js.map +1 -1
- package/lib/chain/opPools/opPool.js +3 -4
- package/lib/chain/opPools/opPool.js.map +1 -1
- package/lib/chain/options.js +1 -1
- package/lib/chain/options.js.map +1 -1
- package/lib/chain/produceBlock/produceBlockBody.js +7 -1
- package/lib/chain/produceBlock/produceBlockBody.js.map +1 -1
- package/lib/chain/regen/regen.js +3 -4
- package/lib/chain/regen/regen.js.map +1 -1
- package/lib/chain/rewards/attestationsRewards.js +2 -1
- package/lib/chain/rewards/attestationsRewards.js.map +1 -1
- package/lib/chain/serializeState.js +63 -10
- package/lib/chain/serializeState.js.map +1 -1
- package/lib/chain/shufflingCache.d.ts +37 -13
- package/lib/chain/shufflingCache.js +85 -76
- package/lib/chain/shufflingCache.js.map +1 -1
- package/lib/chain/stateCache/datastore/file.js +5 -5
- package/lib/chain/stateCache/datastore/file.js.map +1 -1
- package/lib/chain/stateCache/fifoBlockStateCache.d.ts +7 -2
- package/lib/chain/stateCache/fifoBlockStateCache.js +7 -2
- package/lib/chain/stateCache/fifoBlockStateCache.js.map +1 -1
- package/lib/chain/stateCache/persistentCheckpointsCache.d.ts +4 -3
- package/lib/chain/stateCache/persistentCheckpointsCache.js +33 -37
- package/lib/chain/stateCache/persistentCheckpointsCache.js.map +1 -1
- package/lib/db/repositories/lightclientCheckpointHeader.d.ts +1 -0
- package/lib/db/repositories/lightclientCheckpointHeader.js +3 -0
- package/lib/db/repositories/lightclientCheckpointHeader.js.map +1 -1
- package/lib/eth1/index.js +2 -2
- package/lib/eth1/index.js.map +1 -1
- package/lib/eth1/provider/eth1Provider.js +2 -3
- package/lib/eth1/provider/eth1Provider.js.map +1 -1
- package/lib/eth1/provider/utils.js +4 -5
- package/lib/eth1/provider/utils.js.map +1 -1
- package/lib/eth1/utils/depositContract.js +5 -5
- package/lib/eth1/utils/depositContract.js.map +1 -1
- package/lib/eth1/utils/eth1Vote.js +0 -1
- package/lib/eth1/utils/eth1Vote.js.map +1 -1
- package/lib/execution/engine/http.d.ts +3 -2
- package/lib/execution/engine/http.js +27 -10
- package/lib/execution/engine/http.js.map +1 -1
- package/lib/execution/engine/interface.d.ts +5 -4
- package/lib/execution/engine/interface.js.map +1 -1
- package/lib/execution/engine/mock.js +0 -2
- package/lib/execution/engine/mock.js.map +1 -1
- package/lib/execution/engine/payloadIdCache.d.ts +0 -17
- package/lib/execution/engine/payloadIdCache.js.map +1 -1
- package/lib/execution/engine/types.d.ts +29 -25
- package/lib/execution/engine/types.js +29 -43
- package/lib/execution/engine/types.js.map +1 -1
- package/lib/metrics/metrics/beacon.js +1 -1
- package/lib/metrics/metrics/beacon.js.map +1 -1
- package/lib/metrics/metrics/lodestar.d.ts +13 -4
- package/lib/metrics/metrics/lodestar.js +43 -19
- package/lib/metrics/metrics/lodestar.js.map +1 -1
- package/lib/network/core/networkCore.js +2 -3
- package/lib/network/core/networkCore.js.map +1 -1
- package/lib/network/gossip/topic.d.ts +0 -27
- package/lib/network/peers/utils/assertPeerRelevance.js +2 -3
- package/lib/network/peers/utils/assertPeerRelevance.js.map +1 -1
- package/lib/network/processor/gossipHandlers.js +11 -11
- package/lib/network/processor/gossipHandlers.js.map +1 -1
- package/lib/network/processor/gossipQueues/index.js +1 -5
- package/lib/network/processor/gossipQueues/index.js.map +1 -1
- package/lib/network/processor/gossipQueues/types.d.ts +1 -2
- package/lib/network/processor/gossipQueues/types.js +0 -6
- package/lib/network/processor/gossipQueues/types.js.map +1 -1
- package/lib/network/reqresp/beaconBlocksMaybeBlobsByRoot.js +2 -2
- package/lib/network/reqresp/beaconBlocksMaybeBlobsByRoot.js.map +1 -1
- package/lib/network/reqresp/types.d.ts +5 -5
- package/lib/network/reqresp/types.js +1 -1
- package/lib/network/reqresp/types.js.map +1 -1
- package/lib/node/utils/interop/state.js +1 -0
- package/lib/node/utils/interop/state.js.map +1 -1
- package/lib/node/utils/state.d.ts +3 -0
- package/lib/node/utils/state.js +3 -0
- package/lib/node/utils/state.js.map +1 -1
- package/lib/sync/backfill/backfill.js +1 -2
- package/lib/sync/backfill/backfill.js.map +1 -1
- package/lib/sync/unknownBlock.js +3 -4
- package/lib/sync/unknownBlock.js.map +1 -1
- package/package.json +20 -19
- package/lib/network/processor/gossipQueues/indexedAvgTime.d.ts +0 -31
- package/lib/network/processor/gossipQueues/indexedAvgTime.js +0 -115
- package/lib/network/processor/gossipQueues/indexedAvgTime.js.map +0 -1
|
@@ -1,8 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { GENESIS_SLOT } from "@lodestar/params";
|
|
5
|
-
import { computeAnchorCheckpoint } from "./initState.js";
|
|
1
|
+
import { computeEpochShuffling, } from "@lodestar/state-transition";
|
|
2
|
+
import { LodestarError, MapDef, pruneSetToMax } from "@lodestar/utils";
|
|
3
|
+
import { callInNextEventLoop } from "../util/eventLoop.js";
|
|
6
4
|
/**
|
|
7
5
|
* Same value to CheckpointBalancesCache, with the assumption that we don't have to use it for old epochs. In the worse case:
|
|
8
6
|
* - when loading state bytes from disk, we need to compute shuffling for all epochs (~1s as of Sep 2023)
|
|
@@ -27,71 +25,31 @@ var CacheItemType;
|
|
|
27
25
|
* - skip computing shuffling when loading state bytes from disk
|
|
28
26
|
*/
|
|
29
27
|
export class ShufflingCache {
|
|
30
|
-
constructor(metrics = null, opts = {}) {
|
|
28
|
+
constructor(metrics = null, logger = null, opts = {}, precalculatedShufflings) {
|
|
31
29
|
this.metrics = metrics;
|
|
30
|
+
this.logger = logger;
|
|
32
31
|
/** LRU cache implemented as a map, pruned every time we add an item */
|
|
33
32
|
this.itemsByDecisionRootByEpoch = new MapDef(() => new Map());
|
|
34
33
|
if (metrics) {
|
|
35
34
|
metrics.shufflingCache.size.addCollect(() => metrics.shufflingCache.size.set(Array.from(this.itemsByDecisionRootByEpoch.values()).reduce((total, innerMap) => total + innerMap.size, 0)));
|
|
36
35
|
}
|
|
37
36
|
this.maxEpochs = opts.maxShufflingCacheEpochs ?? MAX_EPOCHS;
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
*/
|
|
42
|
-
processState(state, shufflingEpoch) {
|
|
43
|
-
const decisionBlockHex = getDecisionBlock(state, shufflingEpoch);
|
|
44
|
-
let shuffling;
|
|
45
|
-
switch (shufflingEpoch) {
|
|
46
|
-
case state.epochCtx.nextShuffling.epoch:
|
|
47
|
-
shuffling = state.epochCtx.nextShuffling;
|
|
48
|
-
break;
|
|
49
|
-
case state.epochCtx.currentShuffling.epoch:
|
|
50
|
-
shuffling = state.epochCtx.currentShuffling;
|
|
51
|
-
break;
|
|
52
|
-
case state.epochCtx.previousShuffling.epoch:
|
|
53
|
-
shuffling = state.epochCtx.previousShuffling;
|
|
54
|
-
break;
|
|
55
|
-
default:
|
|
56
|
-
throw new Error(`Shuffling not found from state ${state.slot} for epoch ${shufflingEpoch}`);
|
|
57
|
-
}
|
|
58
|
-
let cacheItem = this.itemsByDecisionRootByEpoch.getOrDefault(shufflingEpoch).get(decisionBlockHex);
|
|
59
|
-
if (cacheItem !== undefined) {
|
|
60
|
-
// update existing promise
|
|
61
|
-
if (isPromiseCacheItem(cacheItem)) {
|
|
62
|
-
// unblock consumers of this promise
|
|
63
|
-
cacheItem.resolveFn(shuffling);
|
|
64
|
-
// then update item type to shuffling
|
|
65
|
-
cacheItem = {
|
|
66
|
-
type: CacheItemType.shuffling,
|
|
67
|
-
shuffling,
|
|
68
|
-
};
|
|
69
|
-
this.add(shufflingEpoch, decisionBlockHex, cacheItem);
|
|
70
|
-
// we updated type to CacheItemType.shuffling so the above fields are not used anyway
|
|
71
|
-
this.metrics?.shufflingCache.processStateUpdatePromise.inc();
|
|
72
|
-
}
|
|
73
|
-
else {
|
|
74
|
-
// ShufflingCacheItem, do nothing
|
|
75
|
-
this.metrics?.shufflingCache.processStateNoOp.inc();
|
|
37
|
+
precalculatedShufflings?.map(({ shuffling, decisionRoot }) => {
|
|
38
|
+
if (shuffling !== null) {
|
|
39
|
+
this.set(shuffling, decisionRoot);
|
|
76
40
|
}
|
|
77
|
-
}
|
|
78
|
-
else {
|
|
79
|
-
// not found, new shuffling
|
|
80
|
-
this.add(shufflingEpoch, decisionBlockHex, { type: CacheItemType.shuffling, shuffling });
|
|
81
|
-
this.metrics?.shufflingCache.processStateInsertNew.inc();
|
|
82
|
-
}
|
|
83
|
-
return shuffling;
|
|
41
|
+
});
|
|
84
42
|
}
|
|
85
43
|
/**
|
|
86
44
|
* Insert a promise to make sure we don't regen state for the same shuffling.
|
|
87
45
|
* Bound by MAX_SHUFFLING_PROMISE to make sure our node does not blow up.
|
|
88
46
|
*/
|
|
89
|
-
insertPromise(
|
|
47
|
+
insertPromise(epoch, decisionRoot) {
|
|
90
48
|
const promiseCount = Array.from(this.itemsByDecisionRootByEpoch.values())
|
|
91
49
|
.flatMap((innerMap) => Array.from(innerMap.values()))
|
|
92
50
|
.filter((item) => isPromiseCacheItem(item)).length;
|
|
93
51
|
if (promiseCount >= MAX_PROMISES) {
|
|
94
|
-
throw new Error(`Too many shuffling promises: ${promiseCount}, shufflingEpoch: ${
|
|
52
|
+
throw new Error(`Too many shuffling promises: ${promiseCount}, shufflingEpoch: ${epoch}, decisionRootHex: ${decisionRoot}`);
|
|
95
53
|
}
|
|
96
54
|
let resolveFn = null;
|
|
97
55
|
const promise = new Promise((resolve) => {
|
|
@@ -102,10 +60,11 @@ export class ShufflingCache {
|
|
|
102
60
|
}
|
|
103
61
|
const cacheItem = {
|
|
104
62
|
type: CacheItemType.promise,
|
|
63
|
+
timeInsertedMs: Date.now(),
|
|
105
64
|
promise,
|
|
106
65
|
resolveFn,
|
|
107
66
|
};
|
|
108
|
-
this.
|
|
67
|
+
this.itemsByDecisionRootByEpoch.getOrDefault(epoch).set(decisionRoot, cacheItem);
|
|
109
68
|
this.metrics?.shufflingCache.insertPromiseCount.inc();
|
|
110
69
|
}
|
|
111
70
|
/**
|
|
@@ -113,35 +72,89 @@ export class ShufflingCache {
|
|
|
113
72
|
* If there's a promise, it means we are computing the same shuffling, so we wait for the promise to resolve.
|
|
114
73
|
* Return null if we don't have a shuffling for this epoch and dependentRootHex.
|
|
115
74
|
*/
|
|
116
|
-
async get(
|
|
117
|
-
const cacheItem = this.itemsByDecisionRootByEpoch.getOrDefault(
|
|
75
|
+
async get(epoch, decisionRoot) {
|
|
76
|
+
const cacheItem = this.itemsByDecisionRootByEpoch.getOrDefault(epoch).get(decisionRoot);
|
|
118
77
|
if (cacheItem === undefined) {
|
|
78
|
+
this.metrics?.shufflingCache.miss.inc();
|
|
119
79
|
return null;
|
|
120
80
|
}
|
|
121
81
|
if (isShufflingCacheItem(cacheItem)) {
|
|
82
|
+
this.metrics?.shufflingCache.hit.inc();
|
|
122
83
|
return cacheItem.shuffling;
|
|
123
84
|
}
|
|
124
85
|
else {
|
|
125
|
-
|
|
86
|
+
this.metrics?.shufflingCache.shufflingPromiseNotResolved.inc();
|
|
126
87
|
return cacheItem.promise;
|
|
127
88
|
}
|
|
128
89
|
}
|
|
129
90
|
/**
|
|
130
|
-
*
|
|
91
|
+
* Gets a cached shuffling via the epoch and decision root. If the shuffling is not
|
|
92
|
+
* available it will build it synchronously and return the shuffling.
|
|
93
|
+
*
|
|
94
|
+
* NOTE: If a shuffling is already queued and not calculated it will build and resolve
|
|
95
|
+
* the promise but the already queued build will happen at some later time
|
|
131
96
|
*/
|
|
132
|
-
getSync(
|
|
133
|
-
const cacheItem = this.itemsByDecisionRootByEpoch.getOrDefault(
|
|
134
|
-
if (cacheItem
|
|
135
|
-
|
|
97
|
+
getSync(epoch, decisionRoot, buildProps) {
|
|
98
|
+
const cacheItem = this.itemsByDecisionRootByEpoch.getOrDefault(epoch).get(decisionRoot);
|
|
99
|
+
if (!cacheItem) {
|
|
100
|
+
this.metrics?.shufflingCache.miss.inc();
|
|
136
101
|
}
|
|
137
|
-
if (isShufflingCacheItem(cacheItem)) {
|
|
102
|
+
else if (isShufflingCacheItem(cacheItem)) {
|
|
103
|
+
this.metrics?.shufflingCache.hit.inc();
|
|
138
104
|
return cacheItem.shuffling;
|
|
139
105
|
}
|
|
140
|
-
|
|
141
|
-
|
|
106
|
+
else if (buildProps) {
|
|
107
|
+
// TODO: (@matthewkeil) This should possible log a warning??
|
|
108
|
+
this.metrics?.shufflingCache.shufflingPromiseNotResolvedAndThrownAway.inc();
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
this.metrics?.shufflingCache.shufflingPromiseNotResolved.inc();
|
|
112
|
+
}
|
|
113
|
+
let shuffling = null;
|
|
114
|
+
if (buildProps) {
|
|
115
|
+
const timer = this.metrics?.shufflingCache.shufflingCalculationTime.startTimer({ source: "getSync" });
|
|
116
|
+
shuffling = computeEpochShuffling(buildProps.state, buildProps.activeIndices, epoch);
|
|
117
|
+
timer?.();
|
|
118
|
+
this.set(shuffling, decisionRoot);
|
|
119
|
+
}
|
|
120
|
+
return shuffling;
|
|
142
121
|
}
|
|
143
|
-
|
|
144
|
-
|
|
122
|
+
/**
|
|
123
|
+
* Queue asynchronous build for an EpochShuffling, triggered from state-transition
|
|
124
|
+
*/
|
|
125
|
+
build(epoch, decisionRoot, state, activeIndices) {
|
|
126
|
+
this.insertPromise(epoch, decisionRoot);
|
|
127
|
+
/**
|
|
128
|
+
* TODO: (@matthewkeil) This will get replaced by a proper build queue and a worker to do calculations
|
|
129
|
+
* on a NICE thread with a rust implementation
|
|
130
|
+
*/
|
|
131
|
+
callInNextEventLoop(() => {
|
|
132
|
+
const timer = this.metrics?.shufflingCache.shufflingCalculationTime.startTimer({ source: "build" });
|
|
133
|
+
const shuffling = computeEpochShuffling(state, activeIndices, epoch);
|
|
134
|
+
timer?.();
|
|
135
|
+
this.set(shuffling, decisionRoot);
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Add an EpochShuffling to the ShufflingCache. If a promise for the shuffling is present it will
|
|
140
|
+
* resolve the promise with the built shuffling
|
|
141
|
+
*/
|
|
142
|
+
set(shuffling, decisionRoot) {
|
|
143
|
+
const shufflingAtEpoch = this.itemsByDecisionRootByEpoch.getOrDefault(shuffling.epoch);
|
|
144
|
+
// if a pending shuffling promise exists, resolve it
|
|
145
|
+
const cacheItem = shufflingAtEpoch.get(decisionRoot);
|
|
146
|
+
if (cacheItem) {
|
|
147
|
+
if (isPromiseCacheItem(cacheItem)) {
|
|
148
|
+
cacheItem.resolveFn(shuffling);
|
|
149
|
+
this.metrics?.shufflingCache.shufflingPromiseResolutionTime.observe((Date.now() - cacheItem.timeInsertedMs) / 1000);
|
|
150
|
+
}
|
|
151
|
+
else {
|
|
152
|
+
this.metrics?.shufflingCache.shufflingBuiltMultipleTimes.inc();
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
// set the shuffling
|
|
156
|
+
shufflingAtEpoch.set(decisionRoot, { type: CacheItemType.shuffling, shuffling });
|
|
157
|
+
// prune the cache
|
|
145
158
|
pruneSetToMax(this.itemsByDecisionRootByEpoch, this.maxEpochs);
|
|
146
159
|
}
|
|
147
160
|
}
|
|
@@ -151,14 +164,10 @@ function isShufflingCacheItem(item) {
|
|
|
151
164
|
function isPromiseCacheItem(item) {
|
|
152
165
|
return item.type === CacheItemType.promise;
|
|
153
166
|
}
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
function getDecisionBlock(state, epoch) {
|
|
160
|
-
return state.slot > GENESIS_SLOT
|
|
161
|
-
? getShufflingDecisionBlock(state, epoch)
|
|
162
|
-
: toRootHex(ssz.phase0.BeaconBlockHeader.hashTreeRoot(computeAnchorCheckpoint(state.config, state).blockHeader));
|
|
167
|
+
export var ShufflingCacheErrorCode;
|
|
168
|
+
(function (ShufflingCacheErrorCode) {
|
|
169
|
+
ShufflingCacheErrorCode["NO_SHUFFLING_FOUND"] = "SHUFFLING_CACHE_ERROR_NO_SHUFFLING_FOUND";
|
|
170
|
+
})(ShufflingCacheErrorCode || (ShufflingCacheErrorCode = {}));
|
|
171
|
+
export class ShufflingCacheError extends LodestarError {
|
|
163
172
|
}
|
|
164
173
|
//# sourceMappingURL=shufflingCache.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shufflingCache.js","sourceRoot":"","sources":["../../src/chain/shufflingCache.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"shufflingCache.js","sourceRoot":"","sources":["../../src/chain/shufflingCache.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
|
-
import { toHexString, fromHexString } from "@chainsafe/ssz";
|
|
3
2
|
import { ssz } from "@lodestar/types";
|
|
3
|
+
import { fromHex, toHex } from "@lodestar/utils";
|
|
4
4
|
import { ensureDir, readFile, readFileNames, removeFile, writeIfNotExist } from "../../../util/file.js";
|
|
5
5
|
const CHECKPOINT_STATES_FOLDER = "checkpoint_states";
|
|
6
6
|
const CHECKPOINT_FILE_NAME_LENGTH = 82;
|
|
@@ -22,23 +22,23 @@ export class FileCPStateDatastore {
|
|
|
22
22
|
}
|
|
23
23
|
async write(cpKey, stateBytes) {
|
|
24
24
|
const serializedCheckpoint = ssz.phase0.Checkpoint.serialize(cpKey);
|
|
25
|
-
const filePath = path.join(this.folderPath,
|
|
25
|
+
const filePath = path.join(this.folderPath, toHex(serializedCheckpoint));
|
|
26
26
|
await writeIfNotExist(filePath, stateBytes);
|
|
27
27
|
return serializedCheckpoint;
|
|
28
28
|
}
|
|
29
29
|
async remove(serializedCheckpoint) {
|
|
30
|
-
const filePath = path.join(this.folderPath,
|
|
30
|
+
const filePath = path.join(this.folderPath, toHex(serializedCheckpoint));
|
|
31
31
|
await removeFile(filePath);
|
|
32
32
|
}
|
|
33
33
|
async read(serializedCheckpoint) {
|
|
34
|
-
const filePath = path.join(this.folderPath,
|
|
34
|
+
const filePath = path.join(this.folderPath, toHex(serializedCheckpoint));
|
|
35
35
|
return readFile(filePath);
|
|
36
36
|
}
|
|
37
37
|
async readKeys() {
|
|
38
38
|
const fileNames = await readFileNames(this.folderPath);
|
|
39
39
|
return fileNames
|
|
40
40
|
.filter((fileName) => fileName.startsWith("0x") && fileName.length === CHECKPOINT_FILE_NAME_LENGTH)
|
|
41
|
-
.map((fileName) =>
|
|
41
|
+
.map((fileName) => fromHex(fileName));
|
|
42
42
|
}
|
|
43
43
|
}
|
|
44
44
|
//# sourceMappingURL=file.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"file.js","sourceRoot":"","sources":["../../../../src/chain/stateCache/datastore/file.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,
|
|
1
|
+
{"version":3,"file":"file.js","sourceRoot":"","sources":["../../../../src/chain/stateCache/datastore/file.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAS,GAAG,EAAC,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAC,OAAO,EAAE,KAAK,EAAC,MAAM,iBAAiB,CAAC;AAC/C,OAAO,EAAC,SAAS,EAAE,QAAQ,EAAE,aAAa,EAAE,UAAU,EAAE,eAAe,EAAC,MAAM,uBAAuB,CAAC;AAGtG,MAAM,wBAAwB,GAAG,mBAAmB,CAAC;AACrD,MAAM,2BAA2B,GAAG,EAAE,CAAC;AAEvC;;GAEG;AACH,MAAM,OAAO,oBAAoB;IAG/B,YAAY,YAAoB,GAAG;QACjC,+DAA+D;QAC/D,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,wBAAwB,CAAC,CAAC;IACnE,CAAC;IAED,KAAK,CAAC,IAAI;QACR,IAAI,CAAC;YACH,MAAM,SAAS,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QACnC,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,aAAa;QACf,CAAC;IACH,CAAC;IAED,KAAK,CAAC,KAAK,CAAC,KAAwB,EAAE,UAAsB;QAC1D,MAAM,oBAAoB,GAAG,GAAG,CAAC,MAAM,CAAC,UAAU,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;QACpE,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,KAAK,CAAC,oBAAoB,CAAC,CAAC,CAAC;QACzE,MAAM,eAAe,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;QAC5C,OAAO,oBAAoB,CAAC;IAC9B,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,oBAAkC;QAC7C,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,KAAK,CAAC,oBAAoB,CAAC,CAAC,CAAC;QACzE,MAAM,UAAU,CAAC,QAAQ,CAAC,CAAC;IAC7B,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,oBAAkC;QAC3C,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,KAAK,CAAC,oBAAoB,CAAC,CAAC,CAAC;QACzE,OAAO,QAAQ,CAAC,QAAQ,CAAC,CAAC;IAC5B,CAAC;IAED,KAAK,CAAC,QAAQ;QACZ,MAAM,SAAS,GAAG,MAAM,aAAa,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QACvD,OAAO,SAAS;aACb,MAAM,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,QAAQ,CAAC,MAAM,KAAK,2BAA2B,CAAC;aAClG,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC;IAC1C,CAAC;CACF"}
|
|
@@ -8,9 +8,14 @@ export type FIFOBlockStateCacheOpts = {
|
|
|
8
8
|
maxBlockStates?: number;
|
|
9
9
|
};
|
|
10
10
|
/**
|
|
11
|
-
*
|
|
11
|
+
* Given `maxSkipSlots` = 32 and `DEFAULT_EARLIEST_PERMISSIBLE_SLOT_DISTANCE` = 32, lodestar doesn't need to
|
|
12
|
+
* reload states in order to process a gossip block.
|
|
13
|
+
*
|
|
14
|
+
* |-----------------------------------------------|-----------------------------------------------|
|
|
15
|
+
* maxSkipSlots DEFAULT_EARLIEST_PERMISSIBLE_SLOT_DISTANCE ^
|
|
16
|
+
* clock slot
|
|
12
17
|
*/
|
|
13
|
-
export declare const DEFAULT_MAX_BLOCK_STATES =
|
|
18
|
+
export declare const DEFAULT_MAX_BLOCK_STATES = 64;
|
|
14
19
|
/**
|
|
15
20
|
* New implementation of BlockStateCache that keeps the most recent n states consistently
|
|
16
21
|
* - Maintain a linked list (FIFO) with special handling for head state, which is always the first item in the list
|
|
@@ -2,9 +2,14 @@ import { toRootHex } from "@lodestar/utils";
|
|
|
2
2
|
import { LinkedList } from "../../util/array.js";
|
|
3
3
|
import { MapTracker } from "./mapMetrics.js";
|
|
4
4
|
/**
|
|
5
|
-
*
|
|
5
|
+
* Given `maxSkipSlots` = 32 and `DEFAULT_EARLIEST_PERMISSIBLE_SLOT_DISTANCE` = 32, lodestar doesn't need to
|
|
6
|
+
* reload states in order to process a gossip block.
|
|
7
|
+
*
|
|
8
|
+
* |-----------------------------------------------|-----------------------------------------------|
|
|
9
|
+
* maxSkipSlots DEFAULT_EARLIEST_PERMISSIBLE_SLOT_DISTANCE ^
|
|
10
|
+
* clock slot
|
|
6
11
|
*/
|
|
7
|
-
export const DEFAULT_MAX_BLOCK_STATES =
|
|
12
|
+
export const DEFAULT_MAX_BLOCK_STATES = 64;
|
|
8
13
|
/**
|
|
9
14
|
* New implementation of BlockStateCache that keeps the most recent n states consistently
|
|
10
15
|
* - Maintain a linked list (FIFO) with special handling for head state, which is always the first item in the list
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fifoBlockStateCache.js","sourceRoot":"","sources":["../../../src/chain/stateCache/fifoBlockStateCache.ts"],"names":[],"mappings":"AAGA,OAAO,EAAC,SAAS,EAAC,MAAM,iBAAiB,CAAC;AAE1C,OAAO,EAAC,UAAU,EAAC,MAAM,qBAAqB,CAAC;AAE/C,OAAO,EAAC,UAAU,EAAC,MAAM,iBAAiB,CAAC;AAO3C
|
|
1
|
+
{"version":3,"file":"fifoBlockStateCache.js","sourceRoot":"","sources":["../../../src/chain/stateCache/fifoBlockStateCache.ts"],"names":[],"mappings":"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"}
|
|
@@ -30,10 +30,11 @@ type LoadedStateBytesData = {
|
|
|
30
30
|
stateBytes: Uint8Array;
|
|
31
31
|
};
|
|
32
32
|
/**
|
|
33
|
-
* Before n-historical states, lodestar keeps
|
|
34
|
-
* Since
|
|
33
|
+
* Before n-historical states, lodestar keeps all checkpoint states since finalized
|
|
34
|
+
* Since Sep 2024, lodestar stores 3 most recent checkpoint states in memory and the rest on disk. The finalized state
|
|
35
|
+
* may not be available in memory, and stay on disk instead.
|
|
35
36
|
*/
|
|
36
|
-
export declare const DEFAULT_MAX_CP_STATE_EPOCHS_IN_MEMORY =
|
|
37
|
+
export declare const DEFAULT_MAX_CP_STATE_EPOCHS_IN_MEMORY = 3;
|
|
37
38
|
/**
|
|
38
39
|
* An implementation of CheckpointStateCache that keep up to n epoch checkpoint states in memory and persist the rest to disk
|
|
39
40
|
* - If it's more than `maxEpochsInMemory` epochs old, it will persist n last epochs to disk based on the view of the block
|
|
@@ -43,9 +43,8 @@ var __disposeResources = (this && this.__disposeResources) || (function (Suppres
|
|
|
43
43
|
var e = new Error(message);
|
|
44
44
|
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
45
45
|
});
|
|
46
|
-
import { fromHexString, toHexString } from "@chainsafe/ssz";
|
|
47
46
|
import { computeStartSlotAtEpoch, getBlockRootAtSlot } from "@lodestar/state-transition";
|
|
48
|
-
import { MapDef, sleep, toRootHex } from "@lodestar/utils";
|
|
47
|
+
import { MapDef, fromHex, sleep, toHex, toRootHex } from "@lodestar/utils";
|
|
49
48
|
import { loadCachedBeaconState } from "@lodestar/state-transition";
|
|
50
49
|
import { INTERVALS_PER_SLOT } from "@lodestar/params";
|
|
51
50
|
import { AllocSource } from "../../util/bufferPool.js";
|
|
@@ -54,10 +53,11 @@ import { MapTracker } from "./mapMetrics.js";
|
|
|
54
53
|
import { datastoreKeyToCheckpoint } from "./datastore/index.js";
|
|
55
54
|
import { CacheItemType } from "./types.js";
|
|
56
55
|
/**
|
|
57
|
-
* Before n-historical states, lodestar keeps
|
|
58
|
-
* Since
|
|
56
|
+
* Before n-historical states, lodestar keeps all checkpoint states since finalized
|
|
57
|
+
* Since Sep 2024, lodestar stores 3 most recent checkpoint states in memory and the rest on disk. The finalized state
|
|
58
|
+
* may not be available in memory, and stay on disk instead.
|
|
59
59
|
*/
|
|
60
|
-
export const DEFAULT_MAX_CP_STATE_EPOCHS_IN_MEMORY =
|
|
60
|
+
export const DEFAULT_MAX_CP_STATE_EPOCHS_IN_MEMORY = 3;
|
|
61
61
|
/**
|
|
62
62
|
* An implementation of CheckpointStateCache that keep up to n epoch checkpoint states in memory and persist the rest to disk
|
|
63
63
|
* - If it's more than `maxEpochsInMemory` epochs old, it will persist n last epochs to disk based on the view of the block
|
|
@@ -98,7 +98,7 @@ export class PersistentCheckpointStateCache {
|
|
|
98
98
|
this.preComputedCheckpointHits = null;
|
|
99
99
|
this.cache = new MapTracker(metrics?.cpStateCache);
|
|
100
100
|
if (metrics) {
|
|
101
|
-
this.metrics = metrics
|
|
101
|
+
this.metrics = metrics;
|
|
102
102
|
metrics.cpStateCache.size.addCollect(() => {
|
|
103
103
|
let persistCount = 0;
|
|
104
104
|
let inMemoryCount = 0;
|
|
@@ -166,36 +166,28 @@ export class PersistentCheckpointStateCache {
|
|
|
166
166
|
return stateOrStateBytesData?.clone(opts?.dontTransferCache) ?? null;
|
|
167
167
|
}
|
|
168
168
|
const { persistedKey, stateBytes } = stateOrStateBytesData;
|
|
169
|
-
const logMeta = { persistedKey:
|
|
169
|
+
const logMeta = { persistedKey: toHex(persistedKey) };
|
|
170
170
|
this.logger.debug("Reload: read state successful", logMeta);
|
|
171
|
-
this.metrics?.stateReloadSecFromSlot.observe(this.clock?.secFromSlot(this.clock?.currentSlot ?? 0) ?? 0);
|
|
171
|
+
this.metrics?.cpStateCache.stateReloadSecFromSlot.observe(this.clock?.secFromSlot(this.clock?.currentSlot ?? 0) ?? 0);
|
|
172
172
|
const seedState = this.findSeedStateToReload(cp);
|
|
173
|
-
this.metrics?.stateReloadEpochDiff.observe(Math.abs(seedState.epochCtx.epoch - cp.epoch));
|
|
173
|
+
this.metrics?.cpStateCache.stateReloadEpochDiff.observe(Math.abs(seedState.epochCtx.epoch - cp.epoch));
|
|
174
174
|
this.logger.debug("Reload: found seed state", { ...logMeta, seedSlot: seedState.slot });
|
|
175
175
|
try {
|
|
176
176
|
const env_1 = { stack: [], error: void 0, hasError: false };
|
|
177
177
|
try {
|
|
178
178
|
// 80% of validators serialization time comes from memory allocation, this is to avoid it
|
|
179
|
-
const sszTimer = this.metrics?.stateReloadValidatorsSerializeDuration.startTimer();
|
|
179
|
+
const sszTimer = this.metrics?.cpStateCache.stateReloadValidatorsSerializeDuration.startTimer();
|
|
180
180
|
// automatically free the buffer pool after this scope
|
|
181
181
|
const validatorsBytesWithKey = __addDisposableResource(env_1, this.serializeStateValidators(seedState), false);
|
|
182
182
|
let validatorsBytes = validatorsBytesWithKey?.buffer;
|
|
183
183
|
if (validatorsBytes == null) {
|
|
184
184
|
// fallback logic in case we can't use the buffer pool
|
|
185
|
-
this.metrics?.stateReloadValidatorsSerializeAllocCount.inc();
|
|
185
|
+
this.metrics?.cpStateCache.stateReloadValidatorsSerializeAllocCount.inc();
|
|
186
186
|
validatorsBytes = seedState.validators.serialize();
|
|
187
187
|
}
|
|
188
188
|
sszTimer?.();
|
|
189
|
-
const timer = this.metrics?.stateReloadDuration.startTimer();
|
|
190
|
-
const newCachedState = loadCachedBeaconState(seedState, stateBytes, {
|
|
191
|
-
shufflingGetter: (shufflingEpoch, decisionRootHex) => {
|
|
192
|
-
const shuffling = this.shufflingCache.getSync(shufflingEpoch, decisionRootHex);
|
|
193
|
-
if (shuffling == null) {
|
|
194
|
-
this.metrics?.stateReloadShufflingCacheMiss.inc();
|
|
195
|
-
}
|
|
196
|
-
return shuffling;
|
|
197
|
-
},
|
|
198
|
-
}, validatorsBytes);
|
|
189
|
+
const timer = this.metrics?.cpStateCache.stateReloadDuration.startTimer();
|
|
190
|
+
const newCachedState = loadCachedBeaconState(seedState, stateBytes, {}, validatorsBytes);
|
|
199
191
|
newCachedState.commit();
|
|
200
192
|
const stateRoot = toRootHex(newCachedState.hashTreeRoot());
|
|
201
193
|
timer?.();
|
|
@@ -257,7 +249,7 @@ export class PersistentCheckpointStateCache {
|
|
|
257
249
|
throw new Error("Expected persistent key");
|
|
258
250
|
}
|
|
259
251
|
const persistedKey = cacheItem.value;
|
|
260
|
-
const dbReadTimer = this.metrics?.stateReloadDbReadTime.startTimer();
|
|
252
|
+
const dbReadTimer = this.metrics?.cpStateCache.stateReloadDbReadTime.startTimer();
|
|
261
253
|
const stateBytes = await this.datastore.read(persistedKey);
|
|
262
254
|
dbReadTimer?.();
|
|
263
255
|
if (stateBytes === null) {
|
|
@@ -269,19 +261,19 @@ export class PersistentCheckpointStateCache {
|
|
|
269
261
|
* Similar to get() api without reloading from disk
|
|
270
262
|
*/
|
|
271
263
|
get(cpOrKey, opts) {
|
|
272
|
-
this.metrics?.lookups.inc();
|
|
264
|
+
this.metrics?.cpStateCache.lookups.inc();
|
|
273
265
|
const cpKey = typeof cpOrKey === "string" ? cpOrKey : toCacheKey(cpOrKey);
|
|
274
266
|
const cacheItem = this.cache.get(cpKey);
|
|
275
267
|
if (cacheItem === undefined) {
|
|
276
268
|
return null;
|
|
277
269
|
}
|
|
278
|
-
this.metrics?.hits.inc();
|
|
270
|
+
this.metrics?.cpStateCache.hits.inc();
|
|
279
271
|
if (cpKey === this.preComputedCheckpoint) {
|
|
280
272
|
this.preComputedCheckpointHits = (this.preComputedCheckpointHits ?? 0) + 1;
|
|
281
273
|
}
|
|
282
274
|
if (isInMemoryCacheItem(cacheItem)) {
|
|
283
275
|
const { state } = cacheItem;
|
|
284
|
-
this.metrics?.stateClonedCount.observe(state.clonedCount);
|
|
276
|
+
this.metrics?.cpStateCache.stateClonedCount.observe(state.clonedCount);
|
|
285
277
|
return state.clone(opts?.dontTransferCache);
|
|
286
278
|
}
|
|
287
279
|
return null;
|
|
@@ -293,7 +285,7 @@ export class PersistentCheckpointStateCache {
|
|
|
293
285
|
const cpHex = toCheckpointHex(cp);
|
|
294
286
|
const key = toCacheKey(cpHex);
|
|
295
287
|
const cacheItem = this.cache.get(key);
|
|
296
|
-
this.metrics?.adds.inc();
|
|
288
|
+
this.metrics?.cpStateCache.adds.inc();
|
|
297
289
|
if (cacheItem !== undefined && isPersistedCacheItem(cacheItem)) {
|
|
298
290
|
const persistedKey = cacheItem.value;
|
|
299
291
|
// was persisted to disk, set back to memory
|
|
@@ -301,7 +293,7 @@ export class PersistentCheckpointStateCache {
|
|
|
301
293
|
this.logger.verbose("Added checkpoint state to memory but a persisted key existed", {
|
|
302
294
|
epoch: cp.epoch,
|
|
303
295
|
rootHex: cpHex.rootHex,
|
|
304
|
-
persistedKey:
|
|
296
|
+
persistedKey: toHex(persistedKey),
|
|
305
297
|
});
|
|
306
298
|
}
|
|
307
299
|
else {
|
|
@@ -588,7 +580,7 @@ export class PersistentCheckpointStateCache {
|
|
|
588
580
|
async processPastEpoch(blockRootHex, state, epoch) {
|
|
589
581
|
let persistCount = 0;
|
|
590
582
|
const epochBoundarySlot = computeStartSlotAtEpoch(epoch);
|
|
591
|
-
const epochBoundaryRoot = epochBoundarySlot === state.slot ?
|
|
583
|
+
const epochBoundaryRoot = epochBoundarySlot === state.slot ? fromHex(blockRootHex) : getBlockRootAtSlot(state, epochBoundarySlot);
|
|
592
584
|
const epochBoundaryHex = toRootHex(epochBoundaryRoot);
|
|
593
585
|
const prevEpochRoot = toRootHex(getBlockRootAtSlot(state, epochBoundarySlot - 1));
|
|
594
586
|
// for each epoch, usually there are 2 rootHexes respective to the 2 checkpoint states: Previous Root Checkpoint State and Current Root Checkpoint State
|
|
@@ -614,7 +606,7 @@ export class PersistentCheckpointStateCache {
|
|
|
614
606
|
stateSlot: state.slot,
|
|
615
607
|
rootHex,
|
|
616
608
|
epochBoundaryHex,
|
|
617
|
-
persistedKey: persistedKey ?
|
|
609
|
+
persistedKey: persistedKey ? toHex(persistedKey) : "",
|
|
618
610
|
};
|
|
619
611
|
if (persistedRootHexes.has(rootHex)) {
|
|
620
612
|
if (persistedKey) {
|
|
@@ -623,21 +615,25 @@ export class PersistentCheckpointStateCache {
|
|
|
623
615
|
}
|
|
624
616
|
else {
|
|
625
617
|
// persist and do not update epochIndex
|
|
626
|
-
this.metrics?.statePersistSecFromSlot.observe(this.clock?.secFromSlot(this.clock?.currentSlot ?? 0) ?? 0);
|
|
627
|
-
const cpPersist = { epoch: epoch, root:
|
|
618
|
+
this.metrics?.cpStateCache.statePersistSecFromSlot.observe(this.clock?.secFromSlot(this.clock?.currentSlot ?? 0) ?? 0);
|
|
619
|
+
const cpPersist = { epoch: epoch, root: fromHex(rootHex) };
|
|
628
620
|
// It's not sustainable to allocate ~240MB for each state every epoch, so we use buffer pool to reuse the memory.
|
|
629
621
|
// As monitored on holesky as of Jan 2024:
|
|
630
622
|
// - This does not increase heap allocation while gc time is the same
|
|
631
623
|
// - It helps stabilize persist time and save ~300ms in average (1.5s vs 1.2s)
|
|
632
624
|
// - It also helps the state reload to save ~500ms in average (4.3s vs 3.8s)
|
|
633
625
|
// - Also `serializeState.test.ts` perf test shows a lot of differences allocating ~240MB once vs per state serialization
|
|
634
|
-
const timer = this.metrics?.stateSerializeDuration.startTimer(
|
|
635
|
-
|
|
636
|
-
|
|
626
|
+
const timer = this.metrics?.stateSerializeDuration.startTimer({
|
|
627
|
+
source: AllocSource.PERSISTENT_CHECKPOINTS_CACHE_STATE,
|
|
628
|
+
});
|
|
629
|
+
persistedKey = await serializeState(state, AllocSource.PERSISTENT_CHECKPOINTS_CACHE_STATE, (stateBytes) => {
|
|
630
|
+
timer?.();
|
|
631
|
+
return this.datastore.write(cpPersist, stateBytes);
|
|
632
|
+
}, this.bufferPool);
|
|
637
633
|
persistCount++;
|
|
638
634
|
this.logger.verbose("Pruned checkpoint state from memory and persisted to disk", {
|
|
639
635
|
...logMeta,
|
|
640
|
-
persistedKey:
|
|
636
|
+
persistedKey: toHex(persistedKey),
|
|
641
637
|
});
|
|
642
638
|
}
|
|
643
639
|
// overwrite cpKey, this means the state is deleted from memory
|
|
@@ -655,7 +651,7 @@ export class PersistentCheckpointStateCache {
|
|
|
655
651
|
this.cache.delete(cpKey);
|
|
656
652
|
this.epochIndex.get(epoch)?.delete(rootHex);
|
|
657
653
|
}
|
|
658
|
-
this.metrics?.statePruneFromMemoryCount.inc();
|
|
654
|
+
this.metrics?.cpStateCache.statePruneFromMemoryCount.inc();
|
|
659
655
|
this.logger.verbose("Pruned checkpoint state from memory", logMeta);
|
|
660
656
|
}
|
|
661
657
|
}
|
|
@@ -676,7 +672,7 @@ export class PersistentCheckpointStateCache {
|
|
|
676
672
|
if (persistedKey) {
|
|
677
673
|
await this.datastore.remove(persistedKey);
|
|
678
674
|
persistCount++;
|
|
679
|
-
this.metrics?.persistedStateRemoveCount.inc();
|
|
675
|
+
this.metrics?.cpStateCache.persistedStateRemoveCount.inc();
|
|
680
676
|
}
|
|
681
677
|
}
|
|
682
678
|
this.cache.delete(key);
|