Skip to main content

tempo_payload_builder/
metrics.rs

1use alloy_primitives::{Address, B256, BlockNumber, Bytes, StorageKey, StorageValue};
2use metrics::Gauge;
3use reth_errors::ProviderResult;
4use reth_metrics::{Metrics, metrics::Histogram};
5use reth_primitives_traits::{Account, Bytecode};
6use reth_storage_api::{
7    AccountReader, BlockHashReader, BytecodeReader, HashedPostStateProvider, StateProofProvider,
8    StateProvider, StateRootProvider, StorageRootProvider,
9};
10use reth_trie_common::{
11    AccountProof, HashedPostState, HashedStorage, MultiProof, MultiProofTargets, StorageMultiProof,
12    StorageProof, TrieInput, updates::TrieUpdates,
13};
14use std::time::Instant;
15use tracing::debug_span;
16
17#[derive(Metrics, Clone)]
18#[metrics(scope = "tempo_payload_builder")]
19pub(crate) struct TempoPayloadBuilderMetrics {
20    /// Block time in milliseconds.
21    pub(crate) block_time_millis: Histogram,
22    /// Block time in milliseconds.
23    pub(crate) block_time_millis_last: Gauge,
24    /// Number of transactions in the payload.
25    pub(crate) total_transactions: Histogram,
26    /// Number of transactions in the payload.
27    pub(crate) total_transactions_last: Gauge,
28    /// Number of payment transactions in the payload.
29    pub(crate) payment_transactions: Histogram,
30    /// Number of payment transactions in the payload.
31    pub(crate) payment_transactions_last: Gauge,
32    /// Number of subblocks in the payload.
33    pub(crate) subblocks: Histogram,
34    /// Number of subblocks in the payload.
35    pub(crate) subblocks_last: Gauge,
36    /// Number of subblock transactions in the payload.
37    pub(crate) subblock_transactions: Histogram,
38    /// Number of subblock transactions in the payload.
39    pub(crate) subblock_transactions_last: Gauge,
40    /// Amount of gas used in the payload.
41    pub(crate) gas_used: Histogram,
42    /// Amount of gas used in the payload.
43    pub(crate) gas_used_last: Gauge,
44    /// Gas used by general (non-payment) transactions in the payload.
45    pub(crate) general_gas_used_last: Gauge,
46    /// Gas used by payment transactions in the payload.
47    pub(crate) payment_gas_used_last: Gauge,
48    /// General lane gas limit.
49    pub(crate) general_gas_limit_last: Gauge,
50    /// Payment lane gas limit.
51    pub(crate) payment_gas_limit_last: Gauge,
52    /// Shared (subblock) gas limit.
53    pub(crate) shared_gas_limit_last: Gauge,
54    /// Time to create the pool's `BestTransactions` iterator, including lock acquisition and snapshot.
55    pub(crate) pool_fetch_duration_seconds: Histogram,
56    /// Time to acquire the state provider and initialize the state DB.
57    pub(crate) state_setup_duration_seconds: Histogram,
58    /// The time it took to prepare system transactions in seconds.
59    pub(crate) prepare_system_transactions_duration_seconds: Histogram,
60    /// The time it took to execute one transaction in seconds.
61    pub(crate) transaction_execution_duration_seconds: Histogram,
62    /// The time it took to execute normal transactions in seconds.
63    pub(crate) total_normal_transaction_execution_duration_seconds: Histogram,
64    /// The time it took to execute subblock transactions in seconds.
65    pub(crate) total_subblock_transaction_execution_duration_seconds: Histogram,
66    /// Execution time for a single subblock.
67    pub(crate) subblock_execution_duration_seconds: Histogram,
68    /// Number of transactions in a single subblock.
69    pub(crate) subblock_transaction_count: Histogram,
70    /// The time it took to execute all transactions in seconds.
71    pub(crate) total_transaction_execution_duration_seconds: Histogram,
72    /// The time it took to execute system transactions in seconds.
73    pub(crate) system_transactions_execution_duration_seconds: Histogram,
74    /// The time it took to finalize the payload in seconds. Includes merging transitions and calculating the state root.
75    pub(crate) payload_finalization_duration_seconds: Histogram,
76    /// Total time it took to build the payload in seconds.
77    pub(crate) payload_build_duration_seconds: Histogram,
78    /// Gas per second calculated as gas_used / payload_build_duration.
79    pub(crate) gas_per_second: Histogram,
80    /// Gas per second for the last payload calculated as gas_used / payload_build_duration.
81    pub(crate) gas_per_second_last: Gauge,
82    /// RLP-encoded block size in bytes.
83    pub(crate) rlp_block_size_bytes: Histogram,
84    /// RLP-encoded block size in bytes for the last payload.
85    pub(crate) rlp_block_size_bytes_last: Gauge,
86    /// Time to compute the hashed post-state from the bundle state.
87    pub(crate) hashed_post_state_duration_seconds: Histogram,
88    /// Time to compute the state root and trie updates via `state_root_with_updates`.
89    pub(crate) state_root_with_updates_duration_seconds: Histogram,
90}
91
92impl TempoPayloadBuilderMetrics {
93    /// Increments the unified pool transaction skip counter with the given reason label.
94    ///
95    /// Note: `mark_invalid` may also prune descendant transactions from the iterator,
96    /// so the skip count represents skip *events*, not total transactions removed.
97    #[inline]
98    pub(crate) fn inc_pool_tx_skipped(&self, reason: &'static str) {
99        metrics::counter!("tempo_payload_builder_pool_transactions_skipped_total", "reason" => reason)
100            .increment(1);
101    }
102
103    /// Increments the build failure counter for a given reason.
104    #[inline]
105    pub(crate) fn inc_build_failure(&self, reason: &'static str) {
106        metrics::counter!("tempo_payload_builder_build_failures_total", "reason" => reason)
107            .increment(1);
108    }
109
110    /// Increments the counter for subblocks dropped due to expired transactions.
111    #[inline]
112    pub(crate) fn inc_subblocks_expired(&self) {
113        metrics::counter!("tempo_payload_builder_subblocks_expired_total").increment(1);
114    }
115}
116
117/// Wraps a [`StateProvider`] reference to instrument `hashed_post_state` and
118/// `state_root_with_updates` with tracing spans and histogram metrics during `builder.finish()`.
119pub(crate) struct InstrumentedFinishProvider<'a> {
120    pub(crate) inner: &'a dyn StateProvider,
121    pub(crate) metrics: TempoPayloadBuilderMetrics,
122}
123
124impl<'a> AsRef<dyn StateProvider + 'a> for InstrumentedFinishProvider<'a> {
125    fn as_ref(&self) -> &(dyn StateProvider + 'a) {
126        self.inner
127    }
128}
129
130reth_storage_api::delegate_impls_to_as_ref!(
131    for InstrumentedFinishProvider<'_> =>
132    AccountReader {
133        fn basic_account(&self, address: &Address) -> ProviderResult<Option<Account>>;
134    }
135    BlockHashReader {
136        fn block_hash(&self, number: u64) -> ProviderResult<Option<B256>>;
137        fn canonical_hashes_range(&self, start: BlockNumber, end: BlockNumber) -> ProviderResult<Vec<B256>>;
138    }
139    StateProvider {
140        fn storage(&self, account: Address, storage_key: StorageKey) -> ProviderResult<Option<StorageValue>>;
141    }
142    BytecodeReader {
143        fn bytecode_by_hash(&self, code_hash: &B256) -> ProviderResult<Option<Bytecode>>;
144    }
145    StorageRootProvider {
146        fn storage_root(&self, address: Address, storage: HashedStorage) -> ProviderResult<B256>;
147        fn storage_proof(&self, address: Address, slot: B256, storage: HashedStorage) -> ProviderResult<StorageProof>;
148        fn storage_multiproof(&self, address: Address, slots: &[B256], storage: HashedStorage) -> ProviderResult<StorageMultiProof>;
149    }
150    StateProofProvider {
151        fn proof(&self, input: TrieInput, address: Address, slots: &[B256]) -> ProviderResult<AccountProof>;
152        fn multiproof(&self, input: TrieInput, targets: MultiProofTargets) -> ProviderResult<MultiProof>;
153        fn witness(&self, input: TrieInput, target: HashedPostState) -> ProviderResult<Vec<Bytes>>;
154    }
155);
156
157impl HashedPostStateProvider for InstrumentedFinishProvider<'_> {
158    fn hashed_post_state(&self, bundle_state: &reth_revm::db::BundleState) -> HashedPostState {
159        let start = Instant::now();
160        let _span = debug_span!(target: "payload_builder", "hashed_post_state").entered();
161        let result = self.inner.hashed_post_state(bundle_state);
162        drop(_span);
163        self.metrics
164            .hashed_post_state_duration_seconds
165            .record(start.elapsed());
166        result
167    }
168}
169
170impl StateRootProvider for InstrumentedFinishProvider<'_> {
171    fn state_root(&self, hashed_state: HashedPostState) -> ProviderResult<B256> {
172        let start = Instant::now();
173        let _span = debug_span!(target: "payload_builder", "state_root").entered();
174        let result = self.inner.state_root(hashed_state);
175        drop(_span);
176        self.metrics
177            .state_root_with_updates_duration_seconds
178            .record(start.elapsed());
179        result
180    }
181
182    fn state_root_from_nodes(&self, input: TrieInput) -> ProviderResult<B256> {
183        self.inner.state_root_from_nodes(input)
184    }
185
186    fn state_root_with_updates(
187        &self,
188        hashed_state: HashedPostState,
189    ) -> ProviderResult<(B256, TrieUpdates)> {
190        let start = Instant::now();
191        let _span = debug_span!(target: "payload_builder", "state_root_with_updates").entered();
192        let result = self.inner.state_root_with_updates(hashed_state);
193        drop(_span);
194        self.metrics
195            .state_root_with_updates_duration_seconds
196            .record(start.elapsed());
197        result
198    }
199
200    fn state_root_from_nodes_with_updates(
201        &self,
202        input: TrieInput,
203    ) -> ProviderResult<(B256, TrieUpdates)> {
204        self.inner.state_root_from_nodes_with_updates(input)
205    }
206}