Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -17,34 +17,28 @@ export async function updateBackfillRange(
{chain, db, logger}: {chain: IBeaconChain; db: IBeaconDb; logger: Logger},
finalized: CheckpointWithHex
): Promise<void> {
try {
// Mark the sequence in backfill db from finalized block's slot till anchor slot as
// filled.
const finalizedBlockFC = chain.forkChoice.getBlockHexDefaultStatus(finalized.rootHex);
if (finalizedBlockFC && finalizedBlockFC.slot > chain.anchorStateLatestBlockSlot) {
await db.backfilledRanges.put(finalizedBlockFC.slot, chain.anchorStateLatestBlockSlot);

// Clear previously marked sequence till anchorStateLatestBlockSlot, without
// touching backfill sync process sequence which are at
// <=anchorStateLatestBlockSlot i.e. clear >anchorStateLatestBlockSlot
// and < currentSlot
const filteredSeqs = await db.backfilledRanges.entries({
gt: chain.anchorStateLatestBlockSlot,
lt: finalizedBlockFC.slot,
});
logger.debug("updated backfilledRanges", {
key: finalizedBlockFC.slot,
value: chain.anchorStateLatestBlockSlot,
});
if (filteredSeqs.length > 0) {
await db.backfilledRanges.batchDelete(filteredSeqs.map((entry) => entry.key));
logger.debug(
`Forward Sync - cleaned up backfilledRanges between ${finalizedBlockFC.slot},${chain.anchorStateLatestBlockSlot}`,
{seqs: JSON.stringify(filteredSeqs)}
);
}
}
} catch (e) {
logger.error("Error updating backfilledRanges on finalization", {epoch: finalized.epoch}, e as Error);
}
// try {
// const finalizedBlockFC = chain.forkChoice.getBlockHexDefaultStatus(finalized.rootHex);
// if (finalizedBlockFC && finalizedBlockFC.slot > chain.anchorStateLatestBlockSlot) {
// await db.backfilledRanges.put(finalizedBlockFC.slot, chain.anchorStateLatestBlockSlot);
//
// const filteredSeqs = await db.backfilledRanges.entries({
// gt: chain.anchorStateLatestBlockSlot,
// lt: finalizedBlockFC.slot,
// });
// logger.debug("updated backfilledRanges", {
// key: finalizedBlockFC.slot,
// value: chain.anchorStateLatestBlockSlot,
// });
// if (filteredSeqs.length > 0) {
// await db.backfilledRanges.batchDelete(filteredSeqs.map((entry) => entry.key));
// logger.debug(
// `Forward Sync - cleaned up backfilledRanges between ${finalizedBlockFC.slot},${chain.anchorStateLatestBlockSlot}`,
// {seqs: JSON.stringify(filteredSeqs)}
// );
// }
// }
// } catch (e) {
// logger.error("Error updating backfilledRanges on finalization", {epoch: finalized.epoch}, e as Error);
// }
Comment on lines +20 to +43

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The logic for updating the backfill range during finalization has been commented out. This effectively disables progress tracking for backfill sync during normal node operation. If this logic is being replaced by the new backfillState and backfilledRange singleton, it should be updated to use them instead of being left as commented-out code.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Confirmed — the entire updateBackfillRange body is now commented out (lines ~17-43 of packages/beacon-node/src/chain/archiveStore/utils/updateBackfillRange.ts). The function is still called from the finalization path but is a no-op. Same scope question as discussion_r3109761377 — either restore it pointing at the new singleton backfilledRange + per-epoch backfillState, or delete the call site and the helper entirely as part of the legacy-removal change.

}
9 changes: 6 additions & 3 deletions packages/beacon-node/src/db/beacon.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ import {ChainForkConfig} from "@lodestar/config";
import {Db, LevelDbControllerMetrics, encodeKey} from "@lodestar/db";
import {Bucket} from "./buckets.js";
import {IBeaconDb} from "./interface.js";
import {BackfillStateRepository} from "./repositories/backfillState.js";
import {CheckpointStateRepository} from "./repositories/checkpointState.js";
import {
AttesterSlashingRepository,
BLSToExecutionChangeRepository,
BackfilledRanges,
BestLightClientUpdateRepository,
BlobSidecarsArchiveRepository,
BlobSidecarsRepository,
Expand All @@ -23,6 +23,7 @@ import {
SyncCommitteeWitnessRepository,
VoluntaryExitRepository,
} from "./repositories/index.js";
import {BackfilledRange} from "./single/backfilledRange.js";

export type BeaconDbModules = {
config: ChainForkConfig;
Expand Down Expand Up @@ -55,7 +56,8 @@ export class BeaconDb implements IBeaconDb {
syncCommittee: SyncCommitteeRepository;
syncCommitteeWitness: SyncCommitteeWitnessRepository;

backfilledRanges: BackfilledRanges;
backfillState: BackfillStateRepository;
backfilledRange: BackfilledRange;

constructor(
config: ChainForkConfig,
Expand Down Expand Up @@ -86,7 +88,8 @@ export class BeaconDb implements IBeaconDb {
this.syncCommittee = new SyncCommitteeRepository(config, db);
this.syncCommitteeWitness = new SyncCommitteeWitnessRepository(config, db);

this.backfilledRanges = new BackfilledRanges(config, db);
this.backfillState = new BackfillStateRepository(config, db);
this.backfilledRange = new BackfilledRange(config, db);
}

close(): Promise<void> {
Expand Down
2 changes: 1 addition & 1 deletion packages/beacon-node/src/db/buckets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ export enum Bucket {
// altair_lightClientSyncCommitteeProof = 35, // DEPRECATED on v0.32.0
// index_lightClientInitProof = 36, // DEPRECATED on v0.32.0

backfilled_ranges = 42, // Backfilled From to To, inclusive of both From, To
backfill_state = 42, // Epoch -> EpochBackfillState

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Repurposing bucket ID 42 from backfilled_ranges (which stored Slot -> Slot) to backfill_state (which stores Epoch -> BackfillStateWrapper) is a breaking change for the database schema. Existing databases will contain data in the old format, which will cause deserialization errors or logic bugs when accessed by the new code. A database migration should be provided, or a new bucket ID should be allocated for the new backfill state.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Confirmed — verified by diffing packages/beacon-node/src/db/buckets.ts against origin/unstable:

  • unstable@HEAD: backfilled_ranges = 42 // Backfilled From to To, inclusive of both From, To (Slot → Slot)
  • this PR: backfill_state = 42 // Epoch -> EpochBackfillState

Same ID, different schema. Any existing node DB has entries at bucket 42 in the old Slot → Slot shape; the new repository would try to deserialize those bytes as EpochBackfillState and either fail or load garbage. Two clean paths: allocate a fresh bucket ID for backfill_state and leave 42 as a deprecated reserved slot (lowest-risk), or add a one-shot migration that drops the old bucket 42 contents before the new repository is wired in. Reserving a new ID is the cheaper option.


// Buckets to support LightClient server v2
lightClient_syncCommitteeWitness = 51, // BlockRoot -> SyncCommitteeWitness
Expand Down
7 changes: 5 additions & 2 deletions packages/beacon-node/src/db/interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import {CheckpointStateRepository} from "./repositories/checkpointState.js";
import {
AttesterSlashingRepository,
BLSToExecutionChangeRepository,
BackfilledRanges,
BackfillStateRepository,
BestLightClientUpdateRepository,
BlobSidecarsArchiveRepository,
BlobSidecarsRepository,
Expand All @@ -20,6 +20,7 @@ import {
SyncCommitteeWitnessRepository,
VoluntaryExitRepository,
} from "./repositories/index.js";
import {BackfilledRange} from "./single/backfilledRange.js";

/**
* The DB service manages the data layer of the beacon chain
Expand Down Expand Up @@ -57,7 +58,9 @@ export interface IBeaconDb {
syncCommittee: SyncCommitteeRepository;
syncCommitteeWitness: SyncCommitteeWitnessRepository;

backfilledRanges: BackfilledRanges;
// backfill
backfillState: BackfillStateRepository;
backfilledRange: BackfilledRange;

pruneHotDb(): Promise<void>;

Expand Down
36 changes: 36 additions & 0 deletions packages/beacon-node/src/db/repositories/backfillState.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import {ContainerType, ListBasicType, OptionalType, ValueOf} from "@chainsafe/ssz";
import {ChainForkConfig} from "@lodestar/config";
import {DatabaseController, Repository} from "@lodestar/db";
import {NUMBER_OF_COLUMNS} from "@lodestar/params";
import {ssz} from "@lodestar/types";
import {bytesToInt} from "@lodestar/utils";
import {Bucket, getBucketNameByValue} from "../buckets.js";
import {BACKFILLED_RANGE_KEY} from "../single/backfilledRange.js";

export const backfillStateWrapperSsz = new ContainerType(
{
hasBlock: ssz.Boolean,
hasBlobs: new OptionalType(ssz.Boolean),
columnIndices: new OptionalType(new ListBasicType(ssz.ColumnIndex, NUMBER_OF_COLUMNS)),
},
{typeName: "BackfillStateWrapper", jsonCase: "eth2"}
);
export type BackfillStateWrapper = ValueOf<typeof backfillStateWrapperSsz>;

export class BackfillStateRepository extends Repository<number, BackfillStateWrapper> {
constructor(config: ChainForkConfig, db: DatabaseController<Uint8Array, Uint8Array>) {
const bucket = Bucket.backfill_state;
super(config, db, bucket, backfillStateWrapperSsz, getBucketNameByValue(bucket));
}

encodeKey(key: number): Uint8Array {
if (key === BACKFILLED_RANGE_KEY) {
throw new Error("Reserved key for backfill range singleton object");
}
return super.encodeKey(key);
}

decodeKey(data: Uint8Array): number {
return bytesToInt(super.decodeKey(data) as unknown as Uint8Array, "be");
}
}
29 changes: 0 additions & 29 deletions packages/beacon-node/src/db/repositories/backfilledRanges.ts

This file was deleted.

2 changes: 1 addition & 1 deletion packages/beacon-node/src/db/repositories/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
export {AttesterSlashingRepository} from "./attesterSlashing.js";
export {BackfilledRanges} from "./backfilledRanges.js";
export {BackfillStateRepository} from "./backfillState.js";
export {BlobSidecarsRepository} from "./blobSidecars.js";
export {BlobSidecarsArchiveRepository} from "./blobSidecarsArchive.js";
export {BlockRepository} from "./block.js";
Expand Down
37 changes: 37 additions & 0 deletions packages/beacon-node/src/db/single/backfilledRange.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import {ContainerType, ValueOf} from "@chainsafe/ssz";
import {ChainForkConfig} from "@lodestar/config";
import {Db, DbReqOpts, encodeKey} from "@lodestar/db";
import {ssz} from "@lodestar/types";
import {Bucket, getBucketNameByValue} from "../buckets.js";

export const backfilledRangeWrapperSsz = new ContainerType(
{beginningEpoch: ssz.Epoch, endingEpoch: ssz.Epoch},
{typeName: "BackfilledRange", jsonCase: "eth2"}
);
export type BackfilledRangeWrapper = ValueOf<typeof backfilledRangeWrapperSsz>;
export const BACKFILLED_RANGE_KEY = -1;

export class BackfilledRange {
private readonly db: Db;
private readonly key: Uint8Array;
private readonly dbReqOpts: DbReqOpts;

constructor(_config: ChainForkConfig, db: Db) {
this.db = db;
this.key = encodeKey(Bucket.backfill_state, BACKFILLED_RANGE_KEY);
this.dbReqOpts = {bucketId: getBucketNameByValue(Bucket.backfill_state)};
}

async put(value: BackfilledRangeWrapper): Promise<void> {
await this.db.put(this.key, backfilledRangeWrapperSsz.serialize(value), this.dbReqOpts);
}

async get(): Promise<BackfilledRangeWrapper | null> {
const value = await this.db.get(this.key, this.dbReqOpts);
return value ? backfilledRangeWrapperSsz.deserialize(value) : null;
}

async delete(): Promise<void> {
await this.db.delete(this.key, this.dbReqOpts);
}
}
1 change: 1 addition & 0 deletions packages/beacon-node/src/db/single/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export {BACKFILLED_RANGE_KEY, BackfilledRange} from "./backfilledRange.js";
Loading
Loading