1use alloy_consensus::BlockHeader;
3use metrics::{Counter, Gauge, Histogram};
4use reth_metrics::Metrics;
5use reth_primitives_traits::{Block, FastInstant as Instant, RecoveredBlock};
6
7#[derive(Metrics, Clone)]
9#[metrics(scope = "sync.execution")]
10pub struct ExecutorMetrics {
11 pub gas_processed_total: Counter,
13 pub gas_per_second: Gauge,
15 pub gas_used_histogram: Histogram,
17
18 pub pre_execution_histogram: Histogram,
20 pub transaction_wait_histogram: Histogram,
22 pub transaction_execution_histogram: Histogram,
24 pub post_execution_histogram: Histogram,
26 pub execution_histogram: Histogram,
28 pub execution_duration: Gauge,
30
31 pub accounts_updated_histogram: Histogram,
33 pub storage_slots_updated_histogram: Histogram,
35 pub bytecodes_updated_histogram: Histogram,
37}
38
39impl ExecutorMetrics {
40 fn metered<F, R>(&self, f: F) -> R
42 where
43 F: FnOnce() -> (u64, R),
44 {
45 let execute_start = Instant::now();
47 let (gas_used, output) = f();
48 let execution_duration = execute_start.elapsed().as_secs_f64();
49
50 self.gas_processed_total.increment(gas_used);
52 self.gas_per_second.set(gas_used as f64 / execution_duration);
53 self.gas_used_histogram.record(gas_used as f64);
54 self.execution_histogram.record(execution_duration);
55 self.execution_duration.set(execution_duration);
56
57 output
58 }
59
60 pub fn metered_one<F, R, B>(&self, block: &RecoveredBlock<B>, f: F) -> R
66 where
67 F: FnOnce(&RecoveredBlock<B>) -> R,
68 B: Block,
69 B::Header: BlockHeader,
70 {
71 self.metered(|| (block.header().gas_used(), f(block)))
72 }
73}
74
75#[cfg(test)]
76mod tests {
77 use super::*;
78 use alloy_consensus::Header;
79 use alloy_primitives::B256;
80 use reth_ethereum_primitives::Block;
81 use reth_primitives_traits::Block as BlockTrait;
82
83 fn create_test_block_with_gas(gas_used: u64) -> RecoveredBlock<Block> {
84 let header = Header { gas_used, ..Default::default() };
85 let block = Block { header, body: Default::default() };
86 let hash = B256::default();
88 let sealed = block.seal_unchecked(hash);
89 RecoveredBlock::new_sealed(sealed, Default::default())
90 }
91
92 #[test]
93 fn test_metered_one_updates_metrics() {
94 let metrics = ExecutorMetrics::default();
95 let block = create_test_block_with_gas(1000);
96
97 let result = metrics.metered_one(&block, |b| {
99 std::thread::sleep(std::time::Duration::from_millis(10));
101 b.header().gas_used()
102 });
103
104 assert_eq!(result, 1000);
106 }
107
108 #[test]
109 fn test_metered_helper_tracks_timing() {
110 let metrics = ExecutorMetrics::default();
111
112 let result = metrics.metered(|| {
113 std::thread::sleep(std::time::Duration::from_millis(10));
115 (500, "test_result")
116 });
117
118 assert_eq!(result, "test_result");
119 }
120}