reth_trie/hashed_cursor/
metrics.rs1use super::{HashedCursor, HashedStorageCursor};
2use alloy_primitives::B256;
3use reth_storage_errors::db::DatabaseError;
4use std::time::{Duration, Instant};
5use tracing::debug_span;
6
7#[cfg(feature = "metrics")]
8use crate::TrieType;
9#[cfg(feature = "metrics")]
10use reth_metrics::metrics::{self, Histogram};
11
12#[cfg(feature = "metrics")]
16#[derive(Clone, Debug)]
17pub struct HashedCursorMetrics {
18 overall_duration: Histogram,
20 next_histogram: Histogram,
22 seek_histogram: Histogram,
24 is_storage_empty_histogram: Histogram,
26}
27
28#[cfg(feature = "metrics")]
29impl HashedCursorMetrics {
30 pub fn new(trie_type: TrieType) -> Self {
32 let trie_type_str = trie_type.as_str();
33
34 Self {
35 overall_duration: metrics::histogram!(
36 "trie.hashed_cursor.overall_duration",
37 "type" => trie_type_str
38 ),
39 next_histogram: metrics::histogram!(
40 "trie.hashed_cursor.operations",
41 "type" => trie_type_str,
42 "operation" => "next"
43 ),
44 seek_histogram: metrics::histogram!(
45 "trie.hashed_cursor.operations",
46 "type" => trie_type_str,
47 "operation" => "seek"
48 ),
49 is_storage_empty_histogram: metrics::histogram!(
50 "trie.hashed_cursor.operations",
51 "type" => trie_type_str,
52 "operation" => "is_storage_empty"
53 ),
54 }
55 }
56
57 pub fn record(&mut self, cache: &mut HashedCursorMetricsCache) {
62 self.next_histogram.record(cache.next_count as f64);
63 self.seek_histogram.record(cache.seek_count as f64);
64 self.is_storage_empty_histogram.record(cache.is_storage_empty_count as f64);
65 self.overall_duration.record(cache.total_duration.as_secs_f64());
66 cache.reset();
67 }
68}
69
70#[derive(Debug, Copy, Clone)]
72pub struct HashedCursorMetricsCache {
73 pub next_count: usize,
75 pub seek_count: usize,
77 pub is_storage_empty_count: usize,
79 pub total_duration: Duration,
81}
82
83impl Default for HashedCursorMetricsCache {
84 fn default() -> Self {
85 Self {
86 next_count: 0,
87 seek_count: 0,
88 is_storage_empty_count: 0,
89 total_duration: Duration::ZERO,
90 }
91 }
92}
93
94impl HashedCursorMetricsCache {
95 pub const fn reset(&mut self) {
97 self.next_count = 0;
98 self.seek_count = 0;
99 self.is_storage_empty_count = 0;
100 self.total_duration = Duration::ZERO;
101 }
102
103 pub fn extend(&mut self, other: &Self) {
107 self.next_count += other.next_count;
108 self.seek_count += other.seek_count;
109 self.is_storage_empty_count += other.is_storage_empty_count;
110 self.total_duration += other.total_duration;
111 }
112
113 pub fn record_span(&self, name: &'static str) {
115 let _span = debug_span!(
116 target: "trie::trie_cursor",
117 "Hashed cursor metrics",
118 name,
119 next_count = self.next_count,
120 seek_count = self.seek_count,
121 is_storage_empty_count = self.is_storage_empty_count,
122 total_duration = self.total_duration.as_secs_f64(),
123 )
124 .entered();
125 }
126}
127
128#[derive(Debug)]
134pub struct InstrumentedHashedCursor<'metrics, C> {
135 cursor: C,
137 metrics: &'metrics mut HashedCursorMetricsCache,
139}
140
141impl<'metrics, C> InstrumentedHashedCursor<'metrics, C> {
142 pub const fn new(cursor: C, metrics: &'metrics mut HashedCursorMetricsCache) -> Self {
144 Self { cursor, metrics }
145 }
146}
147
148impl<'metrics, C> HashedCursor for InstrumentedHashedCursor<'metrics, C>
149where
150 C: HashedCursor,
151{
152 type Value = C::Value;
153
154 fn seek(&mut self, key: B256) -> Result<Option<(B256, Self::Value)>, DatabaseError> {
155 let start = Instant::now();
156 self.metrics.seek_count += 1;
157 let result = self.cursor.seek(key);
158 self.metrics.total_duration += start.elapsed();
159 result
160 }
161
162 fn next(&mut self) -> Result<Option<(B256, Self::Value)>, DatabaseError> {
163 let start = Instant::now();
164 self.metrics.next_count += 1;
165 let result = self.cursor.next();
166 self.metrics.total_duration += start.elapsed();
167 result
168 }
169
170 fn reset(&mut self) {
171 self.cursor.reset()
172 }
173}
174
175impl<'metrics, C> HashedStorageCursor for InstrumentedHashedCursor<'metrics, C>
176where
177 C: HashedStorageCursor,
178{
179 fn is_storage_empty(&mut self) -> Result<bool, DatabaseError> {
180 let start = Instant::now();
181 self.metrics.is_storage_empty_count += 1;
182 let result = self.cursor.is_storage_empty();
183 self.metrics.total_duration += start.elapsed();
184 result
185 }
186
187 fn set_hashed_address(&mut self, hashed_address: B256) {
188 self.cursor.set_hashed_address(hashed_address)
189 }
190}