diff --git a/crates/op-rbuilder/src/builder/generator.rs b/crates/op-rbuilder/src/builder/generator.rs index 910a8de5..74d2f26a 100644 --- a/crates/op-rbuilder/src/builder/generator.rs +++ b/crates/op-rbuilder/src/builder/generator.rs @@ -81,6 +81,10 @@ pub(super) struct BlockPayloadJobGenerator { extra_block_deadline: std::time::Duration, /// Stored `cached_reads` for new payload jobs. pre_cached: Option, + /// The configured block time + block_time: std::time::Duration, + /// Metrics for recording telemetry + metrics: Arc, } // === impl EmptyBlockPayloadJobGenerator === @@ -88,6 +92,7 @@ pub(super) struct BlockPayloadJobGenerator { impl BlockPayloadJobGenerator { /// Creates a new [EmptyBlockPayloadJobGenerator] with the given config and custom /// [PayloadBuilder] + #[expect(clippy::too_many_arguments)] pub(super) fn with_builder( client: Client, executor: Tasks, @@ -95,6 +100,8 @@ impl BlockPayloadJobGenerator { builder: Builder, ensure_only_one_payload: bool, extra_block_deadline: std::time::Duration, + block_time: std::time::Duration, + metrics: Arc, ) -> Self { Self { client, @@ -105,6 +112,8 @@ impl BlockPayloadJobGenerator { last_payload: Arc::new(Mutex::new(CancellationToken::new())), extra_block_deadline, pre_cached: None, + block_time, + metrics, } } @@ -139,6 +148,21 @@ where &self, attributes: ::Attributes, ) -> Result { + // Calculate and record FCU arrival delay metric in milliseconds + // Expected: FCU should arrive at (payload_timestamp - block_time) + // Positive delay = FCU arrived late, Negative = FCU arrived early + let timestamp = attributes.timestamp(); + let now = SystemTime::now(); + let expected_fcu_arrival = + SystemTime::UNIX_EPOCH + Duration::from_secs(timestamp) - self.block_time; + let fcu_arrival_delay_ms = now + .duration_since(expected_fcu_arrival) + .map(|d| d.as_millis() as i64) + .unwrap_or_else(|e| -(e.duration().as_millis() as i64)); + self.metrics + .fcu_arrival_delay + .record(fcu_arrival_delay_ms as f64); + let cancel_token = if self.ensure_only_one_payload { // Cancel existing payload { @@ -168,7 +192,10 @@ where .ok_or_else(|| PayloadBuilderError::MissingParentBlock(attributes.parent()))? }; - info!("Spawn block building job"); + info!( + target: "payload_builder", + id = %attributes.payload_id(), + "Spawn block building job"); // The deadline is critical for payload availability. If we reach the deadline, // the payload job stops and cannot be queried again. With tight deadlines close @@ -683,6 +710,8 @@ mod tests { builder.clone(), false, std::time::Duration::from_secs(1), + std::time::Duration::from_secs(2), + Arc::new(crate::metrics::OpRBuilderMetrics::default()), ); // this is not nice but necessary diff --git a/crates/op-rbuilder/src/builder/payload.rs b/crates/op-rbuilder/src/builder/payload.rs index 26e8b27b..6c92087e 100644 --- a/crates/op-rbuilder/src/builder/payload.rs +++ b/crates/op-rbuilder/src/builder/payload.rs @@ -528,12 +528,12 @@ where } // We adjust our flashblocks timings based on time the fcu block building signal arrived - let timestamp = config.attributes.timestamp(); let flashblock_scheduler = FlashblockScheduler::new( &self.config.flashblocks_config, self.config.block_time, - timestamp, + config.attributes.timestamp(), ); + info!( target: "payload_builder", id = %fb_payload.payload_id, diff --git a/crates/op-rbuilder/src/builder/service.rs b/crates/op-rbuilder/src/builder/service.rs index 673a96bd..75a2a132 100644 --- a/crates/op-rbuilder/src/builder/service.rs +++ b/crates/op-rbuilder/src/builder/service.rs @@ -138,6 +138,8 @@ impl FlashblocksServiceBuilder { payload_builder, true, self.0.block_time_leeway, + self.0.block_time, + metrics.clone(), ); let (payload_service, payload_builder_handle) = diff --git a/crates/op-rbuilder/src/metrics.rs b/crates/op-rbuilder/src/metrics.rs index d89addfa..c8ffbd1c 100644 --- a/crates/op-rbuilder/src/metrics.rs +++ b/crates/op-rbuilder/src/metrics.rs @@ -151,6 +151,8 @@ pub struct OpRBuilderMetrics { pub flashblocks_time_drift: Histogram, /// Time offset we used for first flashblock pub first_flashblock_time_offset: Histogram, + /// Delay in milliseconds between expected and actual FCU arrival time (positive = late, negative = early) + pub fcu_arrival_delay: Histogram, /// Number of requests sent to the eth_sendBundle endpoint pub bundle_requests: Counter, /// Number of valid bundles received at the eth_sendBundle endpoint