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 pub ttl_expired_lazy: AtomicU64, pub ttl_expired_active: AtomicU64, pub ttl_cleaner_runs: AtomicU64, pub keys_with_ttl: AtomicU64, }
52
53impl Statistics {
54 pub fn new() -> Self {
55 Self {
56 record_count: AtomicU32::new(0),
58 memory_usage: AtomicUsize::new(0),
59 disk_usage: AtomicU64::new(0),
60
61 total_gets: AtomicU64::new(0),
63 total_inserts: AtomicU64::new(0),
64 total_updates: AtomicU64::new(0),
65 total_deletes: AtomicU64::new(0),
66 total_range_queries: AtomicU64::new(0),
67
68 get_latency_ns: AtomicU64::new(0),
70 insert_latency_ns: AtomicU64::new(0),
71 delete_latency_ns: AtomicU64::new(0),
72
73 cache_hits: AtomicU64::new(0),
75 cache_misses: AtomicU64::new(0),
76 cache_evictions: AtomicU64::new(0),
77 cache_memory: AtomicUsize::new(0),
78
79 writes_buffered: AtomicU64::new(0),
81 writes_flushed: AtomicU64::new(0),
82 write_failures: AtomicU64::new(0),
83 flush_count: AtomicU64::new(0),
84
85 disk_reads: AtomicU64::new(0),
87 disk_writes: AtomicU64::new(0),
88 disk_bytes_read: AtomicU64::new(0),
89 disk_bytes_written: AtomicU64::new(0),
90
91 key_not_found_errors: AtomicU64::new(0),
93 out_of_memory_errors: AtomicU64::new(0),
94 io_errors: AtomicU64::new(0),
95
96 ttl_expired_lazy: AtomicU64::new(0),
98 ttl_expired_active: AtomicU64::new(0),
99 ttl_cleaner_runs: AtomicU64::new(0),
100 keys_with_ttl: AtomicU64::new(0),
101 }
102 }
103
104 pub fn record_get(&self, latency_ns: u64, hit: bool) {
106 self.total_gets.fetch_add(1, Ordering::Relaxed);
107 self.get_latency_ns.fetch_add(latency_ns, Ordering::Relaxed);
108
109 if hit {
110 self.cache_hits.fetch_add(1, Ordering::Relaxed);
111 } else {
112 self.cache_misses.fetch_add(1, Ordering::Relaxed);
113 }
114 }
115
116 pub fn record_insert(&self, latency_ns: u64, is_update: bool) {
118 if is_update {
119 self.total_updates.fetch_add(1, Ordering::Relaxed);
120 } else {
121 self.total_inserts.fetch_add(1, Ordering::Relaxed);
122 }
123 self.insert_latency_ns
124 .fetch_add(latency_ns, Ordering::Relaxed);
125 }
126
127 pub fn record_delete(&self, latency_ns: u64) {
129 self.total_deletes.fetch_add(1, Ordering::Relaxed);
130 self.delete_latency_ns
131 .fetch_add(latency_ns, Ordering::Relaxed);
132 }
133
134 pub fn record_range_query(&self) {
136 self.total_range_queries.fetch_add(1, Ordering::Relaxed);
137 }
138
139 pub fn record_eviction(&self, count: u64) {
141 self.cache_evictions.fetch_add(count, Ordering::Relaxed);
142 }
143
144 pub fn record_write_buffered(&self) {
146 self.writes_buffered.fetch_add(1, Ordering::Relaxed);
147 }
148
149 pub fn record_write_flushed(&self, count: u64) {
150 self.writes_flushed.fetch_add(count, Ordering::Relaxed);
151 }
152
153 pub fn record_write_failed(&self) {
154 self.write_failures.fetch_add(1, Ordering::Relaxed);
155 }
156
157 pub fn record_disk_read(&self, bytes: u64) {
159 self.disk_reads.fetch_add(1, Ordering::Relaxed);
160 self.disk_bytes_read.fetch_add(bytes, Ordering::Relaxed);
161 }
162
163 pub fn record_disk_write(&self, bytes: u64) {
164 self.disk_writes.fetch_add(1, Ordering::Relaxed);
165 self.disk_bytes_written.fetch_add(bytes, Ordering::Relaxed);
166 }
167
168 pub fn record_error(&self, error: &crate::error::FeoxError) {
170 use crate::error::FeoxError;
171 match error {
172 FeoxError::KeyNotFound => {
173 self.key_not_found_errors.fetch_add(1, Ordering::Relaxed);
174 }
175 FeoxError::OutOfMemory => {
176 self.out_of_memory_errors.fetch_add(1, Ordering::Relaxed);
177 }
178 FeoxError::IoError(_) => {
179 self.io_errors.fetch_add(1, Ordering::Relaxed);
180 }
181 _ => {}
182 }
183 }
184
185 pub fn snapshot(&self) -> StatsSnapshot {
187 let total_ops = self.total_gets.load(Ordering::Relaxed)
188 + self.total_inserts.load(Ordering::Relaxed)
189 + self.total_updates.load(Ordering::Relaxed)
190 + self.total_deletes.load(Ordering::Relaxed);
191
192 let avg_get_latency = if self.total_gets.load(Ordering::Relaxed) > 0 {
193 self.get_latency_ns.load(Ordering::Relaxed) / self.total_gets.load(Ordering::Relaxed)
194 } else {
195 0
196 };
197
198 let avg_insert_latency = {
199 let inserts = self.total_inserts.load(Ordering::Relaxed)
200 + self.total_updates.load(Ordering::Relaxed);
201 if inserts > 0 {
202 self.insert_latency_ns.load(Ordering::Relaxed) / inserts
203 } else {
204 0
205 }
206 };
207
208 let avg_delete_latency = if self.total_deletes.load(Ordering::Relaxed) > 0 {
209 self.delete_latency_ns.load(Ordering::Relaxed)
210 / self.total_deletes.load(Ordering::Relaxed)
211 } else {
212 0
213 };
214
215 let cache_hit_rate = {
216 let total_cache_ops =
217 self.cache_hits.load(Ordering::Relaxed) + self.cache_misses.load(Ordering::Relaxed);
218 if total_cache_ops > 0 {
219 (self.cache_hits.load(Ordering::Relaxed) as f64 / total_cache_ops as f64) * 100.0
220 } else {
221 0.0
222 }
223 };
224
225 StatsSnapshot {
226 record_count: self.record_count.load(Ordering::Relaxed),
227 memory_usage: self.memory_usage.load(Ordering::Relaxed),
228 total_operations: total_ops,
229 total_gets: self.total_gets.load(Ordering::Relaxed),
230 total_inserts: self.total_inserts.load(Ordering::Relaxed),
231 total_updates: self.total_updates.load(Ordering::Relaxed),
232 total_deletes: self.total_deletes.load(Ordering::Relaxed),
233 total_range_queries: self.total_range_queries.load(Ordering::Relaxed),
234 avg_get_latency_ns: avg_get_latency,
235 avg_insert_latency_ns: avg_insert_latency,
236 avg_delete_latency_ns: avg_delete_latency,
237 cache_hits: self.cache_hits.load(Ordering::Relaxed),
238 cache_misses: self.cache_misses.load(Ordering::Relaxed),
239 cache_hit_rate,
240 cache_evictions: self.cache_evictions.load(Ordering::Relaxed),
241 cache_memory: self.cache_memory.load(Ordering::Relaxed),
242 writes_buffered: self.writes_buffered.load(Ordering::Relaxed),
243 writes_flushed: self.writes_flushed.load(Ordering::Relaxed),
244 write_failures: self.write_failures.load(Ordering::Relaxed),
245 flush_count: self.flush_count.load(Ordering::Relaxed),
246 disk_reads: self.disk_reads.load(Ordering::Relaxed),
247 disk_writes: self.disk_writes.load(Ordering::Relaxed),
248 disk_bytes_read: self.disk_bytes_read.load(Ordering::Relaxed),
249 disk_bytes_written: self.disk_bytes_written.load(Ordering::Relaxed),
250 key_not_found_errors: self.key_not_found_errors.load(Ordering::Relaxed),
251 out_of_memory_errors: self.out_of_memory_errors.load(Ordering::Relaxed),
252 io_errors: self.io_errors.load(Ordering::Relaxed),
253 }
254 }
255
256 pub fn reset(&self) {
258 self.total_gets.store(0, Ordering::Relaxed);
259 self.total_inserts.store(0, Ordering::Relaxed);
260 self.total_updates.store(0, Ordering::Relaxed);
261 self.total_deletes.store(0, Ordering::Relaxed);
262 self.total_range_queries.store(0, Ordering::Relaxed);
263 self.get_latency_ns.store(0, Ordering::Relaxed);
264 self.insert_latency_ns.store(0, Ordering::Relaxed);
265 self.delete_latency_ns.store(0, Ordering::Relaxed);
266 self.cache_hits.store(0, Ordering::Relaxed);
267 self.cache_misses.store(0, Ordering::Relaxed);
268 self.cache_evictions.store(0, Ordering::Relaxed);
269 self.writes_buffered.store(0, Ordering::Relaxed);
270 self.writes_flushed.store(0, Ordering::Relaxed);
271 self.write_failures.store(0, Ordering::Relaxed);
272 self.flush_count.store(0, Ordering::Relaxed);
273 self.disk_reads.store(0, Ordering::Relaxed);
274 self.disk_writes.store(0, Ordering::Relaxed);
275 self.disk_bytes_read.store(0, Ordering::Relaxed);
276 self.disk_bytes_written.store(0, Ordering::Relaxed);
277 self.key_not_found_errors.store(0, Ordering::Relaxed);
278 self.out_of_memory_errors.store(0, Ordering::Relaxed);
279 self.io_errors.store(0, Ordering::Relaxed);
280 }
281}
282
283impl Default for Statistics {
284 fn default() -> Self {
285 Self::new()
286 }
287}
288
289#[derive(Debug, Clone)]
291pub struct StatsSnapshot {
292 pub record_count: u32,
294 pub memory_usage: usize,
295
296 pub total_operations: u64,
298 pub total_gets: u64,
299 pub total_inserts: u64,
300 pub total_updates: u64,
301 pub total_deletes: u64,
302 pub total_range_queries: u64,
303
304 pub avg_get_latency_ns: u64,
306 pub avg_insert_latency_ns: u64,
307 pub avg_delete_latency_ns: u64,
308
309 pub cache_hits: u64,
311 pub cache_misses: u64,
312 pub cache_hit_rate: f64,
313 pub cache_evictions: u64,
314 pub cache_memory: usize,
315
316 pub writes_buffered: u64,
318 pub writes_flushed: u64,
319 pub write_failures: u64,
320 pub flush_count: u64,
321
322 pub disk_reads: u64,
324 pub disk_writes: u64,
325 pub disk_bytes_read: u64,
326 pub disk_bytes_written: u64,
327
328 pub key_not_found_errors: u64,
330 pub out_of_memory_errors: u64,
331 pub io_errors: u64,
332}
333
334impl StatsSnapshot {
335 pub fn format(&self) -> String {
337 format!(
338 "=== FeOxDB Statistics ===\n\
339 Store:\n\
340 - Records: {}\n\
341 - Memory: {:.2} MB\n\n\
342 Operations:\n\
343 - Total: {}\n\
344 - Gets: {} (avg latency: {:.2}μs)\n\
345 - Inserts: {} (avg latency: {:.2}μs)\n\
346 - Updates: {}\n\
347 - Deletes: {} (avg latency: {:.2}μs)\n\
348 - Range Queries: {}\n\n\
349 Cache:\n\
350 - Hit Rate: {:.1}%\n\
351 - Hits: {}\n\
352 - Misses: {}\n\
353 - Evictions: {}\n\
354 - Memory: {:.2} MB\n\n\
355 Write Buffer:\n\
356 - Buffered: {}\n\
357 - Flushed: {}\n\
358 - Failures: {}\n\
359 - Flush Count: {}\n\n\
360 Disk I/O:\n\
361 - Reads: {} ({:.2} MB)\n\
362 - Writes: {} ({:.2} MB)\n\n\
363 Errors:\n\
364 - Key Not Found: {}\n\
365 - Out of Memory: {}\n\
366 - I/O Errors: {}",
367 self.record_count,
368 self.memory_usage as f64 / 1_048_576.0,
369 self.total_operations,
370 self.total_gets,
371 self.avg_get_latency_ns as f64 / 1000.0,
372 self.total_inserts,
373 self.avg_insert_latency_ns as f64 / 1000.0,
374 self.total_updates,
375 self.total_deletes,
376 self.avg_delete_latency_ns as f64 / 1000.0,
377 self.total_range_queries,
378 self.cache_hit_rate,
379 self.cache_hits,
380 self.cache_misses,
381 self.cache_evictions,
382 self.cache_memory as f64 / 1_048_576.0,
383 self.writes_buffered,
384 self.writes_flushed,
385 self.write_failures,
386 self.flush_count,
387 self.disk_reads,
388 self.disk_bytes_read as f64 / 1_048_576.0,
389 self.disk_writes,
390 self.disk_bytes_written as f64 / 1_048_576.0,
391 self.key_not_found_errors,
392 self.out_of_memory_errors,
393 self.io_errors
394 )
395 }
396}