1use std::sync::atomic::{AtomicU64, Ordering};
7
8#[derive(Debug, Default)]
21pub struct CacheStats {
22 hits: AtomicU64,
24
25 misses: AtomicU64,
27
28 evictions: AtomicU64,
30
31 expirations: AtomicU64,
33
34 size: AtomicU64,
36
37 sets: AtomicU64,
39
40 deletes: AtomicU64,
42}
43
44impl CacheStats {
45 pub fn new() -> Self {
47 Self::default()
48 }
49
50 pub fn record_hit(&self) {
52 self.hits.fetch_add(1, Ordering::Relaxed);
53 }
54
55 pub fn record_miss(&self) {
57 self.misses.fetch_add(1, Ordering::Relaxed);
58 }
59
60 pub fn record_eviction(&self) {
62 self.evictions.fetch_add(1, Ordering::Relaxed);
63 }
64
65 pub fn record_expiration(&self) {
67 self.expirations.fetch_add(1, Ordering::Relaxed);
68 }
69
70 pub fn record_set(&self) {
72 self.sets.fetch_add(1, Ordering::Relaxed);
73 }
74
75 pub fn record_delete(&self) {
77 self.deletes.fetch_add(1, Ordering::Relaxed);
78 }
79
80 pub fn increment_size(&self) {
82 self.size.fetch_add(1, Ordering::Relaxed);
83 }
84
85 pub fn decrement_size(&self) {
87 self.size.fetch_sub(1, Ordering::Relaxed);
88 }
89
90 pub fn set_size(&self, size: u64) {
92 self.size.store(size, Ordering::Relaxed);
93 }
94
95 pub fn hits(&self) -> u64 {
99 self.hits.load(Ordering::Relaxed)
100 }
101
102 pub fn misses(&self) -> u64 {
104 self.misses.load(Ordering::Relaxed)
105 }
106
107 pub fn evictions(&self) -> u64 {
109 self.evictions.load(Ordering::Relaxed)
110 }
111
112 pub fn expirations(&self) -> u64 {
114 self.expirations.load(Ordering::Relaxed)
115 }
116
117 pub fn size(&self) -> u64 {
119 self.size.load(Ordering::Relaxed)
120 }
121
122 pub fn sets(&self) -> u64 {
124 self.sets.load(Ordering::Relaxed)
125 }
126
127 pub fn deletes(&self) -> u64 {
129 self.deletes.load(Ordering::Relaxed)
130 }
131
132 pub fn hit_rate(&self) -> f64 {
135 let hits = self.hits();
136 let misses = self.misses();
137 let total = hits + misses;
138 if total == 0 {
139 0.0
140 } else {
141 (hits as f64 / total as f64) * 100.0
142 }
143 }
144
145 pub fn snapshot(&self) -> StatsSnapshot {
148 StatsSnapshot {
149 hits: self.hits(),
150 misses: self.misses(),
151 evictions: self.evictions(),
152 expirations: self.expirations(),
153 size: self.size(),
154 sets: self.sets(),
155 deletes: self.deletes(),
156 hit_rate: self.hit_rate(),
157 }
158 }
159}
160
161#[derive(Debug, Clone, PartialEq)]
166pub struct StatsSnapshot {
167 pub hits: u64,
168 pub misses: u64,
169 pub evictions: u64,
170 pub expirations: u64,
171 pub size: u64,
172 pub sets: u64,
173 pub deletes: u64,
174 pub hit_rate: f64,
175}
176
177#[cfg(test)]
178mod tests {
179 use super::*;
180
181 #[test]
182 fn test_initial_stats() {
183 let stats = CacheStats::new();
184 assert_eq!(stats.hits(), 0);
185 assert_eq!(stats.misses(), 0);
186 assert_eq!(stats.size(), 0);
187 }
188
189 #[test]
190 fn test_record_operations() {
191 let stats = CacheStats::new();
192
193 stats.record_hit();
194 stats.record_hit();
195 stats.record_miss();
196
197 assert_eq!(stats.hits(), 2);
198 assert_eq!(stats.misses(), 1);
199 }
200
201 #[test]
202 fn test_hit_rate() {
203 let stats = CacheStats::new();
204
205 assert_eq!(stats.hit_rate(), 0.0);
207
208 stats.record_hit();
210 stats.record_hit();
211 stats.record_hit();
212 stats.record_miss();
213
214 assert!((stats.hit_rate() - 75.0).abs() < 0.01);
215 }
216
217 #[test]
218 fn test_size_tracking() {
219 let stats = CacheStats::new();
220
221 stats.increment_size();
222 stats.increment_size();
223 assert_eq!(stats.size(), 2);
224
225 stats.decrement_size();
226 assert_eq!(stats.size(), 1);
227 }
228
229 #[test]
230 fn test_snapshot() {
231 let stats = CacheStats::new();
232 stats.record_hit();
233 stats.record_set();
234 stats.increment_size();
235
236 let snapshot = stats.snapshot();
237 assert_eq!(snapshot.hits, 1);
238 assert_eq!(snapshot.sets, 1);
239 assert_eq!(snapshot.size, 1);
240 }
241}