fix: add bucketId to all db operations (#8347)

**Motivation**

Similar to https://github.com/ChainSafe/lodestar/pull/8346 adds missing
`bucketId` to all db operations to avoid `unknown`

This is from `stable-lg1k-hzax41-dkr` which shows it's already an issue
there

<img width="1890" height="684" alt="image"
src="https://github.com/user-attachments/assets/cfbdb335-b3ec-49b7-9ae2-be345ce8071e"
/>


**Description**

Add `bucketId` to all db operations

deployed to `feat3` to make sure I didn't miss anything
This commit is contained in:
Nico Flaig
2025-09-08 19:47:47 +01:00
committed by GitHub
parent bb10d2b5ea
commit bb15e8ef5f
8 changed files with 54 additions and 26 deletions

View File

@@ -5,7 +5,7 @@ import {bytesToInt} from "@lodestar/utils";
import all from "it-all";
import {getSignedBlockTypeFromBytes} from "../../util/multifork.js";
import {Bucket, getBucketNameByValue} from "../buckets.js";
import {getParentRootIndexKey, getRootIndexKey} from "./blockArchiveIndex.js";
import {getParentRootIndex, getRootIndex} from "./blockArchiveIndex.js";
import {deleteParentRootIndex, deleteRootIndex, storeParentRootIndex, storeRootIndex} from "./blockArchiveIndex.js";
export interface BlockFilterOptions extends FilterOptions<Slot> {
@@ -136,11 +136,11 @@ export class BlockArchiveRepository extends Repository<Slot, SignedBeaconBlock>
}
async getSlotByRoot(root: Root): Promise<Slot | null> {
return this.parseSlot(await this.db.get(getRootIndexKey(root)));
return this.parseSlot(await getRootIndex(this.db, root));
}
async getSlotByParentRoot(root: Root): Promise<Slot | null> {
return this.parseSlot(await this.db.get(getParentRootIndexKey(root)));
return this.parseSlot(await getParentRootIndex(this.db, root));
}
private parseSlot(slotBytes: Uint8Array | null): Slot | null {

View File

@@ -2,14 +2,25 @@ import {Db, encodeKey} from "@lodestar/db";
import {ForkAll} from "@lodestar/params";
import {Root, SSZTypesFor, SignedBeaconBlock, Slot, ssz} from "@lodestar/types";
import {intToBytes} from "@lodestar/utils";
import {Bucket} from "../buckets.js";
import {Bucket, getBucketNameByValue} from "../buckets.js";
export const rootIndexBucketId = getBucketNameByValue(Bucket.index_blockArchiveRootIndex);
export const parentRootIndexBucketId = getBucketNameByValue(Bucket.index_blockArchiveParentRootIndex);
export async function getRootIndex(db: Db, blockRoot: Root): Promise<Uint8Array | null> {
return db.get(getRootIndexKey(blockRoot), {bucketId: rootIndexBucketId});
}
export async function getParentRootIndex(db: Db, parentRoot: Root): Promise<Uint8Array | null> {
return db.get(getParentRootIndexKey(parentRoot), {bucketId: parentRootIndexBucketId});
}
export async function storeRootIndex(db: Db, slot: Slot, blockRoot: Root): Promise<void> {
return db.put(getRootIndexKey(blockRoot), intToBytes(slot, 8, "be"));
return db.put(getRootIndexKey(blockRoot), intToBytes(slot, 8, "be"), {bucketId: rootIndexBucketId});
}
export async function storeParentRootIndex(db: Db, slot: Slot, parentRoot: Root): Promise<void> {
return db.put(getParentRootIndexKey(parentRoot), intToBytes(slot, 8, "be"));
return db.put(getParentRootIndexKey(parentRoot), intToBytes(slot, 8, "be"), {bucketId: parentRootIndexBucketId});
}
export async function deleteRootIndex(
@@ -18,11 +29,11 @@ export async function deleteRootIndex(
block: SignedBeaconBlock
): Promise<void> {
const beaconBlockType = (signedBeaconBlockType as typeof ssz.phase0.SignedBeaconBlock).fields.message;
return db.delete(getRootIndexKey(beaconBlockType.hashTreeRoot(block.message)));
return db.delete(getRootIndexKey(beaconBlockType.hashTreeRoot(block.message)), {bucketId: rootIndexBucketId});
}
export async function deleteParentRootIndex(db: Db, block: SignedBeaconBlock): Promise<void> {
return db.delete(getParentRootIndexKey(block.message.parentRoot));
return db.delete(getParentRootIndexKey(block.message.parentRoot), {bucketId: parentRootIndexBucketId});
}
export function getParentRootIndexKey(parentRoot: Root): Uint8Array {

View File

@@ -5,7 +5,7 @@ import {Epoch, Root, RootHex, Slot, ssz} from "@lodestar/types";
import {bytesToInt, toHex} from "@lodestar/utils";
import {getStateTypeFromBytes} from "../../util/multifork.js";
import {Bucket, getBucketNameByValue} from "../buckets.js";
import {getRootIndexKey, storeRootIndex} from "./stateArchiveIndex.js";
import {getRootIndex, getRootIndexKey, storeRootIndex} from "./stateArchiveIndex.js";
export class StateArchiveRepository extends Repository<Slot, BeaconStateAllForks> {
constructor(config: ChainForkConfig, db: Db) {
@@ -54,6 +54,7 @@ export class StateArchiveRepository extends Repository<Slot, BeaconStateAllForks
const entries = await this.db.entries({
lte: getRootIndexKey(Buffer.alloc(32, 0xff)),
gte: getRootIndexKey(Buffer.alloc(32, 0x00)),
bucketId: this.bucketId,
});
return entries.map((entry) => ({
root: toHex(entry.key),
@@ -62,7 +63,7 @@ export class StateArchiveRepository extends Repository<Slot, BeaconStateAllForks
}
private async getSlotByRoot(root: Root): Promise<Slot | null> {
const value = await this.db.get(getRootIndexKey(root));
const value = await getRootIndex(this.db, root);
return value && bytesToInt(value, "be");
}
}

View File

@@ -1,10 +1,16 @@
import {Db, encodeKey} from "@lodestar/db";
import {Root, Slot} from "@lodestar/types";
import {intToBytes} from "@lodestar/utils";
import {Bucket} from "../buckets.js";
import {Bucket, getBucketNameByValue} from "../buckets.js";
const bucketId = getBucketNameByValue(Bucket.index_stateArchiveRootIndex);
export function getRootIndex(db: Db, stateRoot: Root): Promise<Uint8Array | null> {
return db.get(getRootIndexKey(stateRoot), {bucketId});
}
export function storeRootIndex(db: Db, slot: Slot, stateRoot: Root): Promise<void> {
return db.put(getRootIndexKey(stateRoot), intToBytes(slot, 8, "be"));
return db.put(getRootIndexKey(stateRoot), intToBytes(slot, 8, "be"), {bucketId});
}
export function getRootIndexKey(root: Root): Uint8Array {

View File

@@ -1,9 +1,9 @@
import {ChainForkConfig} from "@lodestar/config";
import {Db} from "@lodestar/db";
import {Db, DbReqOpts} from "@lodestar/db";
import {ForkAll, GENESIS_SLOT} from "@lodestar/params";
import {BeaconStateAllForks} from "@lodestar/state-transition";
import {SSZTypesFor} from "@lodestar/types";
import {Bucket} from "../buckets.js";
import {Bucket, getBucketNameByValue} from "../buckets.js";
export class PreGenesisState {
private readonly config: ChainForkConfig;
@@ -11,6 +11,7 @@ export class PreGenesisState {
private readonly db: Db;
private readonly key: Uint8Array;
private readonly type: SSZTypesFor<ForkAll, "BeaconState">;
private readonly dbReqOpts: DbReqOpts;
constructor(config: ChainForkConfig, db: Db) {
this.config = config;
@@ -18,18 +19,19 @@ export class PreGenesisState {
this.bucket = Bucket.phase0_preGenesisState;
this.key = new Uint8Array([this.bucket]);
this.type = this.config.getForkTypes(GENESIS_SLOT).BeaconState;
this.dbReqOpts = {bucketId: getBucketNameByValue(this.bucket)};
}
async put(value: BeaconStateAllForks): Promise<void> {
await this.db.put(this.key, value.serialize());
await this.db.put(this.key, value.serialize(), this.dbReqOpts);
}
async get(): Promise<BeaconStateAllForks | null> {
const value = await this.db.get(this.key);
const value = await this.db.get(this.key, this.dbReqOpts);
return value ? this.type.deserializeToViewDU(value) : null;
}
async delete(): Promise<void> {
await this.db.delete(this.key);
await this.db.delete(this.key, this.dbReqOpts);
}
}

View File

@@ -1,32 +1,34 @@
import {UintNumberType} from "@chainsafe/ssz";
import {ChainForkConfig} from "@lodestar/config";
import {Db} from "@lodestar/db";
import {Db, DbReqOpts} from "@lodestar/db";
import {ssz} from "@lodestar/types";
import {Bucket} from "../buckets.js";
import {Bucket, getBucketNameByValue} from "../buckets.js";
export class PreGenesisStateLastProcessedBlock {
private readonly bucket: Bucket;
private readonly type: UintNumberType;
private readonly db: Db;
private readonly key: Uint8Array;
private readonly dbReqOpts: DbReqOpts;
constructor(_config: ChainForkConfig, db: Db) {
this.db = db;
this.type = ssz.UintNum64;
this.bucket = Bucket.phase0_preGenesisStateLastProcessedBlock;
this.key = new Uint8Array([this.bucket]);
this.dbReqOpts = {bucketId: getBucketNameByValue(this.bucket)};
}
async put(value: number): Promise<void> {
await this.db.put(this.key, this.type.serialize(value));
await this.db.put(this.key, this.type.serialize(value), this.dbReqOpts);
}
async get(): Promise<number | null> {
const value = await this.db.get(this.key);
const value = await this.db.get(this.key, this.dbReqOpts);
return value ? this.type.deserialize(value) : null;
}
async delete(): Promise<void> {
await this.db.delete(this.key);
await this.db.delete(this.key, this.dbReqOpts);
}
}

View File

@@ -5,7 +5,7 @@ import {intToBytes} from "@lodestar/utils";
import {rimraf} from "rimraf";
import {afterEach, beforeEach, describe, expect, it, vi} from "vitest";
import {Bucket} from "../../../../../src/db/buckets.js";
import {Bucket, getBucketNameByValue} from "../../../../../src/db/buckets.js";
import {BlockArchiveRepository} from "../../../../../src/db/repositories/index.js";
import {testLogger} from "../../../../utils/logger.js";
@@ -112,11 +112,13 @@ describe("block archive repository", () => {
await blockArchive.add(block);
expect(spy).toHaveBeenCalledWith(
encodeKey(Bucket.index_blockArchiveRootIndex, ssz.phase0.BeaconBlock.hashTreeRoot(block.message)),
intToBytes(block.message.slot, 8, "be")
intToBytes(block.message.slot, 8, "be"),
{bucketId: getBucketNameByValue(Bucket.index_blockArchiveRootIndex)}
);
expect(spy).toHaveBeenCalledWith(
encodeKey(Bucket.index_blockArchiveParentRootIndex, block.message.parentRoot),
intToBytes(block.message.slot, 8, "be")
intToBytes(block.message.slot, 8, "be"),
{bucketId: getBucketNameByValue(Bucket.index_blockArchiveParentRootIndex)}
);
});
@@ -131,10 +133,12 @@ describe("block archive repository", () => {
[
encodeKey(Bucket.index_blockArchiveRootIndex, ssz.phase0.BeaconBlock.hashTreeRoot(blocks[0].message)),
intToBytes(blocks[0].message.slot, 8, "be"),
{bucketId: getBucketNameByValue(Bucket.index_blockArchiveRootIndex)},
],
[
encodeKey(Bucket.index_blockArchiveRootIndex, ssz.phase0.BeaconBlock.hashTreeRoot(blocks[0].message)),
intToBytes(blocks[0].message.slot, 8, "be"),
{bucketId: getBucketNameByValue(Bucket.index_blockArchiveRootIndex)},
],
])
);
@@ -143,10 +147,12 @@ describe("block archive repository", () => {
[
encodeKey(Bucket.index_blockArchiveParentRootIndex, blocks[0].message.parentRoot),
intToBytes(blocks[0].message.slot, 8, "be"),
{bucketId: getBucketNameByValue(Bucket.index_blockArchiveParentRootIndex)},
],
[
encodeKey(Bucket.index_blockArchiveParentRootIndex, blocks[0].message.parentRoot),
intToBytes(blocks[0].message.slot, 8, "be"),
{bucketId: getBucketNameByValue(Bucket.index_blockArchiveParentRootIndex)},
],
])
);

View File

@@ -26,7 +26,7 @@ export abstract class Repository<I extends Id, T> {
protected db: Db,
protected bucket: number,
protected type: Type<T>,
private readonly bucketId: string
protected readonly bucketId: string
) {
this.dbReqOpts = {bucketId: this.bucketId};
this.minKey = _encodeKey(bucket, Buffer.alloc(0));