@lodestar/beacon-node 1.51.0-dev.f39fad29a1 → 1.51.0-dev.fa491bf50f
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/validator/index.d.ts.map +1 -1
- package/lib/api/impl/validator/index.js +4 -1
- package/lib/api/impl/validator/index.js.map +1 -1
- package/lib/chain/archiveStore/utils/archiveBlocks.d.ts +4 -0
- package/lib/chain/archiveStore/utils/archiveBlocks.d.ts.map +1 -1
- package/lib/chain/archiveStore/utils/archiveBlocks.js +60 -39
- package/lib/chain/archiveStore/utils/archiveBlocks.js.map +1 -1
- package/lib/chain/blocks/importBlock.d.ts.map +1 -1
- package/lib/chain/blocks/importBlock.js +6 -5
- package/lib/chain/blocks/importBlock.js.map +1 -1
- package/lib/chain/blocks/index.js +1 -1
- package/lib/chain/blocks/index.js.map +1 -1
- package/lib/chain/chain.js +4 -4
- package/lib/chain/chain.js.map +1 -1
- package/lib/db/beacon.d.ts +7 -0
- package/lib/db/beacon.d.ts.map +1 -1
- package/lib/db/beacon.js +32 -0
- package/lib/db/beacon.js.map +1 -1
- package/lib/db/dataColumnStore.d.ts +1 -1
- package/lib/db/dataColumnStore.d.ts.map +1 -1
- package/lib/db/dataColumnStore.js +1 -4
- package/lib/db/dataColumnStore.js.map +1 -1
- package/lib/db/interface.d.ts +1 -0
- package/lib/db/interface.d.ts.map +1 -1
- package/lib/db/repositories/dataColumnSidecarArchive.d.ts +6 -0
- package/lib/db/repositories/dataColumnSidecarArchive.d.ts.map +1 -1
- package/lib/db/repositories/dataColumnSidecarArchive.js +19 -0
- package/lib/db/repositories/dataColumnSidecarArchive.js.map +1 -1
- package/lib/node/nodejs.d.ts.map +1 -1
- package/lib/node/nodejs.js +9 -1
- package/lib/node/nodejs.js.map +1 -1
- package/package.json +15 -15
- package/src/api/impl/validator/index.ts +4 -1
- package/src/chain/archiveStore/utils/archiveBlocks.ts +75 -46
- package/src/chain/blocks/importBlock.ts +8 -7
- package/src/chain/blocks/index.ts +1 -1
- package/src/chain/chain.ts +4 -4
- package/src/db/beacon.ts +39 -0
- package/src/db/dataColumnStore.ts +2 -4
- package/src/db/interface.ts +2 -0
- package/src/db/repositories/dataColumnSidecarArchive.ts +20 -0
- package/src/node/nodejs.ts +18 -1
|
@@ -147,7 +147,7 @@ export async function processBlocks(
|
|
|
147
147
|
proposerBalanceDelta: proposerBalanceDeltas[i],
|
|
148
148
|
indexedAttestations: indexedAttestationsByBlock[i],
|
|
149
149
|
// TODO: Make this param mandatory and capture in gossip
|
|
150
|
-
seenTimestampSec: opts.seenTimestampSec ??
|
|
150
|
+
seenTimestampSec: opts.seenTimestampSec ?? Date.now() / 1000,
|
|
151
151
|
});
|
|
152
152
|
}
|
|
153
153
|
|
package/src/chain/chain.ts
CHANGED
|
@@ -1486,10 +1486,10 @@ export class BeaconChain implements IBeaconChain {
|
|
|
1486
1486
|
this.metrics?.forkChoice.requests.inc();
|
|
1487
1487
|
const timer = this.metrics?.forkChoice.findHead.startTimer({caller: FindHeadFnName.predictProposerHead});
|
|
1488
1488
|
const slot = this.clock.currentSlot;
|
|
1489
|
-
const
|
|
1489
|
+
const msFromSlot = this.clock.msFromSlot(slot);
|
|
1490
1490
|
|
|
1491
1491
|
try {
|
|
1492
|
-
return this.forkChoice.updateAndGetHead({mode: UpdateHeadOpt.GetPredictedProposerHead,
|
|
1492
|
+
return this.forkChoice.updateAndGetHead({mode: UpdateHeadOpt.GetPredictedProposerHead, msFromSlot, slot}).head;
|
|
1493
1493
|
} catch (e) {
|
|
1494
1494
|
this.metrics?.forkChoice.errors.inc({entrypoint: UpdateHeadOpt.GetPredictedProposerHead});
|
|
1495
1495
|
throw e;
|
|
@@ -1501,13 +1501,13 @@ export class BeaconChain implements IBeaconChain {
|
|
|
1501
1501
|
getProposerHead(slot: Slot): ProtoBlock {
|
|
1502
1502
|
this.metrics?.forkChoice.requests.inc();
|
|
1503
1503
|
const timer = this.metrics?.forkChoice.findHead.startTimer({caller: FindHeadFnName.getProposerHead});
|
|
1504
|
-
const
|
|
1504
|
+
const msFromSlot = this.clock.msFromSlot(slot);
|
|
1505
1505
|
|
|
1506
1506
|
try {
|
|
1507
1507
|
// Do not emit head event here, when proposing we rely on the one emitted when importing our own block
|
|
1508
1508
|
const {head, isHeadTimely, notReorgedReason} = this.forkChoice.updateAndGetHead({
|
|
1509
1509
|
mode: UpdateHeadOpt.GetProposerHead,
|
|
1510
|
-
|
|
1510
|
+
msFromSlot,
|
|
1511
1511
|
slot,
|
|
1512
1512
|
});
|
|
1513
1513
|
|
package/src/db/beacon.ts
CHANGED
|
@@ -30,6 +30,9 @@ import {
|
|
|
30
30
|
VoluntaryExitRepository,
|
|
31
31
|
} from "./repositories/index.js";
|
|
32
32
|
|
|
33
|
+
/** Below this size, seeks over leftover legacy column data stay cheap */
|
|
34
|
+
const LEGACY_DATA_COLUMNS_COMPACTION_MIN_BYTES = 1024 ** 3;
|
|
35
|
+
|
|
33
36
|
export type BeaconDbOpts = {
|
|
34
37
|
dataColumnDir: string;
|
|
35
38
|
logger: Logger;
|
|
@@ -67,6 +70,7 @@ export class BeaconDb implements IBeaconDb {
|
|
|
67
70
|
readonly dataColumns: IDataColumnStore;
|
|
68
71
|
lastLegacyArchiveSlot: Slot | null = null;
|
|
69
72
|
private readonly flatFileStore: FlatFileStore;
|
|
73
|
+
private readonly logger: Logger;
|
|
70
74
|
|
|
71
75
|
constructor(
|
|
72
76
|
config: ChainForkConfig,
|
|
@@ -100,6 +104,7 @@ export class BeaconDb implements IBeaconDb {
|
|
|
100
104
|
this.syncCommittee = new SyncCommitteeRepository(config, db);
|
|
101
105
|
this.syncCommitteeWitness = new SyncCommitteeWitnessRepository(config, db);
|
|
102
106
|
|
|
107
|
+
this.logger = opts.logger;
|
|
103
108
|
this.flatFileStore = new FlatFileStore(opts.dataColumnDir, config, opts.logger, opts.metrics);
|
|
104
109
|
this.dataColumns = new LegacyDataColumnStore(
|
|
105
110
|
this.flatFileStore,
|
|
@@ -125,6 +130,40 @@ export class BeaconDb implements IBeaconDb {
|
|
|
125
130
|
this.flatFileStore.setMetrics(flatFileStoreMetrics);
|
|
126
131
|
}
|
|
127
132
|
|
|
133
|
+
/**
|
|
134
|
+
* Deletes made while an iterator held a snapshot can stay in the bottom LevelDB level, which `compactRange` never
|
|
135
|
+
* rewrites, so every seek into the legacy column buckets walks them. Writing a key at each end of the pruned range
|
|
136
|
+
* makes the compaction overlap those files and rewrite them.
|
|
137
|
+
*/
|
|
138
|
+
async compactLegacyDataColumns(
|
|
139
|
+
prunedBeforeSlot: Slot,
|
|
140
|
+
minSizeBytes = LEGACY_DATA_COLUMNS_COMPACTION_MIN_BYTES
|
|
141
|
+
): Promise<void> {
|
|
142
|
+
const archiveBucket = Bucket.allForks_dataColumnSidecarsArchive;
|
|
143
|
+
const start = encodeKey(Bucket.allForks_dataColumnSidecars, Buffer.alloc(0));
|
|
144
|
+
const end = encodeKey(archiveBucket, this.dataColumnSidecarArchive.getMinKeyRaw(prunedBeforeSlot));
|
|
145
|
+
const sizeBytes = await this.db.approximateSize(start, end);
|
|
146
|
+
if (sizeBytes < minSizeBytes) return;
|
|
147
|
+
|
|
148
|
+
// Column indices stay below 0xffff and hot column keys are shorter, so no sentinel matches a real key
|
|
149
|
+
const upperSentinel =
|
|
150
|
+
prunedBeforeSlot > 0
|
|
151
|
+
? encodeKey(archiveBucket, this.dataColumnSidecarArchive.encodeKeyRaw(prunedBeforeSlot - 1, 0xffff))
|
|
152
|
+
: encodeKey(Bucket.allForks_dataColumnSidecars, Buffer.alloc(35, 0xff));
|
|
153
|
+
const sentinels = [start, upperSentinel];
|
|
154
|
+
|
|
155
|
+
this.logger.info("Compacting legacy data columns, this may take a while", {sizeBytes});
|
|
156
|
+
const startTime = Date.now();
|
|
157
|
+
await this.db.batchPut(sentinels.map((key) => ({key, value: new Uint8Array()})));
|
|
158
|
+
await this.db.compactRange(start, end);
|
|
159
|
+
await this.db.batchDelete(sentinels);
|
|
160
|
+
await this.db.compactRange(start, end);
|
|
161
|
+
this.logger.verbose("Compacted legacy data columns", {
|
|
162
|
+
durationMs: Date.now() - startTime,
|
|
163
|
+
sizeBytes: await this.db.approximateSize(start, end),
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
|
|
128
167
|
async pruneHotDb(): Promise<void> {
|
|
129
168
|
// Prune all hot blobs
|
|
130
169
|
await this.blobSidecars.batchDelete(await this.blobSidecars.keys());
|
|
@@ -13,7 +13,7 @@ export type IndexedDataColumnBytes = {index: ColumnIndex; data: Uint8Array};
|
|
|
13
13
|
type LegacyHotDataColumnStore = Pick<DataColumnSidecarRepository, "values" | "getManyBinary" | "deleteMany">;
|
|
14
14
|
type LegacyArchiveDataColumnStore = Pick<
|
|
15
15
|
DataColumnSidecarArchiveRepository,
|
|
16
|
-
"values" | "getManyBinary" | "
|
|
16
|
+
"values" | "getManyBinary" | "pruneBefore"
|
|
17
17
|
>;
|
|
18
18
|
type BlockRootIndex = Pick<BlockArchiveRepository, "getSlotByRoot">;
|
|
19
19
|
|
|
@@ -86,9 +86,7 @@ export class LegacyDataColumnStore implements IDataColumnStore {
|
|
|
86
86
|
|
|
87
87
|
async pruneBefore(slot: Slot): Promise<Slot[]> {
|
|
88
88
|
const flatFileSlots = await this.flatFiles.pruneBefore(slot);
|
|
89
|
-
const
|
|
90
|
-
const slots = [...new Set(prefixedKeys.map(({prefix}) => prefix))];
|
|
91
|
-
if (slots.length > 0) await this.legacyArchive.deleteMany(slots);
|
|
89
|
+
const slots = await this.legacyArchive.pruneBefore(slot);
|
|
92
90
|
return [...new Set([...flatFileSlots, ...slots])].sort((a, b) => a - b);
|
|
93
91
|
}
|
|
94
92
|
}
|
package/src/db/interface.ts
CHANGED
|
@@ -68,6 +68,8 @@ export interface IBeaconDb {
|
|
|
68
68
|
readonly lastLegacyArchiveSlot: Slot | null;
|
|
69
69
|
init(): Promise<void>;
|
|
70
70
|
|
|
71
|
+
compactLegacyDataColumns(prunedBeforeSlot: Slot, minSizeBytes?: number): Promise<void>;
|
|
72
|
+
|
|
71
73
|
pruneHotDb(): Promise<void>;
|
|
72
74
|
|
|
73
75
|
deleteDeprecatedEth1Data(): Promise<void>;
|
|
@@ -15,6 +15,8 @@ const SLOT_BYTE_SIZE = 8;
|
|
|
15
15
|
* Indexed data by `slot` + `columnIndex`
|
|
16
16
|
*/
|
|
17
17
|
export class DataColumnSidecarArchiveRepository extends PrefixedRepository<Slot, ColumnIndex, DataColumnSidecar> {
|
|
18
|
+
private prunedBeforeSlot: Slot | null = null;
|
|
19
|
+
|
|
18
20
|
constructor(config: ChainForkConfig, db: Db) {
|
|
19
21
|
const bucket = Bucket.allForks_dataColumnSidecarsArchive;
|
|
20
22
|
// Type won't be used since we select it dynamically based on fork
|
|
@@ -70,4 +72,22 @@ export class DataColumnSidecarArchiveRepository extends PrefixedRepository<Slot,
|
|
|
70
72
|
encodeNumberForDbKey(0, COLUMN_INDEX_BYTE_SIZE),
|
|
71
73
|
]);
|
|
72
74
|
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Deletes all entries before `slot`. Prunes only delete from the start of the bucket, so the first prune
|
|
78
|
+
* compacts the range to drop tombstones of earlier runs and later prunes resume where the previous one ended.
|
|
79
|
+
*/
|
|
80
|
+
async pruneBefore(slot: Slot): Promise<Slot[]> {
|
|
81
|
+
if (this.prunedBeforeSlot === null) {
|
|
82
|
+
await this.db.compactRange(this.wrapKey(this.getMinKeyRaw(0)), this.wrapKey(this.getMinKeyRaw(slot)));
|
|
83
|
+
} else if (slot <= this.prunedBeforeSlot) {
|
|
84
|
+
return [];
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const keys = await this.keys({gte: {prefix: this.prunedBeforeSlot ?? 0, id: 0}, lt: {prefix: slot, id: 0}});
|
|
88
|
+
const slots = [...new Set(keys.map(({prefix}) => prefix))];
|
|
89
|
+
if (slots.length > 0) await this.deleteMany(slots);
|
|
90
|
+
this.prunedBeforeSlot = slot;
|
|
91
|
+
return slots;
|
|
92
|
+
}
|
|
73
93
|
}
|
package/src/node/nodejs.ts
CHANGED
|
@@ -13,11 +13,17 @@ import {BeaconApiMethods} from "@lodestar/api/beacon/server";
|
|
|
13
13
|
import {BeaconConfig} from "@lodestar/config";
|
|
14
14
|
import type {LoggerNode} from "@lodestar/logger/node";
|
|
15
15
|
import {ZERO_HASH_HEX} from "@lodestar/params";
|
|
16
|
-
import {
|
|
16
|
+
import {
|
|
17
|
+
IBeaconStateView,
|
|
18
|
+
computeStartSlotAtEpoch,
|
|
19
|
+
isStatePostBellatrix,
|
|
20
|
+
isStatePostGloas,
|
|
21
|
+
} from "@lodestar/state-transition";
|
|
17
22
|
import {Slot} from "@lodestar/types";
|
|
18
23
|
import {sleep, toRootHex} from "@lodestar/utils";
|
|
19
24
|
import {ProcessShutdownCallback} from "@lodestar/validator";
|
|
20
25
|
import {BeaconRestApiServer, getApi} from "../api/index.js";
|
|
26
|
+
import {getDataColumnSidecarsMinEpoch} from "../chain/archiveStore/utils/archiveBlocks.js";
|
|
21
27
|
import {BeaconChain, IBeaconChain, initBeaconMetrics} from "../chain/index.js";
|
|
22
28
|
import {ValidatorMonitor, createValidatorMonitor} from "../chain/validatorMonitor.js";
|
|
23
29
|
import {IBeaconDb} from "../db/index.js";
|
|
@@ -212,6 +218,17 @@ export class BeaconNode {
|
|
|
212
218
|
|
|
213
219
|
const clock = new Clock({config, genesisTime: anchorState.genesisTime, signal});
|
|
214
220
|
|
|
221
|
+
// Before any lookup into the legacy column buckets, which can be slow until compacted
|
|
222
|
+
const columnsMinEpoch =
|
|
223
|
+
opts.chain.archiveDataEpochs === Infinity
|
|
224
|
+
? null
|
|
225
|
+
: getDataColumnSidecarsMinEpoch(config, clock.currentEpoch, opts.chain.archiveDataEpochs);
|
|
226
|
+
await db.compactLegacyDataColumns(
|
|
227
|
+
columnsMinEpoch !== null && columnsMinEpoch >= config.FULU_FORK_EPOCH
|
|
228
|
+
? computeStartSlotAtEpoch(columnsMinEpoch)
|
|
229
|
+
: 0
|
|
230
|
+
);
|
|
231
|
+
|
|
215
232
|
await db.init();
|
|
216
233
|
|
|
217
234
|
// Prune hot db repos
|