mito2/sst/index/
statistics.rs1use std::time::{Duration, Instant};
16
17use crate::metrics::{INDEX_CREATE_BYTES_TOTAL, INDEX_CREATE_ELAPSED, INDEX_CREATE_ROWS_TOTAL};
18
19pub(crate) type ByteCount = u64;
20pub(crate) type RowCount = usize;
21
22enum Stage {
24 Update,
25 Finish,
26 Cleanup,
27}
28
29pub(crate) struct Statistics {
31 index_type: &'static str,
33 update_elapsed: Duration,
35 finish_elapsed: Duration,
37 cleanup_eplased: Duration,
39 row_count: RowCount,
41 byte_count: ByteCount,
43}
44
45impl Statistics {
46 pub fn new(index_type: &'static str) -> Self {
47 Self {
48 index_type,
49 update_elapsed: Duration::default(),
50 finish_elapsed: Duration::default(),
51 cleanup_eplased: Duration::default(),
52 row_count: 0,
53 byte_count: 0,
54 }
55 }
56
57 #[must_use]
59 pub fn record_update(&mut self) -> TimerGuard<'_> {
60 TimerGuard::new(self, Stage::Update)
61 }
62
63 #[must_use]
65 pub fn record_finish(&mut self) -> TimerGuard<'_> {
66 TimerGuard::new(self, Stage::Finish)
67 }
68
69 #[must_use]
71 pub fn record_cleanup(&mut self) -> TimerGuard<'_> {
72 TimerGuard::new(self, Stage::Cleanup)
73 }
74
75 pub fn row_count(&self) -> RowCount {
77 self.row_count
78 }
79
80 pub fn byte_count(&self) -> ByteCount {
82 self.byte_count
83 }
84}
85
86impl Drop for Statistics {
87 fn drop(&mut self) {
88 INDEX_CREATE_ELAPSED
89 .with_label_values(&["update", self.index_type])
90 .observe(self.update_elapsed.as_secs_f64());
91 INDEX_CREATE_ELAPSED
92 .with_label_values(&["finish", self.index_type])
93 .observe(self.finish_elapsed.as_secs_f64());
94 INDEX_CREATE_ELAPSED
95 .with_label_values(&["cleanup", self.index_type])
96 .observe(self.cleanup_eplased.as_secs_f64());
97 INDEX_CREATE_ELAPSED
98 .with_label_values(&["total", self.index_type])
99 .observe(
100 (self.update_elapsed + self.finish_elapsed + self.cleanup_eplased).as_secs_f64(),
101 );
102
103 INDEX_CREATE_ROWS_TOTAL
104 .with_label_values(&[self.index_type])
105 .inc_by(self.row_count as _);
106 INDEX_CREATE_BYTES_TOTAL
107 .with_label_values(&[self.index_type])
108 .inc_by(self.byte_count as _);
109 }
110}
111
112pub(crate) struct TimerGuard<'a> {
115 stats: &'a mut Statistics,
116 stage: Stage,
117 timer: Instant,
118}
119
120impl<'a> TimerGuard<'a> {
121 fn new(stats: &'a mut Statistics, stage: Stage) -> Self {
123 Self {
124 stats,
125 stage,
126 timer: Instant::now(),
127 }
128 }
129
130 pub fn inc_row_count(&mut self, n: usize) {
132 self.stats.row_count += n;
133 }
134
135 pub fn inc_byte_count(&mut self, n: u64) {
137 self.stats.byte_count += n;
138 }
139}
140
141impl Drop for TimerGuard<'_> {
142 fn drop(&mut self) {
143 match self.stage {
144 Stage::Update => {
145 self.stats.update_elapsed += self.timer.elapsed();
146 }
147 Stage::Finish => {
148 self.stats.finish_elapsed += self.timer.elapsed();
149 }
150 Stage::Cleanup => {
151 self.stats.cleanup_eplased += self.timer.elapsed();
152 }
153 }
154 }
155}
156
157#[cfg(test)]
158mod tests {
159 use super::*;
160
161 #[test]
162 fn test_statistics_basic() {
163 let mut stats = Statistics::new("test");
164 {
165 let mut guard = stats.record_update();
166 guard.inc_byte_count(100);
167 guard.inc_row_count(10);
168
169 let now = Instant::now();
170 while now.elapsed().is_zero() {
171 }
173 }
174 {
175 let _guard = stats.record_finish();
176 let now = Instant::now();
177 while now.elapsed().is_zero() {
178 }
180 }
181 {
182 let _guard = stats.record_cleanup();
183 let now = Instant::now();
184 while now.elapsed().is_zero() {
185 }
187 }
188 assert_eq!(stats.row_count(), 10);
189 assert_eq!(stats.byte_count(), 100);
190 assert!(stats.update_elapsed > Duration::default());
191 assert!(stats.finish_elapsed > Duration::default());
192 assert!(stats.cleanup_eplased > Duration::default());
193 }
194}