From 22ea7e56da042e1e602c39488effb2d8713701f1 Mon Sep 17 00:00:00 2001 From: evalir Date: Tue, 25 Nov 2025 13:29:20 +0100 Subject: [PATCH 1/2] chore(metrics): Refactor current metrics We're not following our set pattern for metrics (see node-components) and it makes it hard to know what we're really tracking and where. --- src/lib.rs | 3 + src/metrics.rs | 209 ++++++++++++++++++++++++++++++++++ src/tasks/block/sim.rs | 10 +- src/tasks/metrics.rs | 13 +-- src/tasks/submit/flashbots.rs | 13 ++- src/tasks/submit/prep.rs | 30 ++++- 6 files changed, 253 insertions(+), 25 deletions(-) create mode 100644 src/metrics.rs diff --git a/src/lib.rs b/src/lib.rs index 322ba68b..6597143f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -18,6 +18,9 @@ mod macros; /// Configuration for the Builder binary. pub mod config; +/// Centralized metrics definitions. +pub mod metrics; + /// Quincey client for signing requests. pub mod quincey; diff --git a/src/metrics.rs b/src/metrics.rs new file mode 100644 index 00000000..7258629a --- /dev/null +++ b/src/metrics.rs @@ -0,0 +1,209 @@ +//! Builder metrics definitions +//! +//! This module centralizes all metric definitions for the builder. +//! +//! ## Counters +//! - Quincey signature requests (success/failure) +//! - Flashbots submissions (success/failure) +//! - Block builds +//! - Transaction outcomes +//! +//! ## Histograms +//! - Quincey signature duration +//! - Nonce fetch duration +//! - Transaction mine time +//! - Block transaction count + +use init4_bin_base::deps::metrics::{ + Counter, Histogram, counter, describe_counter, describe_histogram, histogram, +}; +use std::sync::LazyLock; + +// -- Quincey -- +const QUINCEY_SIGNATURES: &str = "signet.builder.quincey_signatures"; +const QUINCEY_SIGNATURES_HELP: &str = "Number of successful Quincey signature requests"; + +const QUINCEY_SIGNATURE_FAILURES: &str = "signet.builder.quincey_signature_failures"; +const QUINCEY_SIGNATURE_FAILURES_HELP: &str = "Number of failed Quincey signature requests"; + +const QUINCEY_SIGNATURE_DURATION_MS: &str = "signet.builder.quincey_signature_duration_ms"; +const QUINCEY_SIGNATURE_DURATION_MS_HELP: &str = + "Duration of Quincey signature requests in milliseconds"; + +// -- Nonce -- +const NONCE_FETCH_DURATION_MS: &str = "signet.builder.nonce_fetch_duration_ms"; +const NONCE_FETCH_DURATION_MS_HELP: &str = "Duration of nonce fetch requests in milliseconds"; + +// -- Flashbots -- +const FLASHBOTS_BUNDLES_SUBMITTED: &str = "signet.builder.flashbots.bundles_submitted"; +const FLASHBOTS_BUNDLES_SUBMITTED_HELP: &str = + "Number of bundles successfully submitted to Flashbots"; + +const FLASHBOTS_SUBMISSION_FAILURES: &str = "signet.builder.flashbots.submission_failures"; +const FLASHBOTS_SUBMISSION_FAILURES_HELP: &str = "Number of failed Flashbots bundle submissions"; + +const FLASHBOTS_BUNDLE_PREP_FAILURES: &str = "signet.builder.flashbots.bundle_prep_failures"; +const FLASHBOTS_BUNDLE_PREP_FAILURES_HELP: &str = "Number of failed bundle preparations"; + +const FLASHBOTS_EMPTY_BLOCKS: &str = "signet.builder.flashbots.empty_block"; +const FLASHBOTS_EMPTY_BLOCKS_HELP: &str = "Number of empty blocks skipped"; + +const FLASHBOTS_SUBMISSIONS: &str = "signet.builder.flashbots.submissions"; +const FLASHBOTS_SUBMISSIONS_HELP: &str = "Number of submission attempts to Flashbots"; + +// -- Block Building -- +const BUILT_BLOCKS: &str = "signet.builder.built_blocks"; +const BUILT_BLOCKS_HELP: &str = "Number of blocks built by the simulator"; + +const BUILT_BLOCKS_TX_COUNT: &str = "signet.builder.built_blocks.tx_count"; +const BUILT_BLOCKS_TX_COUNT_HELP: &str = "Number of transactions in built blocks"; + +// -- Transaction Outcomes -- +const TX_MINE_TIME_MS: &str = "signet.builder.tx_mine_time_ms"; +const TX_MINE_TIME_MS_HELP: &str = "Time for transaction to be mined in milliseconds"; + +const TX_SUCCEEDED: &str = "signet.builder.tx_succeeded"; +const TX_SUCCEEDED_HELP: &str = "Number of transactions that succeeded"; + +const TX_REVERTED: &str = "signet.builder.tx_reverted"; +const TX_REVERTED_HELP: &str = "Number of transactions that reverted"; + +const TX_NOT_MINED: &str = "signet.builder.tx_not_mined"; +const TX_NOT_MINED_HELP: &str = "Number of transactions that timed out without being mined"; + +const RPC_ERROR: &str = "signet.builder.rpc_error"; +const RPC_ERROR_HELP: &str = "Number of RPC errors encountered"; + +static DESCRIBE: LazyLock<()> = LazyLock::new(|| { + // Quincey + describe_counter!(QUINCEY_SIGNATURES, QUINCEY_SIGNATURES_HELP); + describe_counter!(QUINCEY_SIGNATURE_FAILURES, QUINCEY_SIGNATURE_FAILURES_HELP); + describe_histogram!(QUINCEY_SIGNATURE_DURATION_MS, QUINCEY_SIGNATURE_DURATION_MS_HELP); + + // Nonce + describe_histogram!(NONCE_FETCH_DURATION_MS, NONCE_FETCH_DURATION_MS_HELP); + + // Flashbots + describe_counter!(FLASHBOTS_BUNDLES_SUBMITTED, FLASHBOTS_BUNDLES_SUBMITTED_HELP); + describe_counter!(FLASHBOTS_SUBMISSION_FAILURES, FLASHBOTS_SUBMISSION_FAILURES_HELP); + describe_counter!(FLASHBOTS_BUNDLE_PREP_FAILURES, FLASHBOTS_BUNDLE_PREP_FAILURES_HELP); + describe_counter!(FLASHBOTS_EMPTY_BLOCKS, FLASHBOTS_EMPTY_BLOCKS_HELP); + describe_counter!(FLASHBOTS_SUBMISSIONS, FLASHBOTS_SUBMISSIONS_HELP); + + // Block building + describe_counter!(BUILT_BLOCKS, BUILT_BLOCKS_HELP); + describe_histogram!(BUILT_BLOCKS_TX_COUNT, BUILT_BLOCKS_TX_COUNT_HELP); + + // Transaction outcomes + describe_histogram!(TX_MINE_TIME_MS, TX_MINE_TIME_MS_HELP); + describe_counter!(TX_SUCCEEDED, TX_SUCCEEDED_HELP); + describe_counter!(TX_REVERTED, TX_REVERTED_HELP); + describe_counter!(TX_NOT_MINED, TX_NOT_MINED_HELP); + describe_counter!(RPC_ERROR, RPC_ERROR_HELP); +}); + +// -- Quincey -- + +/// Counter for successful Quincey signature requests. +pub fn quincey_signatures() -> Counter { + LazyLock::force(&DESCRIBE); + counter!(QUINCEY_SIGNATURES) +} + +/// Counter for failed Quincey signature requests. +pub fn quincey_signature_failures() -> Counter { + LazyLock::force(&DESCRIBE); + counter!(QUINCEY_SIGNATURE_FAILURES) +} + +/// Histogram for Quincey signature request duration in milliseconds. +pub fn quincey_signature_duration_ms() -> Histogram { + LazyLock::force(&DESCRIBE); + histogram!(QUINCEY_SIGNATURE_DURATION_MS) +} + +// -- Nonce -- + +/// Histogram for nonce fetch duration in milliseconds. +pub fn nonce_fetch_duration_ms() -> Histogram { + LazyLock::force(&DESCRIBE); + histogram!(NONCE_FETCH_DURATION_MS) +} + +// -- Flashbots -- + +/// Counter for bundles successfully submitted to Flashbots. +pub fn flashbots_bundles_submitted() -> Counter { + LazyLock::force(&DESCRIBE); + counter!(FLASHBOTS_BUNDLES_SUBMITTED) +} + +/// Counter for failed Flashbots bundle submissions. +pub fn flashbots_submission_failures() -> Counter { + LazyLock::force(&DESCRIBE); + counter!(FLASHBOTS_SUBMISSION_FAILURES) +} + +/// Counter for failed bundle preparations. +pub fn flashbots_bundle_prep_failures() -> Counter { + LazyLock::force(&DESCRIBE); + counter!(FLASHBOTS_BUNDLE_PREP_FAILURES) +} + +/// Counter for empty blocks that were skipped. +pub fn flashbots_empty_blocks() -> Counter { + LazyLock::force(&DESCRIBE); + counter!(FLASHBOTS_EMPTY_BLOCKS) +} + +/// Counter for submission attempts to Flashbots. +pub fn flashbots_submissions() -> Counter { + LazyLock::force(&DESCRIBE); + counter!(FLASHBOTS_SUBMISSIONS) +} + +// -- Block Building -- + +/// Counter for blocks built by the simulator. +pub fn built_blocks() -> Counter { + LazyLock::force(&DESCRIBE); + counter!(BUILT_BLOCKS) +} + +/// Histogram for transaction count in built blocks. +pub fn built_blocks_tx_count() -> Histogram { + LazyLock::force(&DESCRIBE); + histogram!(BUILT_BLOCKS_TX_COUNT) +} + +// -- Transaction Outcomes -- + +/// Histogram for transaction mine time in milliseconds. +pub fn tx_mine_time_ms() -> Histogram { + LazyLock::force(&DESCRIBE); + histogram!(TX_MINE_TIME_MS) +} + +/// Counter for transactions that succeeded. +pub fn tx_succeeded() -> Counter { + LazyLock::force(&DESCRIBE); + counter!(TX_SUCCEEDED) +} + +/// Counter for transactions that reverted. +pub fn tx_reverted() -> Counter { + LazyLock::force(&DESCRIBE); + counter!(TX_REVERTED) +} + +/// Counter for transactions that timed out without being mined. +pub fn tx_not_mined() -> Counter { + LazyLock::force(&DESCRIBE); + counter!(TX_NOT_MINED) +} + +/// Counter for RPC errors encountered. +pub fn rpc_error() -> Counter { + LazyLock::force(&DESCRIBE); + counter!(RPC_ERROR) +} diff --git a/src/tasks/block/sim.rs b/src/tasks/block/sim.rs index a2945380..da9fdc91 100644 --- a/src/tasks/block/sim.rs +++ b/src/tasks/block/sim.rs @@ -3,13 +3,11 @@ //! and turns them into valid Pecorino blocks for network submission. use crate::{ config::{BuilderConfig, HostProvider, RuProvider}, + metrics, tasks::env::SimEnv, }; use alloy::consensus::Header; -use init4_bin_base::{ - deps::metrics::{counter, histogram}, - utils::calc::SlotCalculator, -}; +use init4_bin_base::utils::calc::SlotCalculator; use signet_sim::{BlockBuild, BuiltBlock, SimCache}; use signet_types::constants::SignetSystemConstants; use std::time::{Duration, Instant}; @@ -158,8 +156,8 @@ impl Simulator { block_number = built_block.block_number(), "block simulation completed", ); - counter!("signet.builder.built_blocks").increment(1); - histogram!("signet.builder.built_blocks.tx_count").record(built_block.tx_count() as u32); + metrics::built_blocks().increment(1); + metrics::built_blocks_tx_count().record(built_block.tx_count() as u32); Ok(built_block) } diff --git a/src/tasks/metrics.rs b/src/tasks/metrics.rs index a282354b..831bd284 100644 --- a/src/tasks/metrics.rs +++ b/src/tasks/metrics.rs @@ -1,9 +1,8 @@ -use crate::config::HostProvider; +use crate::{config::HostProvider, metrics}; use alloy::{ primitives::TxHash, providers::{PendingTransactionBuilder, PendingTransactionError, Provider as _, WatchTxError}, }; -use init4_bin_base::deps::metrics::{counter, histogram}; use std::time::{Duration, Instant}; use tokio::{sync::mpsc, task::JoinHandle}; use tracing::{Instrument, debug, error, info_span}; @@ -47,24 +46,24 @@ impl MetricsTask { Ok(receipt) => { // record how long it took to mine the transaction // potential improvement: use the block timestamp to calculate the time elapsed - histogram!("metrics.tx_mine_time").record(start.elapsed().as_millis() as f64); + metrics::tx_mine_time_ms().record(start.elapsed().as_millis() as f64); // log whether the transaction reverted if receipt.status() { - counter!("metrics.tx_succeeded").increment(1); + metrics::tx_succeeded().increment(1); debug!("tx succeeded"); } else { - counter!("metrics.tx_reverted").increment(1); + metrics::tx_reverted().increment(1); debug!("tx reverted"); } } Err(PendingTransactionError::TxWatcher(WatchTxError::Timeout)) => { // log that the transaction timed out - counter!("metrics.tx_not_mined").increment(1); + metrics::tx_not_mined().increment(1); debug!("tx not mined"); } Err(e) => { - counter!("metrics.rpc_error").increment(1); + metrics::rpc_error().increment(1); error!(error = ?e, "rpc error"); } } diff --git a/src/tasks/submit/flashbots.rs b/src/tasks/submit/flashbots.rs index 11e5e6de..01bbe328 100644 --- a/src/tasks/submit/flashbots.rs +++ b/src/tasks/submit/flashbots.rs @@ -2,6 +2,7 @@ //! submits them to the Flashbots relay as bundles. use crate::{ config::{BuilderConfig, FlashbotsProvider, HostProvider, ZenithInstance}, + metrics, quincey::Quincey, tasks::{block::sim::SimResult, submit::SubmitPrep}, }; @@ -12,7 +13,7 @@ use alloy::{ rpc::types::mev::{BundleItem, MevSendBundle, ProtocolVersion}, }; use eyre::OptionExt; -use init4_bin_base::{deps::metrics::counter, utils::signer::LocalOrAws}; +use init4_bin_base::utils::signer::LocalOrAws; use tokio::{sync::mpsc, task::JoinHandle}; use tracing::{Instrument, debug, debug_span}; @@ -120,7 +121,7 @@ impl FlashbotsTask { /// Sends the transaction hash to the outbound channel for monitoring. /// Logs a debug message if the channel is closed. fn track_outbound_tx(&self, envelope: &alloy::consensus::TxEnvelope) { - counter!("signet.builder.flashbots.").increment(1); + metrics::flashbots_submissions().increment(1); let hash = *envelope.tx_hash(); if self.outbound.send(hash).is_err() { debug!("outbound channel closed, could not track tx hash"); @@ -166,7 +167,7 @@ impl FlashbotsTask { // Don't submit empty blocks if sim_result.block.is_empty() { - counter!("signet.builder.flashbots.empty_block").increment(1); + metrics::flashbots_empty_blocks().increment(1); span_debug!(span, "received empty block - skipping"); continue; } @@ -175,7 +176,7 @@ impl FlashbotsTask { // Prepare a MEV bundle with the configured call type from the sim result let result = self.prepare(&sim_result).instrument(span.clone()).await.inspect_err(|error| { - counter!("signet.builder.flashbots.bundle_prep_failures").increment(1); + metrics::flashbots_bundle_prep_failures().increment(1); span_debug!(span, %error, "bundle preparation failed"); }); let bundle = match result { @@ -204,7 +205,7 @@ impl FlashbotsTask { match response { Ok(resp) => { - counter!("signet.builder.flashbots.bundles_submitted").increment(1); + metrics::flashbots_bundles_submitted().increment(1); span_debug!( submit_span, hash = resp.map(|r| r.bundle_hash.to_string()), @@ -212,7 +213,7 @@ impl FlashbotsTask { ); } Err(err) => { - counter!("signet.builder.flashbots.submission_failures").increment(1); + metrics::flashbots_submission_failures().increment(1); span_error!(submit_span, %err, "MEV bundle submission failed - error returned"); } } diff --git a/src/tasks/submit/prep.rs b/src/tasks/submit/prep.rs index daf807f7..96f822e7 100644 --- a/src/tasks/submit/prep.rs +++ b/src/tasks/submit/prep.rs @@ -1,5 +1,6 @@ use crate::{ config::{BuilderConfig, HostProvider}, + metrics, quincey::Quincey, utils, }; @@ -11,11 +12,11 @@ use alloy::{ rpc::types::TransactionRequest, sol_types::SolCall, }; -use init4_bin_base::deps::metrics::counter; use signet_sim::BuiltBlock; use signet_types::{SignRequest, SignResponse}; use signet_zenith::Zenith; -use tracing::{Instrument, debug}; +use std::time::Instant; +use tracing::{Instrument, debug, debug_span}; /// Preparation logic for transactions issued to the host chain by the /// [`SubmitTask`]. @@ -76,12 +77,20 @@ impl<'a> SubmitPrep<'a> { self.quincey_resp .get_or_try_init(|| async { let sig_request = self.sig_request(); + let start = Instant::now(); self.quincey .get_signature(sig_request) + .instrument(debug_span!("quincey_signature")) .await - .inspect(|_| counter!("signet.builder.quincey_signatures").increment(1)) + .inspect(|_| { + metrics::quincey_signature_duration_ms() + .record(start.elapsed().as_millis() as f64); + metrics::quincey_signatures().increment(1); + }) .inspect_err(|_| { - counter!("signet.builder.quincey_signature_failures").increment(1) + metrics::quincey_signature_duration_ms() + .record(start.elapsed().as_millis() as f64); + metrics::quincey_signature_failures().increment(1); }) }) .await @@ -113,8 +122,17 @@ impl<'a> SubmitPrep<'a> { } async fn new_tx_request(&self) -> eyre::Result { - let nonce = - self.provider.get_transaction_count(self.provider.default_signer_address()).await?; + let nonce = { + let start = Instant::now(); + let nonce = self + .provider + .get_transaction_count(self.provider.default_signer_address()) + .into_future() + .instrument(debug_span!("nonce_fetch")) + .await?; + metrics::nonce_fetch_duration_ms().record(start.elapsed().as_millis() as f64); + nonce + }; debug!(nonce, "assigned nonce to rollup block transaction"); From ef767a9c16a66c7ccf72acb896d9df3886ee8fdc Mon Sep 17 00:00:00 2001 From: evalir Date: Tue, 25 Nov 2025 13:34:34 +0100 Subject: [PATCH 2/2] feat: track flashbots submission duration --- src/metrics.rs | 11 +++++++++++ src/tasks/submit/flashbots.rs | 5 +++++ 2 files changed, 16 insertions(+) diff --git a/src/metrics.rs b/src/metrics.rs index 7258629a..9e62cd39 100644 --- a/src/metrics.rs +++ b/src/metrics.rs @@ -51,6 +51,10 @@ const FLASHBOTS_EMPTY_BLOCKS_HELP: &str = "Number of empty blocks skipped"; const FLASHBOTS_SUBMISSIONS: &str = "signet.builder.flashbots.submissions"; const FLASHBOTS_SUBMISSIONS_HELP: &str = "Number of submission attempts to Flashbots"; +const FLASHBOTS_SUBMISSION_DURATION_MS: &str = "signet.builder.flashbots.submission_duration_ms"; +const FLASHBOTS_SUBMISSION_DURATION_MS_HELP: &str = + "Duration of Flashbots bundle submission requests in milliseconds"; + // -- Block Building -- const BUILT_BLOCKS: &str = "signet.builder.built_blocks"; const BUILT_BLOCKS_HELP: &str = "Number of blocks built by the simulator"; @@ -89,6 +93,7 @@ static DESCRIBE: LazyLock<()> = LazyLock::new(|| { describe_counter!(FLASHBOTS_BUNDLE_PREP_FAILURES, FLASHBOTS_BUNDLE_PREP_FAILURES_HELP); describe_counter!(FLASHBOTS_EMPTY_BLOCKS, FLASHBOTS_EMPTY_BLOCKS_HELP); describe_counter!(FLASHBOTS_SUBMISSIONS, FLASHBOTS_SUBMISSIONS_HELP); + describe_histogram!(FLASHBOTS_SUBMISSION_DURATION_MS, FLASHBOTS_SUBMISSION_DURATION_MS_HELP); // Block building describe_counter!(BUILT_BLOCKS, BUILT_BLOCKS_HELP); @@ -162,6 +167,12 @@ pub fn flashbots_submissions() -> Counter { counter!(FLASHBOTS_SUBMISSIONS) } +/// Histogram for Flashbots bundle submission duration in milliseconds. +pub fn flashbots_submission_duration_ms() -> Histogram { + LazyLock::force(&DESCRIBE); + histogram!(FLASHBOTS_SUBMISSION_DURATION_MS) +} + // -- Block Building -- /// Counter for blocks built by the simulator. diff --git a/src/tasks/submit/flashbots.rs b/src/tasks/submit/flashbots.rs index 01bbe328..a5ec3680 100644 --- a/src/tasks/submit/flashbots.rs +++ b/src/tasks/submit/flashbots.rs @@ -14,6 +14,7 @@ use alloy::{ }; use eyre::OptionExt; use init4_bin_base::utils::signer::LocalOrAws; +use std::time::Instant; use tokio::{sync::mpsc, task::JoinHandle}; use tracing::{Instrument, debug, debug_span}; @@ -196,6 +197,7 @@ impl FlashbotsTask { let signer = self.signer.clone(); tokio::spawn(async move { + let start = Instant::now(); let response = flashbots .send_mev_bundle(bundle.clone()) .with_auth(signer.clone()) @@ -203,6 +205,9 @@ impl FlashbotsTask { .instrument(submit_span.clone()) .await; + metrics::flashbots_submission_duration_ms() + .record(start.elapsed().as_millis() as f64); + match response { Ok(resp) => { metrics::flashbots_bundles_submitted().increment(1);