referrerpolicy=no-referrer-when-downgrade

sc_rpc_spec_v2/transaction/
metrics.rs

1// This file is part of Substrate.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
5
6// This program is free software: you can redistribute it and/or modify
7// it under the terms of the GNU General Public License as published by
8// the Free Software Foundation, either version 3 of the License, or
9// (at your option) any later version.
10
11// This program is distributed in the hope that it will be useful,
12// but WITHOUT ANY WARRANTY; without even the implied warranty of
13// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14// GNU General Public License for more details.
15
16// You should have received a copy of the GNU General Public License
17// along with this program. If not, see <https://www.gnu.org/licenses/>.
18
19//! Metrics for recording transaction events.
20
21use std::{collections::HashSet, time::Instant};
22
23use prometheus_endpoint::{
24	exponential_buckets, linear_buckets, register, Histogram, HistogramOpts, PrometheusError,
25	Registry,
26};
27
28use super::TransactionEvent;
29
30/// RPC layer metrics for transaction pool.
31#[derive(Debug, Clone)]
32pub struct Metrics {
33	validated: Histogram,
34	in_block: Histogram,
35	finalized: Histogram,
36	dropped: Histogram,
37	invalid: Histogram,
38	error: Histogram,
39}
40
41impl Metrics {
42	/// Creates a new [`Metrics`] instance.
43	pub fn new(registry: &Registry) -> Result<Self, PrometheusError> {
44		let validated = register(
45			Histogram::with_opts(
46				HistogramOpts::new(
47					"rpc_transaction_validation_time",
48					"RPC Transaction validation time in seconds",
49				)
50				.buckets(exponential_buckets(0.01, 2.0, 16).expect("Valid buckets; qed")),
51			)?,
52			registry,
53		)?;
54
55		let in_block = register(
56			Histogram::with_opts(
57				HistogramOpts::new(
58					"rpc_transaction_in_block_time",
59					"RPC Transaction in block time in seconds",
60				)
61				.buckets(linear_buckets(0.0, 3.0, 20).expect("Valid buckets; qed")),
62			)?,
63			registry,
64		)?;
65
66		let finalized = register(
67			Histogram::with_opts(
68				HistogramOpts::new(
69					"rpc_transaction_finalized_time",
70					"RPC Transaction finalized time in seconds",
71				)
72				.buckets(linear_buckets(0.01, 40.0, 20).expect("Valid buckets; qed")),
73			)?,
74			registry,
75		)?;
76
77		let dropped = register(
78			Histogram::with_opts(
79				HistogramOpts::new(
80					"rpc_transaction_dropped_time",
81					"RPC Transaction dropped time in seconds",
82				)
83				.buckets(linear_buckets(0.01, 3.0, 20).expect("Valid buckets; qed")),
84			)?,
85			registry,
86		)?;
87
88		let invalid = register(
89			Histogram::with_opts(
90				HistogramOpts::new(
91					"rpc_transaction_invalid_time",
92					"RPC Transaction invalid time in seconds",
93				)
94				.buckets(linear_buckets(0.01, 3.0, 20).expect("Valid buckets; qed")),
95			)?,
96			registry,
97		)?;
98
99		let error = register(
100			Histogram::with_opts(
101				HistogramOpts::new(
102					"rpc_transaction_error_time",
103					"RPC Transaction error time in seconds",
104				)
105				.buckets(linear_buckets(0.01, 3.0, 20).expect("Valid buckets; qed")),
106			)?,
107			registry,
108		)?;
109
110		Ok(Metrics { validated, in_block, finalized, dropped, invalid, error })
111	}
112}
113
114/// Transaction metrics for a single transaction instance.
115pub struct InstanceMetrics {
116	/// The metrics instance.
117	metrics: Option<Metrics>,
118	/// The time when the transaction was submitted.
119	submitted_at: Instant,
120	/// Ensure the states are reported once.
121	reported_states: HashSet<&'static str>,
122}
123
124impl InstanceMetrics {
125	/// Creates a new [`InstanceMetrics`] instance.
126	pub fn new(metrics: Option<Metrics>) -> Self {
127		Self { metrics, submitted_at: Instant::now(), reported_states: HashSet::new() }
128	}
129
130	/// Record the execution time of a transaction state.
131	///
132	/// This represents how long it took for the transaction to move to the next state.
133	///
134	/// The method must be called before the transaction event is provided to the user.
135	pub fn register_event<Hash>(&mut self, event: &TransactionEvent<Hash>) {
136		let Some(ref metrics) = self.metrics else {
137			return;
138		};
139
140		let (histogram, target_state) = match event {
141			TransactionEvent::Validated => (&metrics.validated, "validated"),
142			TransactionEvent::BestChainBlockIncluded(Some(_)) => (&metrics.in_block, "in_block"),
143			TransactionEvent::BestChainBlockIncluded(None) => (&metrics.in_block, "retracted"),
144			TransactionEvent::Finalized(..) => (&metrics.finalized, "finalized"),
145			TransactionEvent::Error(..) => (&metrics.error, "error"),
146			TransactionEvent::Dropped(..) => (&metrics.dropped, "dropped"),
147			TransactionEvent::Invalid(..) => (&metrics.invalid, "invalid"),
148		};
149
150		// Only record the state if it hasn't been reported before.
151		if self.reported_states.insert(target_state) {
152			histogram.observe(self.submitted_at.elapsed().as_secs_f64());
153		}
154	}
155}