feoxdb/
stats.rs

1use std::sync::atomic::{AtomicU32, AtomicU64, AtomicUsize, Ordering};
2
3/// Central statistics hub for FeoxStore
4#[derive(Debug)]
5pub struct Statistics {
6    // Store metrics
7    pub record_count: AtomicU32,
8    pub memory_usage: AtomicUsize,
9    pub disk_usage: AtomicU64,
10
11    // Operation counters
12    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    // Operation latencies (in nanoseconds)
19    pub get_latency_ns: AtomicU64,
20    pub insert_latency_ns: AtomicU64,
21    pub delete_latency_ns: AtomicU64,
22
23    // Cache metrics
24    pub cache_hits: AtomicU64,
25    pub cache_misses: AtomicU64,
26    pub cache_evictions: AtomicU64,
27    pub cache_memory: AtomicUsize,
28
29    // Write buffer metrics
30    pub writes_buffered: AtomicU64,
31    pub writes_flushed: AtomicU64,
32    pub write_failures: AtomicU64,
33    pub flush_count: AtomicU64,
34
35    // Disk I/O metrics
36    pub disk_reads: AtomicU64,
37    pub disk_writes: AtomicU64,
38    pub disk_bytes_read: AtomicU64,
39    pub disk_bytes_written: AtomicU64,
40
41    // Error counters
42    pub key_not_found_errors: AtomicU64,
43    pub out_of_memory_errors: AtomicU64,
44    pub io_errors: AtomicU64,
45}
46
47impl Statistics {
48    pub fn new() -> Self {
49        Self {
50            // Store metrics
51            record_count: AtomicU32::new(0),
52            memory_usage: AtomicUsize::new(0),
53            disk_usage: AtomicU64::new(0),
54
55            // Operation counters
56            total_gets: AtomicU64::new(0),
57            total_inserts: AtomicU64::new(0),
58            total_updates: AtomicU64::new(0),
59            total_deletes: AtomicU64::new(0),
60            total_range_queries: AtomicU64::new(0),
61
62            // Operation latencies
63            get_latency_ns: AtomicU64::new(0),
64            insert_latency_ns: AtomicU64::new(0),
65            delete_latency_ns: AtomicU64::new(0),
66
67            // Cache metrics
68            cache_hits: AtomicU64::new(0),
69            cache_misses: AtomicU64::new(0),
70            cache_evictions: AtomicU64::new(0),
71            cache_memory: AtomicUsize::new(0),
72
73            // Write buffer metrics
74            writes_buffered: AtomicU64::new(0),
75            writes_flushed: AtomicU64::new(0),
76            write_failures: AtomicU64::new(0),
77            flush_count: AtomicU64::new(0),
78
79            // Disk I/O metrics
80            disk_reads: AtomicU64::new(0),
81            disk_writes: AtomicU64::new(0),
82            disk_bytes_read: AtomicU64::new(0),
83            disk_bytes_written: AtomicU64::new(0),
84
85            // Error counters
86            key_not_found_errors: AtomicU64::new(0),
87            out_of_memory_errors: AtomicU64::new(0),
88            io_errors: AtomicU64::new(0),
89        }
90    }
91
92    /// Record a get operation
93    pub fn record_get(&self, latency_ns: u64, hit: bool) {
94        self.total_gets.fetch_add(1, Ordering::Relaxed);
95        self.get_latency_ns.fetch_add(latency_ns, Ordering::Relaxed);
96
97        if hit {
98            self.cache_hits.fetch_add(1, Ordering::Relaxed);
99        } else {
100            self.cache_misses.fetch_add(1, Ordering::Relaxed);
101        }
102    }
103
104    /// Record an insert operation
105    pub fn record_insert(&self, latency_ns: u64, is_update: bool) {
106        if is_update {
107            self.total_updates.fetch_add(1, Ordering::Relaxed);
108        } else {
109            self.total_inserts.fetch_add(1, Ordering::Relaxed);
110        }
111        self.insert_latency_ns
112            .fetch_add(latency_ns, Ordering::Relaxed);
113    }
114
115    /// Record a delete operation
116    pub fn record_delete(&self, latency_ns: u64) {
117        self.total_deletes.fetch_add(1, Ordering::Relaxed);
118        self.delete_latency_ns
119            .fetch_add(latency_ns, Ordering::Relaxed);
120    }
121
122    /// Record a range query
123    pub fn record_range_query(&self) {
124        self.total_range_queries.fetch_add(1, Ordering::Relaxed);
125    }
126
127    /// Record cache eviction
128    pub fn record_eviction(&self, count: u64) {
129        self.cache_evictions.fetch_add(count, Ordering::Relaxed);
130    }
131
132    /// Record write buffer operation
133    pub fn record_write_buffered(&self) {
134        self.writes_buffered.fetch_add(1, Ordering::Relaxed);
135    }
136
137    pub fn record_write_flushed(&self, count: u64) {
138        self.writes_flushed.fetch_add(count, Ordering::Relaxed);
139        self.flush_count.fetch_add(1, Ordering::Relaxed);
140    }
141
142    pub fn record_write_failed(&self) {
143        self.write_failures.fetch_add(1, Ordering::Relaxed);
144    }
145
146    /// Record disk I/O
147    pub fn record_disk_read(&self, bytes: u64) {
148        self.disk_reads.fetch_add(1, Ordering::Relaxed);
149        self.disk_bytes_read.fetch_add(bytes, Ordering::Relaxed);
150    }
151
152    pub fn record_disk_write(&self, bytes: u64) {
153        self.disk_writes.fetch_add(1, Ordering::Relaxed);
154        self.disk_bytes_written.fetch_add(bytes, Ordering::Relaxed);
155    }
156
157    /// Record errors
158    pub fn record_error(&self, error: &crate::error::FeoxError) {
159        use crate::error::FeoxError;
160        match error {
161            FeoxError::KeyNotFound => {
162                self.key_not_found_errors.fetch_add(1, Ordering::Relaxed);
163            }
164            FeoxError::OutOfMemory => {
165                self.out_of_memory_errors.fetch_add(1, Ordering::Relaxed);
166            }
167            FeoxError::IoError(_) => {
168                self.io_errors.fetch_add(1, Ordering::Relaxed);
169            }
170            _ => {}
171        }
172    }
173
174    /// Get a snapshot of current statistics
175    pub fn snapshot(&self) -> StatsSnapshot {
176        let total_ops = self.total_gets.load(Ordering::Relaxed)
177            + self.total_inserts.load(Ordering::Relaxed)
178            + self.total_updates.load(Ordering::Relaxed)
179            + self.total_deletes.load(Ordering::Relaxed);
180
181        let avg_get_latency = if self.total_gets.load(Ordering::Relaxed) > 0 {
182            self.get_latency_ns.load(Ordering::Relaxed) / self.total_gets.load(Ordering::Relaxed)
183        } else {
184            0
185        };
186
187        let avg_insert_latency = {
188            let inserts = self.total_inserts.load(Ordering::Relaxed)
189                + self.total_updates.load(Ordering::Relaxed);
190            if inserts > 0 {
191                self.insert_latency_ns.load(Ordering::Relaxed) / inserts
192            } else {
193                0
194            }
195        };
196
197        let avg_delete_latency = if self.total_deletes.load(Ordering::Relaxed) > 0 {
198            self.delete_latency_ns.load(Ordering::Relaxed)
199                / self.total_deletes.load(Ordering::Relaxed)
200        } else {
201            0
202        };
203
204        let cache_hit_rate = {
205            let total_cache_ops =
206                self.cache_hits.load(Ordering::Relaxed) + self.cache_misses.load(Ordering::Relaxed);
207            if total_cache_ops > 0 {
208                (self.cache_hits.load(Ordering::Relaxed) as f64 / total_cache_ops as f64) * 100.0
209            } else {
210                0.0
211            }
212        };
213
214        StatsSnapshot {
215            record_count: self.record_count.load(Ordering::Relaxed),
216            memory_usage: self.memory_usage.load(Ordering::Relaxed),
217            total_operations: total_ops,
218            total_gets: self.total_gets.load(Ordering::Relaxed),
219            total_inserts: self.total_inserts.load(Ordering::Relaxed),
220            total_updates: self.total_updates.load(Ordering::Relaxed),
221            total_deletes: self.total_deletes.load(Ordering::Relaxed),
222            total_range_queries: self.total_range_queries.load(Ordering::Relaxed),
223            avg_get_latency_ns: avg_get_latency,
224            avg_insert_latency_ns: avg_insert_latency,
225            avg_delete_latency_ns: avg_delete_latency,
226            cache_hits: self.cache_hits.load(Ordering::Relaxed),
227            cache_misses: self.cache_misses.load(Ordering::Relaxed),
228            cache_hit_rate,
229            cache_evictions: self.cache_evictions.load(Ordering::Relaxed),
230            cache_memory: self.cache_memory.load(Ordering::Relaxed),
231            writes_buffered: self.writes_buffered.load(Ordering::Relaxed),
232            writes_flushed: self.writes_flushed.load(Ordering::Relaxed),
233            write_failures: self.write_failures.load(Ordering::Relaxed),
234            flush_count: self.flush_count.load(Ordering::Relaxed),
235            disk_reads: self.disk_reads.load(Ordering::Relaxed),
236            disk_writes: self.disk_writes.load(Ordering::Relaxed),
237            disk_bytes_read: self.disk_bytes_read.load(Ordering::Relaxed),
238            disk_bytes_written: self.disk_bytes_written.load(Ordering::Relaxed),
239            key_not_found_errors: self.key_not_found_errors.load(Ordering::Relaxed),
240            out_of_memory_errors: self.out_of_memory_errors.load(Ordering::Relaxed),
241            io_errors: self.io_errors.load(Ordering::Relaxed),
242        }
243    }
244
245    /// Reset all statistics
246    pub fn reset(&self) {
247        self.total_gets.store(0, Ordering::Relaxed);
248        self.total_inserts.store(0, Ordering::Relaxed);
249        self.total_updates.store(0, Ordering::Relaxed);
250        self.total_deletes.store(0, Ordering::Relaxed);
251        self.total_range_queries.store(0, Ordering::Relaxed);
252        self.get_latency_ns.store(0, Ordering::Relaxed);
253        self.insert_latency_ns.store(0, Ordering::Relaxed);
254        self.delete_latency_ns.store(0, Ordering::Relaxed);
255        self.cache_hits.store(0, Ordering::Relaxed);
256        self.cache_misses.store(0, Ordering::Relaxed);
257        self.cache_evictions.store(0, Ordering::Relaxed);
258        self.writes_buffered.store(0, Ordering::Relaxed);
259        self.writes_flushed.store(0, Ordering::Relaxed);
260        self.write_failures.store(0, Ordering::Relaxed);
261        self.flush_count.store(0, Ordering::Relaxed);
262        self.disk_reads.store(0, Ordering::Relaxed);
263        self.disk_writes.store(0, Ordering::Relaxed);
264        self.disk_bytes_read.store(0, Ordering::Relaxed);
265        self.disk_bytes_written.store(0, Ordering::Relaxed);
266        self.key_not_found_errors.store(0, Ordering::Relaxed);
267        self.out_of_memory_errors.store(0, Ordering::Relaxed);
268        self.io_errors.store(0, Ordering::Relaxed);
269    }
270}
271
272impl Default for Statistics {
273    fn default() -> Self {
274        Self::new()
275    }
276}
277
278/// Snapshot of statistics at a point in time
279#[derive(Debug, Clone)]
280pub struct StatsSnapshot {
281    // Store metrics
282    pub record_count: u32,
283    pub memory_usage: usize,
284
285    // Operations
286    pub total_operations: u64,
287    pub total_gets: u64,
288    pub total_inserts: u64,
289    pub total_updates: u64,
290    pub total_deletes: u64,
291    pub total_range_queries: u64,
292
293    // Latencies (nanoseconds)
294    pub avg_get_latency_ns: u64,
295    pub avg_insert_latency_ns: u64,
296    pub avg_delete_latency_ns: u64,
297
298    // Cache
299    pub cache_hits: u64,
300    pub cache_misses: u64,
301    pub cache_hit_rate: f64,
302    pub cache_evictions: u64,
303    pub cache_memory: usize,
304
305    // Write buffer
306    pub writes_buffered: u64,
307    pub writes_flushed: u64,
308    pub write_failures: u64,
309    pub flush_count: u64,
310
311    // Disk I/O
312    pub disk_reads: u64,
313    pub disk_writes: u64,
314    pub disk_bytes_read: u64,
315    pub disk_bytes_written: u64,
316
317    // Errors
318    pub key_not_found_errors: u64,
319    pub out_of_memory_errors: u64,
320    pub io_errors: u64,
321}
322
323impl StatsSnapshot {
324    /// Format statistics as a human-readable string
325    pub fn format(&self) -> String {
326        format!(
327            "=== FeOxDB Statistics ===\n\
328            Store:\n\
329            - Records: {}\n\
330            - Memory: {:.2} MB\n\n\
331            Operations:\n\
332            - Total: {}\n\
333            - Gets: {} (avg latency: {:.2}μs)\n\
334            - Inserts: {} (avg latency: {:.2}μs)\n\
335            - Updates: {}\n\
336            - Deletes: {} (avg latency: {:.2}μs)\n\
337            - Range Queries: {}\n\n\
338            Cache:\n\
339            - Hit Rate: {:.1}%\n\
340            - Hits: {}\n\
341            - Misses: {}\n\
342            - Evictions: {}\n\
343            - Memory: {:.2} MB\n\n\
344            Write Buffer:\n\
345            - Buffered: {}\n\
346            - Flushed: {}\n\
347            - Failures: {}\n\
348            - Flush Count: {}\n\n\
349            Disk I/O:\n\
350            - Reads: {} ({:.2} MB)\n\
351            - Writes: {} ({:.2} MB)\n\n\
352            Errors:\n\
353            - Key Not Found: {}\n\
354            - Out of Memory: {}\n\
355            - I/O Errors: {}",
356            self.record_count,
357            self.memory_usage as f64 / 1_048_576.0,
358            self.total_operations,
359            self.total_gets,
360            self.avg_get_latency_ns as f64 / 1000.0,
361            self.total_inserts,
362            self.avg_insert_latency_ns as f64 / 1000.0,
363            self.total_updates,
364            self.total_deletes,
365            self.avg_delete_latency_ns as f64 / 1000.0,
366            self.total_range_queries,
367            self.cache_hit_rate,
368            self.cache_hits,
369            self.cache_misses,
370            self.cache_evictions,
371            self.cache_memory as f64 / 1_048_576.0,
372            self.writes_buffered,
373            self.writes_flushed,
374            self.write_failures,
375            self.flush_count,
376            self.disk_reads,
377            self.disk_bytes_read as f64 / 1_048_576.0,
378            self.disk_writes,
379            self.disk_bytes_written as f64 / 1_048_576.0,
380            self.key_not_found_errors,
381            self.out_of_memory_errors,
382            self.io_errors
383        )
384    }
385}