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 execution_histogram: Histogram,
22 pub execution_duration: Gauge,
24
25 pub accounts_loaded_histogram: Histogram,
27 pub storage_slots_loaded_histogram: Histogram,
29 pub bytecodes_loaded_histogram: Histogram,
31
32 pub accounts_updated_histogram: Histogram,
34 pub storage_slots_updated_histogram: Histogram,
36 pub bytecodes_updated_histogram: Histogram,
38}
39
40impl ExecutorMetrics {
41 fn metered<F, R>(&self, f: F) -> R
43 where
44 F: FnOnce() -> (u64, R),
45 {
46 let execute_start = Instant::now();
48 let (gas_used, output) = f();
49 let execution_duration = execute_start.elapsed().as_secs_f64();
50
51 self.gas_processed_total.increment(gas_used);
53 self.gas_per_second.set(gas_used as f64 / execution_duration);
54 self.gas_used_histogram.record(gas_used as f64);
55 self.execution_histogram.record(execution_duration);
56 self.execution_duration.set(execution_duration);
57
58 output
59 }
60
61 pub fn metered_one<F, R, B>(&self, block: &RecoveredBlock<B>, f: F) -> R
67 where
68 F: FnOnce(&RecoveredBlock<B>) -> R,
69 B: Block,
70 B::Header: BlockHeader,
71 {
72 self.metered(|| (block.header().gas_used(), f(block)))
73 }
74}
75
76#[cfg(test)]
77mod tests {
78 use super::*;
79 use alloy_consensus::Header;
80 use alloy_primitives::B256;
81 use reth_ethereum_primitives::Block;
82 use reth_primitives_traits::Block as BlockTrait;
83
84 fn create_test_block_with_gas(gas_used: u64) -> RecoveredBlock<Block> {
85 let header = Header { gas_used, ..Default::default() };
86 let block = Block { header, body: Default::default() };
87 let hash = B256::default();
89 let sealed = block.seal_unchecked(hash);
90 RecoveredBlock::new_sealed(sealed, Default::default())
91 }
92
93 #[test]
94 fn test_metered_one_updates_metrics() {
95 let metrics = ExecutorMetrics::default();
96 let block = create_test_block_with_gas(1000);
97
98 let result = metrics.metered_one(&block, |b| {
100 std::thread::sleep(std::time::Duration::from_millis(10));
102 b.header().gas_used()
103 });
104
105 assert_eq!(result, 1000);
107 }
108
109 #[test]
110 fn test_metered_helper_tracks_timing() {
111 let metrics = ExecutorMetrics::default();
112
113 let result = metrics.metered(|| {
114 std::thread::sleep(std::time::Duration::from_millis(10));
116 (500, "test_result")
117 });
118
119 assert_eq!(result, "test_result");
120 }
121}