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