1use std::sync::atomic::{AtomicU32, AtomicU64, AtomicUsize, Ordering};
2
3#[derive(Debug)]
5pub struct Statistics {
6 pub record_count: AtomicU32,
8 pub memory_usage: AtomicUsize,
9 pub disk_usage: AtomicU64,
10
11 pub total_gets: AtomicU64,
13 pub total_inserts: AtomicU64,
14 pub total_updates: AtomicU64,
15 pub total_deletes: AtomicU64,
16 pub total_range_queries: AtomicU64,
17
18 pub get_latency_ns: AtomicU64,
20 pub insert_latency_ns: AtomicU64,
21 pub delete_latency_ns: AtomicU64,
22
23 pub cache_hits: AtomicU64,
25 pub cache_misses: AtomicU64,
26 pub cache_evictions: AtomicU64,
27 pub cache_memory: AtomicUsize,
28
29 pub writes_buffered: AtomicU64,
31 pub writes_flushed: AtomicU64,
32 pub write_failures: AtomicU64,
33 pub sector_release_failures: AtomicU64,
34 pub write_entries_stuck: AtomicU64,
35 pub flush_count: AtomicU64,
36
37 pub disk_reads: AtomicU64,
39 pub disk_writes: AtomicU64,
40 pub disk_bytes_read: AtomicU64,
41 pub disk_bytes_written: AtomicU64,
42
43 pub key_not_found_errors: AtomicU64,
45 pub out_of_memory_errors: AtomicU64,
46 pub io_errors: AtomicU64,
47
48 pub ttl_expired_lazy: AtomicU64, pub ttl_expired_active: AtomicU64, pub ttl_cleaner_runs: AtomicU64, pub keys_with_ttl: AtomicU64, }
54
55impl Statistics {
56 pub fn new() -> Self {
57 Self {
58 record_count: AtomicU32::new(0),
60 memory_usage: AtomicUsize::new(0),
61 disk_usage: AtomicU64::new(0),
62
63 total_gets: AtomicU64::new(0),
65 total_inserts: AtomicU64::new(0),
66 total_updates: AtomicU64::new(0),
67 total_deletes: AtomicU64::new(0),
68 total_range_queries: AtomicU64::new(0),
69
70 get_latency_ns: AtomicU64::new(0),
72 insert_latency_ns: AtomicU64::new(0),
73 delete_latency_ns: AtomicU64::new(0),
74
75 cache_hits: AtomicU64::new(0),
77 cache_misses: AtomicU64::new(0),
78 cache_evictions: AtomicU64::new(0),
79 cache_memory: AtomicUsize::new(0),
80
81 writes_buffered: AtomicU64::new(0),
83 writes_flushed: AtomicU64::new(0),
84 write_failures: AtomicU64::new(0),
85 sector_release_failures: AtomicU64::new(0),
86 write_entries_stuck: AtomicU64::new(0),
87 flush_count: AtomicU64::new(0),
88
89 disk_reads: AtomicU64::new(0),
91 disk_writes: AtomicU64::new(0),
92 disk_bytes_read: AtomicU64::new(0),
93 disk_bytes_written: AtomicU64::new(0),
94
95 key_not_found_errors: AtomicU64::new(0),
97 out_of_memory_errors: AtomicU64::new(0),
98 io_errors: AtomicU64::new(0),
99
100 ttl_expired_lazy: AtomicU64::new(0),
102 ttl_expired_active: AtomicU64::new(0),
103 ttl_cleaner_runs: AtomicU64::new(0),
104 keys_with_ttl: AtomicU64::new(0),
105 }
106 }
107
108 pub fn record_get(&self, latency_ns: u64, hit: bool) {
110 self.total_gets.fetch_add(1, Ordering::Relaxed);
111 self.get_latency_ns.fetch_add(latency_ns, Ordering::Relaxed);
112
113 if hit {
114 self.cache_hits.fetch_add(1, Ordering::Relaxed);
115 } else {
116 self.cache_misses.fetch_add(1, Ordering::Relaxed);
117 }
118 }
119
120 pub fn record_insert(&self, latency_ns: u64, is_update: bool) {
122 if is_update {
123 self.total_updates.fetch_add(1, Ordering::Relaxed);
124 } else {
125 self.total_inserts.fetch_add(1, Ordering::Relaxed);
126 }
127 self.insert_latency_ns
128 .fetch_add(latency_ns, Ordering::Relaxed);
129 }
130
131 pub fn record_delete(&self, latency_ns: u64) {
133 self.total_deletes.fetch_add(1, Ordering::Relaxed);
134 self.delete_latency_ns
135 .fetch_add(latency_ns, Ordering::Relaxed);
136 }
137
138 pub fn record_range_query(&self) {
140 self.total_range_queries.fetch_add(1, Ordering::Relaxed);
141 }
142
143 pub fn record_eviction(&self, count: u64) {
145 self.cache_evictions.fetch_add(count, Ordering::Relaxed);
146 }
147
148 pub fn record_write_buffered(&self) {
150 self.record_writes_buffered(1);
151 }
152
153 pub(crate) fn record_writes_buffered(&self, count: u64) {
154 self.writes_buffered.fetch_add(count, Ordering::Relaxed);
155 }
156
157 pub fn record_write_flushed(&self, count: u64) {
158 self.writes_flushed.fetch_add(count, Ordering::Relaxed);
159 }
160
161 pub fn record_write_failed(&self) {
162 self.write_failures.fetch_add(1, Ordering::Relaxed);
163 }
164
165 pub fn record_write_entry_stuck(&self) {
166 self.write_entries_stuck.fetch_add(1, Ordering::Relaxed);
167 }
168
169 pub fn record_sector_release_failure(&self) {
170 self.sector_release_failures.fetch_add(1, Ordering::Relaxed);
171 }
172
173 pub fn record_disk_read(&self, bytes: u64) {
175 self.disk_reads.fetch_add(1, Ordering::Relaxed);
176 self.disk_bytes_read.fetch_add(bytes, Ordering::Relaxed);
177 }
178
179 pub fn record_disk_write(&self, bytes: u64) {
180 self.disk_writes.fetch_add(1, Ordering::Relaxed);
181 self.disk_bytes_written.fetch_add(bytes, Ordering::Relaxed);
182 }
183
184 pub fn record_error(&self, error: &crate::error::FeoxError) {
186 use crate::error::FeoxError;
187 match error {
188 FeoxError::KeyNotFound => {
189 self.key_not_found_errors.fetch_add(1, Ordering::Relaxed);
190 }
191 FeoxError::OutOfMemory => {
192 self.out_of_memory_errors.fetch_add(1, Ordering::Relaxed);
193 }
194 FeoxError::IoError(_) | FeoxError::IndeterminateWrite(_) => {
195 self.io_errors.fetch_add(1, Ordering::Relaxed);
196 }
197 _ => {}
198 }
199 }
200
201 pub fn snapshot(&self) -> StatsSnapshot {
203 let total_gets = self.total_gets.load(Ordering::Relaxed);
204 let total_inserts = self.total_inserts.load(Ordering::Relaxed);
205 let total_updates = self.total_updates.load(Ordering::Relaxed);
206 let total_deletes = self.total_deletes.load(Ordering::Relaxed);
207 let cache_hits = self.cache_hits.load(Ordering::Relaxed);
208 let cache_misses = self.cache_misses.load(Ordering::Relaxed);
209 let total_ops = total_gets
210 .saturating_add(total_inserts)
211 .saturating_add(total_updates)
212 .saturating_add(total_deletes);
213 let avg_get_latency = self
214 .get_latency_ns
215 .load(Ordering::Relaxed)
216 .checked_div(total_gets)
217 .unwrap_or(0);
218 let avg_insert_latency = self
219 .insert_latency_ns
220 .load(Ordering::Relaxed)
221 .checked_div(total_inserts.saturating_add(total_updates))
222 .unwrap_or(0);
223 let avg_delete_latency = self
224 .delete_latency_ns
225 .load(Ordering::Relaxed)
226 .checked_div(total_deletes)
227 .unwrap_or(0);
228 let cache_hit_rate = match cache_hits.saturating_add(cache_misses) {
229 0 => 0.0,
230 total => (cache_hits as f64 / total as f64) * 100.0,
231 };
232
233 StatsSnapshot {
234 record_count: self.record_count.load(Ordering::Relaxed),
235 memory_usage: self.memory_usage.load(Ordering::Relaxed),
236 total_operations: total_ops,
237 total_gets,
238 total_inserts,
239 total_updates,
240 total_deletes,
241 total_range_queries: self.total_range_queries.load(Ordering::Relaxed),
242 avg_get_latency_ns: avg_get_latency,
243 avg_insert_latency_ns: avg_insert_latency,
244 avg_delete_latency_ns: avg_delete_latency,
245 cache_hits,
246 cache_misses,
247 cache_hit_rate,
248 cache_evictions: self.cache_evictions.load(Ordering::Relaxed),
249 cache_memory: self.cache_memory.load(Ordering::Relaxed),
250 writes_buffered: self.writes_buffered.load(Ordering::Relaxed),
251 writes_flushed: self.writes_flushed.load(Ordering::Relaxed),
252 write_failures: self.write_failures.load(Ordering::Relaxed),
253 sector_release_failures: self.sector_release_failures.load(Ordering::Relaxed),
254 write_entries_stuck: self.write_entries_stuck.load(Ordering::Relaxed),
255 flush_count: self.flush_count.load(Ordering::Relaxed),
256 disk_reads: self.disk_reads.load(Ordering::Relaxed),
257 disk_writes: self.disk_writes.load(Ordering::Relaxed),
258 disk_bytes_read: self.disk_bytes_read.load(Ordering::Relaxed),
259 disk_bytes_written: self.disk_bytes_written.load(Ordering::Relaxed),
260 key_not_found_errors: self.key_not_found_errors.load(Ordering::Relaxed),
261 out_of_memory_errors: self.out_of_memory_errors.load(Ordering::Relaxed),
262 io_errors: self.io_errors.load(Ordering::Relaxed),
263 ttl_expired_lazy: self.ttl_expired_lazy.load(Ordering::Relaxed),
264 ttl_expired_active: self.ttl_expired_active.load(Ordering::Relaxed),
265 ttl_cleaner_runs: self.ttl_cleaner_runs.load(Ordering::Relaxed),
266 keys_with_ttl: self.keys_with_ttl.load(Ordering::Relaxed),
267 }
268 }
269
270 pub fn reset(&self) {
272 self.total_gets.store(0, Ordering::Relaxed);
273 self.total_inserts.store(0, Ordering::Relaxed);
274 self.total_updates.store(0, Ordering::Relaxed);
275 self.total_deletes.store(0, Ordering::Relaxed);
276 self.total_range_queries.store(0, Ordering::Relaxed);
277 self.get_latency_ns.store(0, Ordering::Relaxed);
278 self.insert_latency_ns.store(0, Ordering::Relaxed);
279 self.delete_latency_ns.store(0, Ordering::Relaxed);
280 self.cache_hits.store(0, Ordering::Relaxed);
281 self.cache_misses.store(0, Ordering::Relaxed);
282 self.cache_evictions.store(0, Ordering::Relaxed);
283 self.writes_buffered.store(0, Ordering::Relaxed);
284 self.writes_flushed.store(0, Ordering::Relaxed);
285 self.write_failures.store(0, Ordering::Relaxed);
286 self.sector_release_failures.store(0, Ordering::Relaxed);
287 self.write_entries_stuck.store(0, Ordering::Relaxed);
288 self.flush_count.store(0, Ordering::Relaxed);
289 self.disk_reads.store(0, Ordering::Relaxed);
290 self.disk_writes.store(0, Ordering::Relaxed);
291 self.disk_bytes_read.store(0, Ordering::Relaxed);
292 self.disk_bytes_written.store(0, Ordering::Relaxed);
293 self.key_not_found_errors.store(0, Ordering::Relaxed);
294 self.out_of_memory_errors.store(0, Ordering::Relaxed);
295 self.io_errors.store(0, Ordering::Relaxed);
296 self.ttl_expired_lazy.store(0, Ordering::Relaxed);
297 self.ttl_expired_active.store(0, Ordering::Relaxed);
298 self.ttl_cleaner_runs.store(0, Ordering::Relaxed);
299 }
300}
301
302impl Default for Statistics {
303 fn default() -> Self {
304 Self::new()
305 }
306}
307
308#[derive(Debug, Clone)]
310pub struct StatsSnapshot {
311 pub record_count: u32,
313 pub memory_usage: usize,
314
315 pub total_operations: u64,
317 pub total_gets: u64,
318 pub total_inserts: u64,
319 pub total_updates: u64,
320 pub total_deletes: u64,
321 pub total_range_queries: u64,
322
323 pub avg_get_latency_ns: u64,
325 pub avg_insert_latency_ns: u64,
326 pub avg_delete_latency_ns: u64,
327
328 pub cache_hits: u64,
330 pub cache_misses: u64,
331 pub cache_hit_rate: f64,
332 pub cache_evictions: u64,
333 pub cache_memory: usize,
334
335 pub writes_buffered: u64,
337 pub writes_flushed: u64,
338 pub write_failures: u64,
339 pub sector_release_failures: u64,
340 pub write_entries_stuck: u64,
341 pub flush_count: u64,
342
343 pub disk_reads: u64,
345 pub disk_writes: u64,
346 pub disk_bytes_read: u64,
347 pub disk_bytes_written: u64,
348
349 pub key_not_found_errors: u64,
351 pub out_of_memory_errors: u64,
352 pub io_errors: u64,
353
354 pub ttl_expired_lazy: u64,
356 pub ttl_expired_active: u64,
357 pub ttl_cleaner_runs: u64,
358 pub keys_with_ttl: u64,
359}
360
361impl StatsSnapshot {
362 pub fn format(&self) -> String {
364 format!(
365 "=== FeOxDB Statistics ===\n\
366 Store:\n\
367 - Records: {}\n\
368 - Memory: {:.2} MB\n\n\
369 Operations:\n\
370 - Total: {}\n\
371 - Gets: {} (avg latency: {:.2}μs)\n\
372 - Inserts: {} (avg latency: {:.2}μs)\n\
373 - Updates: {}\n\
374 - Deletes: {} (avg latency: {:.2}μs)\n\
375 - Range Queries: {}\n\n\
376 Cache:\n\
377 - Hit Rate: {:.1}%\n\
378 - Hits: {}\n\
379 - Misses: {}\n\
380 - Evictions: {}\n\
381 - Memory: {:.2} MB\n\n\
382 Write Buffer:\n\
383 - Buffered: {}\n\
384 - Flushed: {}\n\
385 - Failures: {}\n\
386 - Flush Count: {}\n\n\
387 Disk I/O:\n\
388 - Reads: {} ({:.2} MB)\n\
389 - Writes: {} ({:.2} MB)\n\n\
390 Errors:\n\
391 - Key Not Found: {}\n\
392 - Out of Memory: {}\n\
393 - I/O Errors: {}",
394 self.record_count,
395 self.memory_usage as f64 / 1_048_576.0,
396 self.total_operations,
397 self.total_gets,
398 self.avg_get_latency_ns as f64 / 1000.0,
399 self.total_inserts,
400 self.avg_insert_latency_ns as f64 / 1000.0,
401 self.total_updates,
402 self.total_deletes,
403 self.avg_delete_latency_ns as f64 / 1000.0,
404 self.total_range_queries,
405 self.cache_hit_rate,
406 self.cache_hits,
407 self.cache_misses,
408 self.cache_evictions,
409 self.cache_memory as f64 / 1_048_576.0,
410 self.writes_buffered,
411 self.writes_flushed,
412 self.write_failures,
413 self.flush_count,
414 self.disk_reads,
415 self.disk_bytes_read as f64 / 1_048_576.0,
416 self.disk_writes,
417 self.disk_bytes_written as f64 / 1_048_576.0,
418 self.key_not_found_errors,
419 self.out_of_memory_errors,
420 self.io_errors
421 )
422 }
423}