diff --git a/yarn-project/p2p/src/client/factory.ts b/yarn-project/p2p/src/client/factory.ts index 1ec728155f98..08d1dd209343 100644 --- a/yarn-project/p2p/src/client/factory.ts +++ b/yarn-project/p2p/src/client/factory.ts @@ -161,6 +161,7 @@ export async function createP2PClient( config.txCollectionFileStoreUrls, txFileStoreBasePath, logger.createChild('file-store-tx-source'), + telemetry, ); if (fileStoreSources.length > 0) { logger.info(`Using ${fileStoreSources.length} file store sources for tx collection.`, { diff --git a/yarn-project/p2p/src/services/tx_collection/file_store_tx_source.ts b/yarn-project/p2p/src/services/tx_collection/file_store_tx_source.ts index ec8381d2d6cf..d9762a955d43 100644 --- a/yarn-project/p2p/src/services/tx_collection/file_store_tx_source.ts +++ b/yarn-project/p2p/src/services/tx_collection/file_store_tx_source.ts @@ -1,28 +1,51 @@ import { type Logger, createLogger } from '@aztec/foundation/log'; +import { Timer } from '@aztec/foundation/timer'; import { type ReadOnlyFileStore, createReadOnlyFileStore } from '@aztec/stdlib/file-store'; import { Tx, type TxHash } from '@aztec/stdlib/tx'; +import { + type Histogram, + Metrics, + type TelemetryClient, + type UpDownCounter, + getTelemetryClient, +} from '@aztec/telemetry-client'; import type { TxSource } from './tx_source.js'; /** TxSource implementation that downloads txs from a file store. */ export class FileStoreTxSource implements TxSource { + private downloadsSuccess: UpDownCounter; + private downloadsFailed: UpDownCounter; + private downloadDuration: Histogram; + private downloadSize: Histogram; + private constructor( private readonly fileStore: ReadOnlyFileStore, private readonly baseUrl: string, private readonly basePath: string, private readonly log: Logger, - ) {} + telemetry: TelemetryClient, + ) { + const meter = telemetry.getMeter('file-store-tx-source'); + this.downloadsSuccess = meter.createUpDownCounter(Metrics.TX_FILE_STORE_DOWNLOADS_SUCCESS); + this.downloadsFailed = meter.createUpDownCounter(Metrics.TX_FILE_STORE_DOWNLOADS_FAILED); + this.downloadDuration = meter.createHistogram(Metrics.TX_FILE_STORE_DOWNLOAD_DURATION); + this.downloadSize = meter.createHistogram(Metrics.TX_FILE_STORE_DOWNLOAD_SIZE); + } /** * Creates a FileStoreTxSource from a URL. * @param url - The file store URL (s3://, gs://, file://, http://, https://). + * @param basePath - Base path for tx files within the store. * @param log - Optional logger. + * @param telemetry - Optional telemetry client. * @returns The FileStoreTxSource instance, or undefined if creation fails. */ public static async create( url: string, basePath: string, log: Logger = createLogger('p2p:file_store_tx_source'), + telemetry: TelemetryClient = getTelemetryClient(), ): Promise { try { const fileStore = await createReadOnlyFileStore(url, log); @@ -30,7 +53,7 @@ export class FileStoreTxSource implements TxSource { log.warn(`Failed to create file store for URL: ${url}`); return undefined; } - return new FileStoreTxSource(fileStore, url, basePath, log); + return new FileStoreTxSource(fileStore, url, basePath, log, telemetry); } catch (err) { log.warn(`Error creating file store for URL: ${url}`, { error: err }); return undefined; @@ -45,10 +68,15 @@ export class FileStoreTxSource implements TxSource { return Promise.all( txHashes.map(async txHash => { const path = `${this.basePath}/txs/${txHash.toString()}.bin`; + const timer = new Timer(); try { const buffer = await this.fileStore.read(path); + this.downloadsSuccess.add(1); + this.downloadDuration.record(Math.ceil(timer.ms())); + this.downloadSize.record(buffer.length); return Tx.fromBuffer(buffer); } catch { + this.downloadsFailed.add(1); // Tx not found or error reading - return undefined return undefined; } @@ -60,14 +88,17 @@ export class FileStoreTxSource implements TxSource { /** * Creates FileStoreTxSource instances from URLs. * @param urls - Array of file store URLs. + * @param basePath - Base path for tx files within each store. * @param log - Optional logger. + * @param telemetry - Optional telemetry client. * @returns Array of successfully created FileStoreTxSource instances. */ export async function createFileStoreTxSources( urls: string[], basePath: string, log: Logger = createLogger('p2p:file_store_tx_source'), + telemetry: TelemetryClient = getTelemetryClient(), ): Promise { - const sources = await Promise.all(urls.map(url => FileStoreTxSource.create(url, basePath, log))); + const sources = await Promise.all(urls.map(url => FileStoreTxSource.create(url, basePath, log, telemetry))); return sources.filter((s): s is FileStoreTxSource => s !== undefined); } diff --git a/yarn-project/p2p/src/services/tx_collection/instrumentation.ts b/yarn-project/p2p/src/services/tx_collection/instrumentation.ts index 780ee1b043f4..16068bb7045f 100644 --- a/yarn-project/p2p/src/services/tx_collection/instrumentation.ts +++ b/yarn-project/p2p/src/services/tx_collection/instrumentation.ts @@ -18,7 +18,13 @@ export class TxCollectionInstrumentation { const meter = client.getMeter(name); this.txsCollected = createUpDownCounterWithDefault(meter, Metrics.TX_COLLECTOR_COUNT, { - [Attributes.TX_COLLECTION_METHOD]: ['fast-req-resp', 'fast-node-rpc', 'slow-req-resp', 'slow-node-rpc'], + [Attributes.TX_COLLECTION_METHOD]: [ + 'fast-req-resp', + 'fast-node-rpc', + 'slow-req-resp', + 'slow-node-rpc', + 'file-store', + ], }); this.collectionDurationPerTx = meter.createHistogram(Metrics.TX_COLLECTOR_DURATION_PER_TX); diff --git a/yarn-project/telemetry-client/src/metrics.ts b/yarn-project/telemetry-client/src/metrics.ts index 0c8f61d07077..0187114e0d5d 100644 --- a/yarn-project/telemetry-client/src/metrics.ts +++ b/yarn-project/telemetry-client/src/metrics.ts @@ -1402,6 +1402,28 @@ export const TX_FILE_STORE_QUEUE_SIZE: MetricDefinition = { description: 'Number of txs pending upload', valueType: ValueType.INT, }; +export const TX_FILE_STORE_DOWNLOADS_SUCCESS: MetricDefinition = { + name: 'aztec.p2p.tx_file_store.downloads_success', + description: 'Number of successful tx downloads from file storage', + valueType: ValueType.INT, +}; +export const TX_FILE_STORE_DOWNLOADS_FAILED: MetricDefinition = { + name: 'aztec.p2p.tx_file_store.downloads_failed', + description: 'Number of failed tx downloads from file storage', + valueType: ValueType.INT, +}; +export const TX_FILE_STORE_DOWNLOAD_DURATION: MetricDefinition = { + name: 'aztec.p2p.tx_file_store.download_duration', + description: 'Duration to download a tx from file storage', + unit: 'ms', + valueType: ValueType.INT, +}; +export const TX_FILE_STORE_DOWNLOAD_SIZE: MetricDefinition = { + name: 'aztec.p2p.tx_file_store.download_size', + description: 'Size of a downloaded tx from file storage', + unit: 'By', + valueType: ValueType.INT, +}; export const IVC_VERIFIER_TIME: MetricDefinition = { name: 'aztec.ivc_verifier.time',