chore: add logs and metrics to GetBlobsTracker (#8432)

**Motivation**

- we want to know the result of getBlobsV2 and how it helps our block
import

**Description**

- add logs and metrics to GetBlobsTracker

Closes #8401

---------

Co-authored-by: Tuyen Nguyen <twoeths@users.noreply.github.com>
This commit is contained in:
twoeths
2025-09-19 19:56:14 +07:00
committed by GitHub
parent 200e2443d4
commit c20158159f
3 changed files with 52 additions and 11 deletions

View File

@@ -4,7 +4,11 @@ import {ChainEventEmitter} from "./emitter.js";
import {Metrics} from "../metrics/metrics.js";
import {ChainForkConfig} from "@lodestar/config";
import {IBlockInput, isBlockInputBlobs} from "./blocks/blockInput/index.js";
import {getBlobSidecarsFromExecution, getDataColumnSidecarsFromExecution} from "../util/execution.js";
import {
DataColumnEngineResult,
getBlobSidecarsFromExecution,
getDataColumnSidecarsFromExecution,
} from "../util/execution.js";
import {callInNextEventLoop} from "../util/eventLoop.js";
import {computeEpochAtSlot} from "@lodestar/state-transition";
import {BLOB_AND_PROOF_V2_RPC_BYTES} from "../execution/engine/types.js";
@@ -49,8 +53,11 @@ export class GetBlobsTracker {
// store the index for the preallocated buffers
this.activeReconstructions.add(blockInput.blockRootHex);
callInNextEventLoop(() => {
const logCtx = {slot: blockInput.slot, root: blockInput.blockRootHex};
this.logger.verbose("Trigger getBlobsV1 for block", logCtx);
getBlobSidecarsFromExecution(this.config, this.executionEngine, this.metrics, this.emitter, blockInput).finally(
() => {
this.logger.verbose("Completed getBlobsV1 for block", logCtx);
this.activeReconstructions.delete(blockInput.blockRootHex);
}
);
@@ -80,6 +87,8 @@ export class GetBlobsTracker {
this.activeReconstructions.add(blockInput.blockRootHex);
this.blobsAndProofsBuffers[freeIndex].inUse = true;
callInNextEventLoop(() => {
const logCtx = {slot: blockInput.slot, root: blockInput.blockRootHex};
this.logger.verbose("Trigger getBlobsV2 for block", logCtx);
getDataColumnSidecarsFromExecution(
this.config,
this.executionEngine,
@@ -87,10 +96,20 @@ export class GetBlobsTracker {
blockInput,
this.metrics,
this.blobsAndProofsBuffers[freeIndex].buffers
).finally(() => {
this.activeReconstructions.delete(blockInput.blockRootHex);
this.blobsAndProofsBuffers[freeIndex].inUse = false;
});
)
.then((result) => {
this.logger.debug("getBlobsV2 result for block", {...logCtx, result});
this.metrics?.dataColumns.dataColumnEngineResult.inc({result});
})
.catch((error) => {
this.logger.debug("Error during getBlobsV2 for block", logCtx, error as Error);
this.metrics?.dataColumns.dataColumnEngineResult.inc({result: DataColumnEngineResult.Failed});
})
.finally(() => {
this.logger.verbose("Completed getBlobsV2 for block", logCtx);
this.activeReconstructions.delete(blockInput.blockRootHex);
this.blobsAndProofsBuffers[freeIndex].inUse = false;
});
});
}
}

View File

@@ -23,6 +23,7 @@ import {AllocSource} from "../../util/bufferPool.js";
import {DataColumnReconstructionCode} from "../../util/dataColumns.js";
import {LodestarMetadata} from "../options.js";
import {RegistryMetricCreator} from "../utils/registryMetricCreator.js";
import {DataColumnEngineResult} from "../../util/execution.js";
export type LodestarMetrics = ReturnType<typeof createLodestarMetrics>;
@@ -841,6 +842,11 @@ export function createLodestarMetrics(
name: "lodestar_data_columns_missing_custody_columns_count",
help: "Total number of missing columns that should be in the database but were not when requested",
}),
dataColumnEngineResult: register.counter<{result: DataColumnEngineResult}>({
name: "lodestar_data_column_engine_result_total",
help: "The total result of sending data column to execution layer",
labelNames: ["result"],
}),
},
importBlock: {
persistBlockNoSerializedDataCount: register.gauge({

View File

@@ -16,6 +16,21 @@ import {signedBlockToSignedHeader} from "@lodestar/state-transition";
import {routes} from "@lodestar/api";
import {toHex} from "@lodestar/utils";
export enum DataColumnEngineResult {
PreFulu = "pre_fulu",
// the recover is not attempted because it has full data columns
NotAttemptedFull = "not_attempted_full",
// block has no blob so no need to call EL
NotAttemptedNoBlobs = "not_attempted_no_blobs",
// EL call returned null, meaning it could not find the blobs
NullResponse = "null_response",
// the recover is a success and it helps resolve availability
SuccessResolved = "success_resolved",
// the recover is a success but it's late, availability is already resolved by either gossip or getBlobsV2
SuccessLate = "success_late",
Failed = "failed",
}
export async function getBlobSidecarsFromExecution(
config: ChainForkConfig,
executionEngine: IExecutionEngine,
@@ -118,22 +133,22 @@ export async function getDataColumnSidecarsFromExecution(
blockInput: IBlockInput,
metrics: Metrics | null,
blobAndProofBuffers?: Uint8Array[]
): Promise<void> {
): Promise<DataColumnEngineResult> {
// If its not a column block input, exit
if (!isBlockInputColumns(blockInput)) {
return;
return DataColumnEngineResult.PreFulu;
}
// If already have all columns, exit
if (blockInput.hasAllData()) {
return;
return DataColumnEngineResult.NotAttemptedFull;
}
const versionedHashes = blockInput.getVersionedHashes();
// If there are no blobs in this block, exit
if (versionedHashes.length === 0) {
return;
return DataColumnEngineResult.NotAttemptedNoBlobs;
}
// Get blobs from execution engine
@@ -148,13 +163,13 @@ export async function getDataColumnSidecarsFromExecution(
// Execution engine was unable to find one or more blobs
if (blobs === null) {
return;
return DataColumnEngineResult.NullResponse;
}
metrics?.peerDas.getBlobsV2Responses.inc();
// Return if we received all data columns while waiting for getBlobs
if (blockInput.hasAllData()) {
return;
return DataColumnEngineResult.SuccessLate;
}
let dataColumnSidecars: fulu.DataColumnSidecars;
@@ -187,4 +202,5 @@ export async function getDataColumnSidecarsFromExecution(
}
metrics?.dataColumns.bySource.inc({source: BlockInputSource.engine}, previouslyMissingColumns.length);
return DataColumnEngineResult.SuccessResolved;
}