referrerpolicy=no-referrer-when-downgrade

sp_trie/cache/
metrics.rs

1// This file is part of Substrate.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: Apache-2.0
5
6// Licensed under the Apache License, Version 2.0 (the "License");
7// you may not use this file except in compliance with the License.
8// You may obtain a copy of the License at
9//
10// 	http://www.apache.org/licenses/LICENSE-2.0
11//
12// Unless required by applicable law or agreed to in writing, software
13// distributed under the License is distributed on an "AS IS" BASIS,
14// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15// See the License for the specific language governing permissions and
16// limitations under the License.
17
18//! Metrics for the trie cache.
19
20use prometheus_endpoint::{
21	exponential_buckets,
22	prometheus::{core::Collector, HistogramTimer},
23	CounterVec, GaugeVec, HistogramOpts, HistogramVec, Opts, PrometheusError, Registry, U64,
24};
25
26// Register a metric with the given registry.
27fn register<T: Clone + Collector + 'static>(
28	metric: T,
29	registry: &Registry,
30) -> Result<T, PrometheusError> {
31	registry.register(Box::new(metric.clone()))?;
32	Ok(metric)
33}
34
35/// Metrics for the trie cache.
36/// This struct is used to track the performance of the trie cache.
37/// It contains histograms and counters for the shared and local caches.
38#[derive(Clone)]
39pub struct Metrics {
40	// The duration in seconds to update the shared trie caches from local to shared cache.
41	shared_update_duration: HistogramVec,
42	// Number of attempts hitting the shared trie caches.
43	shared_hits: CounterVec<U64>,
44	// Number of attempts to the shared trie caches.
45	shared_fetch_attempts: CounterVec<U64>,
46	// Number of attempts hitting the local trie caches.
47	local_hits: CounterVec<U64>,
48	// Number of attempts to the local caches.
49	local_fetch_attempts: CounterVec<U64>,
50	// Length of the local caches.
51	local_cache_lengths: HistogramVec,
52	// The inline size of the shared caches.
53	shared_cache_inline_size: GaugeVec<U64>,
54	// The heap size of the shared caches.
55	shared_cache_heap_size: GaugeVec<U64>,
56}
57
58impl Metrics {
59	/// Create a new instance of the metrics.
60	pub(crate) fn register(registry: &Registry) -> Result<Self, PrometheusError> {
61		Ok(Self {
62			shared_update_duration: register(
63				HistogramVec::new(
64					HistogramOpts {
65						common_opts: Opts::new(
66							"trie_cache_shared_update_duration",
67							"Duration in seconds to update the shared trie caches from local cache to shared cache",
68						),
69						buckets: exponential_buckets(0.001, 4.0, 9)
70							.expect("function parameters are constant and always valid; qed"),
71					},
72					&["cache_type"], // node or value
73				)?,
74				registry,
75			)?,
76			shared_hits: register(
77				CounterVec::new(
78					Opts::new(
79						"trie_cache_shared_hits",
80						"Number of attempts hitting the shared trie cache",
81					),
82					&["cache_type"], // node or value
83				)?,
84				registry,
85			)?,
86			shared_fetch_attempts: register(
87				CounterVec::new(
88					Opts::new(
89						"trie_cache_shared_fetch_attempts",
90						"Number of attempts to the shared trie cache",
91					),
92					&["cache_type"],
93				)?,
94				registry,
95			)?,
96			local_hits: register(
97				CounterVec::new(
98					Opts::new(
99						"trie_cache_local_hits",
100						"Number of attempts hitting the local trie cache",
101					),
102					&["cache_type"],
103				)?,
104				registry,
105			)?,
106			local_fetch_attempts: register(
107				CounterVec::new(
108					Opts::new(
109						"trie_cache_local_fetch_attempts",
110						"Number of attempts to the local cache",
111					),
112					&["cache_type"],
113				)?,
114				registry,
115			)?,
116			local_cache_lengths: register(
117				HistogramVec::new(
118					HistogramOpts {
119						common_opts: Opts::new(
120							"trie_cache_local_cache_lengths",
121							"Histogram of length of the local cache",
122						),
123						buckets: exponential_buckets(1.0, 4.0, 9)
124							.expect("function parameters are constant and always valid; qed"),
125					},
126					&["cache_type"],
127				)?,
128				registry,
129			)?,
130			shared_cache_inline_size: register(
131				GaugeVec::new(
132					Opts::new(
133						"trie_cache_shared_cache_inline_size",
134						"The inline size of the shared caches",
135					),
136					&["cache_type"],
137				)?,
138				registry,
139			)?,
140			shared_cache_heap_size: register(
141				GaugeVec::new(
142					Opts::new(
143						"trie_cache_shared_cache_heap_size",
144						"The heap size of the shared caches",
145					),
146					&["cache_type"],
147				)?,
148				registry,
149			)?,
150		})
151	}
152
153	/// Start a timer for the shared node cache update duration.
154	pub(crate) fn start_shared_node_update_timer(&self) -> HistogramTimer {
155		self.shared_update_duration.with_label_values(&["node"]).start_timer()
156	}
157
158	/// Start a timer for the shared value cache update duration.
159	pub(crate) fn start_shared_value_update_timer(&self) -> HistogramTimer {
160		self.shared_update_duration.with_label_values(&["value"]).start_timer()
161	}
162
163	/// Observe the shared node cache length.
164	pub(crate) fn observe_local_node_cache_length(&self, node_cache_len: usize) {
165		self.local_cache_lengths
166			.with_label_values(&["node"])
167			.observe(node_cache_len as f64);
168	}
169
170	/// Observe the shared value cache length.
171	pub(crate) fn observe_local_value_cache_length(&self, value_cache_len: usize) {
172		self.local_cache_lengths
173			.with_label_values(&["value"])
174			.observe(value_cache_len as f64);
175	}
176
177	/// Observe the shared node cache inline size.
178	pub(crate) fn observe_node_cache_inline_size(&self, cache_size: usize) {
179		self.shared_cache_inline_size
180			.with_label_values(&["node"])
181			.set(cache_size as u64);
182	}
183
184	/// Observe the shared value cache inline size.
185	pub(crate) fn observe_value_cache_inline_size(&self, cache_size: usize) {
186		self.shared_cache_inline_size
187			.with_label_values(&["value"])
188			.set(cache_size as u64);
189	}
190
191	/// Observe the shared node cache heap size.
192	pub(crate) fn observe_node_cache_heap_size(&self, cache_size: usize) {
193		self.shared_cache_heap_size.with_label_values(&["node"]).set(cache_size as u64);
194	}
195
196	/// Observe the shared value cache heap size.
197	pub(crate) fn observe_value_cache_heap_size(&self, cache_size: usize) {
198		self.shared_cache_heap_size.with_label_values(&["value"]).set(cache_size as u64);
199	}
200
201	/// Observe the hit stats from an instance of a local cache.
202	pub(crate) fn observe_hits_stats(&self, stats: &TrieHitStatsSnapshot) {
203		self.shared_hits
204			.with_label_values(&["node"])
205			.inc_by(stats.node_cache.shared_hits);
206		self.shared_fetch_attempts
207			.with_label_values(&["node"])
208			.inc_by(stats.node_cache.shared_fetch_attempts);
209		self.local_hits.with_label_values(&["node"]).inc_by(stats.node_cache.local_hits);
210		self.local_fetch_attempts
211			.with_label_values(&["node"])
212			.inc_by(stats.node_cache.local_fetch_attempts);
213
214		self.shared_hits
215			.with_label_values(&["value"])
216			.inc_by(stats.value_cache.shared_hits);
217		self.shared_fetch_attempts
218			.with_label_values(&["value"])
219			.inc_by(stats.value_cache.shared_fetch_attempts);
220		self.local_hits
221			.with_label_values(&["value"])
222			.inc_by(stats.value_cache.local_hits);
223		self.local_fetch_attempts
224			.with_label_values(&["value"])
225			.inc_by(stats.value_cache.local_fetch_attempts);
226	}
227}
228
229/// A snapshot of the hit/miss stats.
230#[derive(Default, Copy, Clone, Debug)]
231pub(crate) struct HitStatsSnapshot {
232	pub(crate) shared_hits: u64,
233	pub(crate) shared_fetch_attempts: u64,
234	pub(crate) local_hits: u64,
235	pub(crate) local_fetch_attempts: u64,
236}
237
238impl std::fmt::Display for HitStatsSnapshot {
239	fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
240		let shared_hits = self.shared_hits;
241		let shared_fetch_attempts = self.shared_fetch_attempts;
242		let local_hits = self.local_hits;
243		let local_fetch_attempts = self.local_fetch_attempts;
244
245		if shared_fetch_attempts == 0 && local_hits == 0 {
246			write!(fmt, "empty")
247		} else {
248			let percent_local = (local_hits as f32 / local_fetch_attempts as f32) * 100.0;
249			let percent_shared = (shared_hits as f32 / shared_fetch_attempts as f32) * 100.0;
250			write!(
251				fmt,
252				"local hit rate = {}% [{}/{}], shared hit rate = {}% [{}/{}]",
253				percent_local as u32,
254				local_hits,
255				local_fetch_attempts,
256				percent_shared as u32,
257				shared_hits,
258				shared_fetch_attempts
259			)
260		}
261	}
262}
263
264/// Snapshot of the hit/miss stats for the node cache and the value cache.
265#[derive(Default, Debug, Clone, Copy)]
266pub(crate) struct TrieHitStatsSnapshot {
267	pub(crate) node_cache: HitStatsSnapshot,
268	pub(crate) value_cache: HitStatsSnapshot,
269}