reth_network/
metrics.rs

1use metrics::Histogram;
2use reth_eth_wire::DisconnectReason;
3use reth_ethereum_primitives::TxType;
4use reth_metrics::{
5    metrics::{Counter, Gauge},
6    Metrics,
7};
8
9/// Scope for monitoring transactions sent from the manager to the tx manager
10pub(crate) const NETWORK_POOL_TRANSACTIONS_SCOPE: &str = "network.pool.transactions";
11
12/// Metrics for the entire network, handled by `NetworkManager`
13#[derive(Metrics)]
14#[metrics(scope = "network")]
15pub struct NetworkMetrics {
16    /// Number of currently connected peers
17    pub(crate) connected_peers: Gauge,
18
19    /// Number of currently backed off peers
20    pub(crate) backed_off_peers: Gauge,
21
22    /// Number of peers known to the node
23    pub(crate) tracked_peers: Gauge,
24
25    /// Cumulative number of failures of pending sessions
26    pub(crate) pending_session_failures: Counter,
27
28    /// Total number of sessions closed
29    pub(crate) closed_sessions: Counter,
30
31    /// Number of active incoming connections
32    pub(crate) incoming_connections: Gauge,
33
34    /// Number of active outgoing connections
35    pub(crate) outgoing_connections: Gauge,
36
37    /// Number of currently pending outgoing connections
38    pub(crate) pending_outgoing_connections: Gauge,
39
40    /// Total number of pending connections, incoming and outgoing.
41    pub(crate) total_pending_connections: Gauge,
42
43    /// Total Number of incoming connections handled
44    pub(crate) total_incoming_connections: Counter,
45
46    /// Total Number of outgoing connections established
47    pub(crate) total_outgoing_connections: Counter,
48
49    /// Number of invalid/malformed messages received from peers
50    pub(crate) invalid_messages_received: Counter,
51
52    /// Number of Eth Requests dropped due to channel being at full capacity
53    pub(crate) total_dropped_eth_requests_at_full_capacity: Counter,
54
55    /* ================ POLL DURATION ================ */
56
57    /* -- Total poll duration of `NetworksManager` future -- */
58    /// Duration in seconds of call to
59    /// [`NetworkManager`](crate::NetworkManager)'s poll function.
60    ///
61    /// True duration of this call, should be sum of the accumulated durations of calling nested
62    // items.
63    pub(crate) duration_poll_network_manager: Gauge,
64
65    /* -- Poll duration of items nested in `NetworkManager` future -- */
66    /// Time spent streaming messages sent over the [`NetworkHandle`](crate::NetworkHandle), which
67    /// can be cloned and shared via [`NetworkManager::handle`](crate::NetworkManager::handle), in
68    /// one call to poll the [`NetworkManager`](crate::NetworkManager) future. At least
69    /// [`TransactionsManager`](crate::transactions::TransactionsManager) holds this handle.
70    ///
71    /// Duration in seconds.
72    pub(crate) acc_duration_poll_network_handle: Gauge,
73    /// Time spent polling [`Swarm`](crate::swarm::Swarm), in one call to poll the
74    /// [`NetworkManager`](crate::NetworkManager) future.
75    ///
76    /// Duration in seconds.
77    pub(crate) acc_duration_poll_swarm: Gauge,
78}
79
80/// Metrics for `SessionManager`
81#[derive(Metrics)]
82#[metrics(scope = "network")]
83pub struct SessionManagerMetrics {
84    /// Number of successful outgoing dial attempts.
85    pub(crate) total_dial_successes: Counter,
86    /// Number of dropped outgoing peer messages.
87    pub(crate) total_outgoing_peer_messages_dropped: Counter,
88    /// Number of queued outgoing messages
89    pub(crate) queued_outgoing_messages: Gauge,
90}
91
92/// Metrics for the [`TransactionsManager`](crate::transactions::TransactionsManager).
93#[derive(Metrics)]
94#[metrics(scope = "network")]
95pub struct TransactionsManagerMetrics {
96    /* ================ BROADCAST ================ */
97    /// Total number of propagated transactions
98    pub(crate) propagated_transactions: Counter,
99    /// Total number of reported bad transactions
100    pub(crate) reported_bad_transactions: Counter,
101
102    /* -- Freq txns already marked as seen by peer -- */
103    /// Total number of messages from a peer, announcing transactions that have already been
104    /// marked as seen by that peer.
105    pub(crate) messages_with_hashes_already_seen_by_peer: Counter,
106    /// Total number of messages from a peer, with transaction that have already been marked as
107    /// seen by that peer.
108    pub(crate) messages_with_transactions_already_seen_by_peer: Counter,
109    /// Total number of occurrences, of a peer announcing a transaction that has already been
110    /// marked as seen by that peer.
111    pub(crate) occurrences_hash_already_seen_by_peer: Counter,
112    /// Total number of times a transaction is seen from a peer, that has already been marked as
113    /// seen by that peer.
114    pub(crate) occurrences_of_transaction_already_seen_by_peer: Counter,
115
116    /* -- Freq txns already in pool -- */
117    /// Total number of times a hash is announced that is already in the local pool.
118    pub(crate) occurrences_hashes_already_in_pool: Counter,
119    /// Total number of times a transaction is sent that is already in the local pool.
120    pub(crate) occurrences_transactions_already_in_pool: Counter,
121
122    /* ================ POOL IMPORTS ================ */
123    /// Number of transactions about to be imported into the pool.
124    pub(crate) pending_pool_imports: Gauge,
125    /// Total number of bad imports, imports that fail because the transaction is badly formed
126    /// (i.e. have no chance of passing validation, unlike imports that fail due to e.g. nonce
127    /// gaps).
128    pub(crate) bad_imports: Counter,
129    /// Number of inflight requests at which the
130    /// [`TransactionPool`](reth_transaction_pool::TransactionPool) is considered to be at
131    /// capacity. Note, this is not a limit to the number of inflight requests, but a health
132    /// measure.
133    pub(crate) capacity_pending_pool_imports: Counter,
134    /// The time it took to prepare transactions for import. This is mostly sender recovery.
135    pub(crate) pool_import_prepare_duration: Histogram,
136
137    /* ================ POLL DURATION ================ */
138
139    /* -- Total poll duration of `TransactionsManager` future -- */
140    /// Duration in seconds of call to
141    /// [`TransactionsManager`](crate::transactions::TransactionsManager)'s poll function.
142    ///
143    /// Updating metrics could take time, so the true duration of this call could
144    /// be longer than the sum of the accumulated durations of polling nested items.
145    pub(crate) duration_poll_tx_manager: Gauge,
146
147    /* -- Poll duration of items nested in `TransactionsManager` future -- */
148    /// Accumulated time spent streaming session updates and updating peers accordingly, in
149    /// one call to poll the [`TransactionsManager`](crate::transactions::TransactionsManager)
150    /// future.
151    ///
152    /// Duration in seconds.
153    pub(crate) acc_duration_poll_network_events: Gauge,
154    /// Accumulated time spent flushing the queue of batched pending pool imports into pool, in
155    /// one call to poll the [`TransactionsManager`](crate::transactions::TransactionsManager)
156    /// future.
157    ///
158    /// Duration in seconds.
159    pub(crate) acc_duration_poll_pending_pool_imports: Gauge,
160    /// Accumulated time spent streaming transaction and announcement broadcast, queueing for
161    /// pool import or requesting respectively, in one call to poll the
162    /// [`TransactionsManager`](crate::transactions::TransactionsManager) future.
163    ///
164    /// Duration in seconds.
165    pub(crate) acc_duration_poll_transaction_events: Gauge,
166    /// Accumulated time spent streaming fetch events, queueing for pool import on successful
167    /// fetch, in one call to poll the
168    /// [`TransactionsManager`](crate::transactions::TransactionsManager) future.
169    ///
170    /// Duration in seconds.
171    pub(crate) acc_duration_poll_fetch_events: Gauge,
172    /// Accumulated time spent streaming and propagating transactions that were successfully
173    /// imported into the pool, in one call to poll the
174    /// [`TransactionsManager`](crate::transactions::TransactionsManager) future.
175    ///
176    /// Duration in seconds.
177    pub(crate) acc_duration_poll_imported_transactions: Gauge,
178    /// Accumulated time spent assembling and sending requests for hashes fetching pending, in
179    /// one call to poll the [`TransactionsManager`](crate::transactions::TransactionsManager)
180    /// future.
181    ///
182    /// Duration in seconds.
183    pub(crate) acc_duration_fetch_pending_hashes: Gauge,
184    /// Accumulated time spent streaming commands and propagating, fetching and serving
185    /// transactions accordingly, in one call to poll the
186    /// [`TransactionsManager`](crate::transactions::TransactionsManager) future.
187    ///
188    /// Duration in seconds.
189    pub(crate) acc_duration_poll_commands: Gauge,
190}
191
192/// Metrics for the [`TransactionsManager`](crate::transactions::TransactionsManager).
193#[derive(Metrics)]
194#[metrics(scope = "network")]
195pub struct TransactionFetcherMetrics {
196    /// Currently active outgoing [`GetPooledTransactions`](reth_eth_wire::GetPooledTransactions)
197    /// requests.
198    pub(crate) inflight_transaction_requests: Gauge,
199    /// Number of inflight requests at which the
200    /// [`TransactionFetcher`](crate::transactions::TransactionFetcher) is considered to be at
201    /// capacity. Note, this is not a limit to the number of inflight requests, but a health
202    /// measure.
203    pub(crate) capacity_inflight_requests: Counter,
204    /// Hashes in currently active outgoing
205    /// [`GetPooledTransactions`](reth_eth_wire::GetPooledTransactions) requests.
206    pub(crate) hashes_inflight_transaction_requests: Gauge,
207    /// How often we failed to send a request to the peer because the channel was full.
208    pub(crate) egress_peer_channel_full: Counter,
209    /// Total number of hashes pending fetch.
210    pub(crate) hashes_pending_fetch: Gauge,
211    /// Total number of fetched transactions.
212    pub(crate) fetched_transactions: Counter,
213    /// Total number of transactions that were received in
214    /// [`PooledTransactions`](reth_eth_wire::PooledTransactions) responses, that weren't
215    /// requested.
216    pub(crate) unsolicited_transactions: Counter,
217    /* ================ SEARCH DURATION ================ */
218    /// Time spent searching for an idle peer in call to
219    /// [`TransactionFetcher::find_any_idle_fallback_peer_for_any_pending_hash`](crate::transactions::TransactionFetcher::find_any_idle_fallback_peer_for_any_pending_hash).
220    ///
221    /// Duration in seconds.
222    pub(crate) duration_find_idle_fallback_peer_for_any_pending_hash: Gauge,
223
224    /// Time spent searching for hashes pending fetch, announced by a given peer in
225    /// [`TransactionFetcher::fill_request_from_hashes_pending_fetch`](crate::transactions::TransactionFetcher::fill_request_from_hashes_pending_fetch).
226    ///
227    /// Duration in seconds.
228    pub(crate) duration_fill_request_from_hashes_pending_fetch: Gauge,
229}
230
231/// Measures the duration of executing the given code block. The duration is added to the given
232/// accumulator value passed as a mutable reference.
233#[macro_export]
234macro_rules! duration_metered_exec {
235    ($code:expr, $acc:expr) => {{
236        let start = std::time::Instant::now();
237
238        let res = $code;
239
240        $acc += start.elapsed();
241
242        res
243    }};
244}
245
246/// Metrics for Disconnection types
247///
248/// These are just counters, and ideally we would implement these metrics on a peer-by-peer basis,
249/// in that we do not double-count peers for `TooManyPeers` if we make an outgoing connection and
250/// get disconnected twice
251#[derive(Metrics)]
252#[metrics(scope = "network")]
253pub struct DisconnectMetrics {
254    /// Number of peer disconnects due to `DisconnectRequested` (0x00)
255    pub(crate) disconnect_requested: Counter,
256
257    /// Number of peer disconnects due to `TcpSubsystemError` (0x01)
258    pub(crate) tcp_subsystem_error: Counter,
259
260    /// Number of peer disconnects due to `ProtocolBreach` (0x02)
261    pub(crate) protocol_breach: Counter,
262
263    /// Number of peer disconnects due to `UselessPeer` (0x03)
264    pub(crate) useless_peer: Counter,
265
266    /// Number of peer disconnects due to `TooManyPeers` (0x04)
267    pub(crate) too_many_peers: Counter,
268
269    /// Number of peer disconnects due to `AlreadyConnected` (0x05)
270    pub(crate) already_connected: Counter,
271
272    /// Number of peer disconnects due to `IncompatibleP2PProtocolVersion` (0x06)
273    pub(crate) incompatible: Counter,
274
275    /// Number of peer disconnects due to `NullNodeIdentity` (0x07)
276    pub(crate) null_node_identity: Counter,
277
278    /// Number of peer disconnects due to `ClientQuitting` (0x08)
279    pub(crate) client_quitting: Counter,
280
281    /// Number of peer disconnects due to `UnexpectedHandshakeIdentity` (0x09)
282    pub(crate) unexpected_identity: Counter,
283
284    /// Number of peer disconnects due to `ConnectedToSelf` (0x0a)
285    pub(crate) connected_to_self: Counter,
286
287    /// Number of peer disconnects due to `PingTimeout` (0x0b)
288    pub(crate) ping_timeout: Counter,
289
290    /// Number of peer disconnects due to `SubprotocolSpecific` (0x10)
291    pub(crate) subprotocol_specific: Counter,
292}
293
294impl DisconnectMetrics {
295    /// Increments the proper counter for the given disconnect reason
296    pub(crate) fn increment(&self, reason: DisconnectReason) {
297        match reason {
298            DisconnectReason::DisconnectRequested => self.disconnect_requested.increment(1),
299            DisconnectReason::TcpSubsystemError => self.tcp_subsystem_error.increment(1),
300            DisconnectReason::ProtocolBreach => self.protocol_breach.increment(1),
301            DisconnectReason::UselessPeer => self.useless_peer.increment(1),
302            DisconnectReason::TooManyPeers => self.too_many_peers.increment(1),
303            DisconnectReason::AlreadyConnected => self.already_connected.increment(1),
304            DisconnectReason::IncompatibleP2PProtocolVersion => self.incompatible.increment(1),
305            DisconnectReason::NullNodeIdentity => self.null_node_identity.increment(1),
306            DisconnectReason::ClientQuitting => self.client_quitting.increment(1),
307            DisconnectReason::UnexpectedHandshakeIdentity => self.unexpected_identity.increment(1),
308            DisconnectReason::ConnectedToSelf => self.connected_to_self.increment(1),
309            DisconnectReason::PingTimeout => self.ping_timeout.increment(1),
310            DisconnectReason::SubprotocolSpecific => self.subprotocol_specific.increment(1),
311        }
312    }
313}
314
315/// Metrics for the `EthRequestHandler`
316#[derive(Metrics)]
317#[metrics(scope = "network")]
318pub struct EthRequestHandlerMetrics {
319    /// Number of `GetBlockHeaders` requests received
320    pub(crate) eth_headers_requests_received_total: Counter,
321
322    /// Number of `GetReceipts` requests received
323    pub(crate) eth_receipts_requests_received_total: Counter,
324
325    /// Number of `GetBlockBodies` requests received
326    pub(crate) eth_bodies_requests_received_total: Counter,
327
328    /// Number of `GetNodeData` requests received
329    pub(crate) eth_node_data_requests_received_total: Counter,
330
331    /// Duration in seconds of call to poll
332    /// [`EthRequestHandler`](crate::eth_requests::EthRequestHandler).
333    pub(crate) acc_duration_poll_eth_req_handler: Gauge,
334}
335
336/// Eth67 announcement metrics, track entries by `TxType`
337#[derive(Metrics)]
338#[metrics(scope = "network.transaction_fetcher")]
339pub struct AnnouncedTxTypesMetrics {
340    /// Histogram for tracking frequency of legacy transaction type
341    pub(crate) legacy: Histogram,
342
343    /// Histogram for tracking frequency of EIP-2930 transaction type
344    pub(crate) eip2930: Histogram,
345
346    /// Histogram for tracking frequency of EIP-1559 transaction type
347    pub(crate) eip1559: Histogram,
348
349    /// Histogram for tracking frequency of EIP-4844 transaction type
350    pub(crate) eip4844: Histogram,
351
352    /// Histogram for tracking frequency of EIP-7702 transaction type
353    pub(crate) eip7702: Histogram,
354}
355
356/// Counts the number of transactions by their type in a block or collection.
357///
358/// This struct keeps track of the count of different transaction types
359/// as defined by various Ethereum Improvement Proposals (EIPs).
360#[derive(Debug, Default)]
361pub struct TxTypesCounter {
362    /// Count of legacy transactions (pre-EIP-2718).
363    pub(crate) legacy: usize,
364
365    /// Count of transactions conforming to EIP-2930 (Optional access lists).
366    pub(crate) eip2930: usize,
367
368    /// Count of transactions conforming to EIP-1559 (Fee market change).
369    pub(crate) eip1559: usize,
370
371    /// Count of transactions conforming to EIP-4844 (Shard Blob Transactions).
372    pub(crate) eip4844: usize,
373
374    /// Count of transactions conforming to EIP-7702 (Restricted Storage Windows).
375    pub(crate) eip7702: usize,
376}
377
378impl TxTypesCounter {
379    pub(crate) const fn increase_by_tx_type(&mut self, tx_type: TxType) {
380        match tx_type {
381            TxType::Legacy => {
382                self.legacy += 1;
383            }
384            TxType::Eip2930 => {
385                self.eip2930 += 1;
386            }
387            TxType::Eip1559 => {
388                self.eip1559 += 1;
389            }
390            TxType::Eip4844 => {
391                self.eip4844 += 1;
392            }
393            TxType::Eip7702 => {
394                self.eip7702 += 1;
395            }
396        }
397    }
398}
399
400impl AnnouncedTxTypesMetrics {
401    /// Update metrics during announcement validation, by examining each announcement entry based on
402    /// `TxType`
403    pub(crate) fn update_eth68_announcement_metrics(&self, tx_types_counter: TxTypesCounter) {
404        self.legacy.record(tx_types_counter.legacy as f64);
405        self.eip2930.record(tx_types_counter.eip2930 as f64);
406        self.eip1559.record(tx_types_counter.eip1559 as f64);
407        self.eip4844.record(tx_types_counter.eip4844 as f64);
408        self.eip7702.record(tx_types_counter.eip7702 as f64);
409    }
410}