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 flush_count: AtomicU64,
34
35 pub disk_reads: AtomicU64,
37 pub disk_writes: AtomicU64,
38 pub disk_bytes_read: AtomicU64,
39 pub disk_bytes_written: AtomicU64,
40
41 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 record_count: AtomicU32::new(0),
52 memory_usage: AtomicUsize::new(0),
53 disk_usage: AtomicU64::new(0),
54
55 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 get_latency_ns: AtomicU64::new(0),
64 insert_latency_ns: AtomicU64::new(0),
65 delete_latency_ns: AtomicU64::new(0),
66
67 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 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_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 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 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 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 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 pub fn record_range_query(&self) {
124 self.total_range_queries.fetch_add(1, Ordering::Relaxed);
125 }
126
127 pub fn record_eviction(&self, count: u64) {
129 self.cache_evictions.fetch_add(count, Ordering::Relaxed);
130 }
131
132 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 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 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 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 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#[derive(Debug, Clone)]
280pub struct StatsSnapshot {
281 pub record_count: u32,
283 pub memory_usage: usize,
284
285 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 pub avg_get_latency_ns: u64,
295 pub avg_insert_latency_ns: u64,
296 pub avg_delete_latency_ns: u64,
297
298 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 pub writes_buffered: u64,
307 pub writes_flushed: u64,
308 pub write_failures: u64,
309 pub flush_count: u64,
310
311 pub disk_reads: u64,
313 pub disk_writes: u64,
314 pub disk_bytes_read: u64,
315 pub disk_bytes_written: u64,
316
317 pub key_not_found_errors: u64,
319 pub out_of_memory_errors: u64,
320 pub io_errors: u64,
321}
322
323impl StatsSnapshot {
324 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}