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 /// Total number of transactions ignored because pending pool imports are at capacity.
135 pub(crate) skipped_transactions_pending_pool_imports_at_capacity: Counter,
136 /// The time it took to prepare transactions for import. This is mostly sender recovery.
137 pub(crate) pool_import_prepare_duration: Histogram,
138
139 /* ================ POLL DURATION ================ */
140
141 /* -- Total poll duration of `TransactionsManager` future -- */
142 /// Duration in seconds of call to
143 /// [`TransactionsManager`](crate::transactions::TransactionsManager)'s poll function.
144 ///
145 /// Updating metrics could take time, so the true duration of this call could
146 /// be longer than the sum of the accumulated durations of polling nested items.
147 pub(crate) duration_poll_tx_manager: Gauge,
148
149 /* -- Poll duration of items nested in `TransactionsManager` future -- */
150 /// Accumulated time spent streaming session updates and updating peers accordingly, in
151 /// one call to poll the [`TransactionsManager`](crate::transactions::TransactionsManager)
152 /// future.
153 ///
154 /// Duration in seconds.
155 pub(crate) acc_duration_poll_network_events: Gauge,
156 /// Accumulated time spent flushing the queue of batched pending pool imports into pool, in
157 /// one call to poll the [`TransactionsManager`](crate::transactions::TransactionsManager)
158 /// future.
159 ///
160 /// Duration in seconds.
161 pub(crate) acc_duration_poll_pending_pool_imports: Gauge,
162 /// Accumulated time spent streaming transaction and announcement broadcast, queueing for
163 /// pool import or requesting respectively, in one call to poll the
164 /// [`TransactionsManager`](crate::transactions::TransactionsManager) future.
165 ///
166 /// Duration in seconds.
167 pub(crate) acc_duration_poll_transaction_events: Gauge,
168 /// Accumulated time spent streaming fetch events, queueing for pool import on successful
169 /// fetch, in one call to poll the
170 /// [`TransactionsManager`](crate::transactions::TransactionsManager) future.
171 ///
172 /// Duration in seconds.
173 pub(crate) acc_duration_poll_fetch_events: Gauge,
174 /// Accumulated time spent streaming and propagating transactions that were successfully
175 /// imported into the pool, in one call to poll the
176 /// [`TransactionsManager`](crate::transactions::TransactionsManager) future.
177 ///
178 /// Duration in seconds.
179 pub(crate) acc_duration_poll_imported_transactions: Gauge,
180 /// Accumulated time spent assembling and sending requests for hashes fetching pending, in
181 /// one call to poll the [`TransactionsManager`](crate::transactions::TransactionsManager)
182 /// future.
183 ///
184 /// Duration in seconds.
185 pub(crate) acc_duration_fetch_pending_hashes: Gauge,
186 /// Accumulated time spent streaming commands and propagating, fetching and serving
187 /// transactions accordingly, in one call to poll the
188 /// [`TransactionsManager`](crate::transactions::TransactionsManager) future.
189 ///
190 /// Duration in seconds.
191 pub(crate) acc_duration_poll_commands: Gauge,
192}
193
194/// Metrics for the [`TransactionsManager`](crate::transactions::TransactionsManager).
195#[derive(Metrics)]
196#[metrics(scope = "network")]
197pub struct TransactionFetcherMetrics {
198 /// Currently active outgoing [`GetPooledTransactions`](reth_eth_wire::GetPooledTransactions)
199 /// requests.
200 pub(crate) inflight_transaction_requests: Gauge,
201 /// Number of inflight requests at which the
202 /// [`TransactionFetcher`](crate::transactions::TransactionFetcher) is considered to be at
203 /// capacity. Note, this is not a limit to the number of inflight requests, but a health
204 /// measure.
205 pub(crate) capacity_inflight_requests: Counter,
206 /// Hashes in currently active outgoing
207 /// [`GetPooledTransactions`](reth_eth_wire::GetPooledTransactions) requests.
208 pub(crate) hashes_inflight_transaction_requests: Gauge,
209 /// How often we failed to send a request to the peer because the channel was full.
210 pub(crate) egress_peer_channel_full: Counter,
211 /// Total number of hashes pending fetch.
212 pub(crate) hashes_pending_fetch: Gauge,
213 /// Total number of fetched transactions.
214 pub(crate) fetched_transactions: Counter,
215 /// Total number of transactions that were received in
216 /// [`PooledTransactions`](reth_eth_wire::PooledTransactions) responses, that weren't
217 /// requested.
218 pub(crate) unsolicited_transactions: Counter,
219 /* ================ SEARCH DURATION ================ */
220 /// Time spent searching for an idle peer in call to
221 /// [`TransactionFetcher::find_any_idle_fallback_peer_for_any_pending_hash`](crate::transactions::TransactionFetcher::find_any_idle_fallback_peer_for_any_pending_hash).
222 ///
223 /// Duration in seconds.
224 pub(crate) duration_find_idle_fallback_peer_for_any_pending_hash: Gauge,
225
226 /// Time spent searching for hashes pending fetch, announced by a given peer in
227 /// [`TransactionFetcher::fill_request_from_hashes_pending_fetch`](crate::transactions::TransactionFetcher::fill_request_from_hashes_pending_fetch).
228 ///
229 /// Duration in seconds.
230 pub(crate) duration_fill_request_from_hashes_pending_fetch: Gauge,
231}
232
233/// Measures the duration of executing the given code block. The duration is added to the given
234/// accumulator value passed as a mutable reference.
235#[macro_export]
236macro_rules! duration_metered_exec {
237 ($code:expr, $acc:expr) => {{
238 let start = std::time::Instant::now();
239
240 let res = $code;
241
242 $acc += start.elapsed();
243
244 res
245 }};
246}
247
248/// Metrics for Disconnection types
249///
250/// These are just counters, and ideally we would implement these metrics on a peer-by-peer basis,
251/// in that we do not double-count peers for `TooManyPeers` if we make an outgoing connection and
252/// get disconnected twice
253#[derive(Metrics)]
254#[metrics(scope = "network")]
255pub struct DisconnectMetrics {
256 /// Number of peer disconnects due to `DisconnectRequested` (0x00)
257 pub(crate) disconnect_requested: Counter,
258
259 /// Number of peer disconnects due to `TcpSubsystemError` (0x01)
260 pub(crate) tcp_subsystem_error: Counter,
261
262 /// Number of peer disconnects due to `ProtocolBreach` (0x02)
263 pub(crate) protocol_breach: Counter,
264
265 /// Number of peer disconnects due to `UselessPeer` (0x03)
266 pub(crate) useless_peer: Counter,
267
268 /// Number of peer disconnects due to `TooManyPeers` (0x04)
269 pub(crate) too_many_peers: Counter,
270
271 /// Number of peer disconnects due to `AlreadyConnected` (0x05)
272 pub(crate) already_connected: Counter,
273
274 /// Number of peer disconnects due to `IncompatibleP2PProtocolVersion` (0x06)
275 pub(crate) incompatible: Counter,
276
277 /// Number of peer disconnects due to `NullNodeIdentity` (0x07)
278 pub(crate) null_node_identity: Counter,
279
280 /// Number of peer disconnects due to `ClientQuitting` (0x08)
281 pub(crate) client_quitting: Counter,
282
283 /// Number of peer disconnects due to `UnexpectedHandshakeIdentity` (0x09)
284 pub(crate) unexpected_identity: Counter,
285
286 /// Number of peer disconnects due to `ConnectedToSelf` (0x0a)
287 pub(crate) connected_to_self: Counter,
288
289 /// Number of peer disconnects due to `PingTimeout` (0x0b)
290 pub(crate) ping_timeout: Counter,
291
292 /// Number of peer disconnects due to `SubprotocolSpecific` (0x10)
293 pub(crate) subprotocol_specific: Counter,
294}
295
296impl DisconnectMetrics {
297 /// Increments the proper counter for the given disconnect reason
298 pub(crate) fn increment(&self, reason: DisconnectReason) {
299 match reason {
300 DisconnectReason::DisconnectRequested => self.disconnect_requested.increment(1),
301 DisconnectReason::TcpSubsystemError => self.tcp_subsystem_error.increment(1),
302 DisconnectReason::ProtocolBreach => self.protocol_breach.increment(1),
303 DisconnectReason::UselessPeer => self.useless_peer.increment(1),
304 DisconnectReason::TooManyPeers => self.too_many_peers.increment(1),
305 DisconnectReason::AlreadyConnected => self.already_connected.increment(1),
306 DisconnectReason::IncompatibleP2PProtocolVersion => self.incompatible.increment(1),
307 DisconnectReason::NullNodeIdentity => self.null_node_identity.increment(1),
308 DisconnectReason::ClientQuitting => self.client_quitting.increment(1),
309 DisconnectReason::UnexpectedHandshakeIdentity => self.unexpected_identity.increment(1),
310 DisconnectReason::ConnectedToSelf => self.connected_to_self.increment(1),
311 DisconnectReason::PingTimeout => self.ping_timeout.increment(1),
312 DisconnectReason::SubprotocolSpecific => self.subprotocol_specific.increment(1),
313 }
314 }
315}
316
317/// Metrics for the `EthRequestHandler`
318#[derive(Metrics)]
319#[metrics(scope = "network")]
320pub struct EthRequestHandlerMetrics {
321 /// Number of `GetBlockHeaders` requests received
322 pub(crate) eth_headers_requests_received_total: Counter,
323
324 /// Number of `GetReceipts` requests received
325 pub(crate) eth_receipts_requests_received_total: Counter,
326
327 /// Number of `GetBlockBodies` requests received
328 pub(crate) eth_bodies_requests_received_total: Counter,
329
330 /// Number of `GetNodeData` requests received
331 pub(crate) eth_node_data_requests_received_total: Counter,
332
333 /// Duration in seconds of call to poll
334 /// [`EthRequestHandler`](crate::eth_requests::EthRequestHandler).
335 pub(crate) acc_duration_poll_eth_req_handler: Gauge,
336}
337
338/// Eth67 announcement metrics, track entries by `TxType`
339#[derive(Metrics)]
340#[metrics(scope = "network.transaction_fetcher")]
341pub struct AnnouncedTxTypesMetrics {
342 /// Histogram for tracking frequency of legacy transaction type
343 pub(crate) legacy: Histogram,
344
345 /// Histogram for tracking frequency of EIP-2930 transaction type
346 pub(crate) eip2930: Histogram,
347
348 /// Histogram for tracking frequency of EIP-1559 transaction type
349 pub(crate) eip1559: Histogram,
350
351 /// Histogram for tracking frequency of EIP-4844 transaction type
352 pub(crate) eip4844: Histogram,
353
354 /// Histogram for tracking frequency of EIP-7702 transaction type
355 pub(crate) eip7702: Histogram,
356}
357
358/// Counts the number of transactions by their type in a block or collection.
359///
360/// This struct keeps track of the count of different transaction types
361/// as defined by various Ethereum Improvement Proposals (EIPs).
362#[derive(Debug, Default)]
363pub struct TxTypesCounter {
364 /// Count of legacy transactions (pre-EIP-2718).
365 pub(crate) legacy: usize,
366
367 /// Count of transactions conforming to EIP-2930 (Optional access lists).
368 pub(crate) eip2930: usize,
369
370 /// Count of transactions conforming to EIP-1559 (Fee market change).
371 pub(crate) eip1559: usize,
372
373 /// Count of transactions conforming to EIP-4844 (Shard Blob Transactions).
374 pub(crate) eip4844: usize,
375
376 /// Count of transactions conforming to EIP-7702 (Restricted Storage Windows).
377 pub(crate) eip7702: usize,
378}
379
380impl TxTypesCounter {
381 pub(crate) const fn increase_by_tx_type(&mut self, tx_type: TxType) {
382 match tx_type {
383 TxType::Legacy => {
384 self.legacy += 1;
385 }
386 TxType::Eip2930 => {
387 self.eip2930 += 1;
388 }
389 TxType::Eip1559 => {
390 self.eip1559 += 1;
391 }
392 TxType::Eip4844 => {
393 self.eip4844 += 1;
394 }
395 TxType::Eip7702 => {
396 self.eip7702 += 1;
397 }
398 }
399 }
400}
401
402impl AnnouncedTxTypesMetrics {
403 /// Update metrics during announcement validation, by examining each announcement entry based on
404 /// `TxType`
405 pub(crate) fn update_eth68_announcement_metrics(&self, tx_types_counter: TxTypesCounter) {
406 self.legacy.record(tx_types_counter.legacy as f64);
407 self.eip2930.record(tx_types_counter.eip2930 as f64);
408 self.eip1559.record(tx_types_counter.eip1559 as f64);
409 self.eip4844.record(tx_types_counter.eip4844 as f64);
410 self.eip7702.record(tx_types_counter.eip7702 as f64);
411 }
412}