1use std::sync::atomic::{AtomicU64, Ordering};
8use std::sync::Arc;
9
10use crate::Cache;
11
12#[derive(Debug, Default)]
17pub struct CacheStats {
18 hits: AtomicU64,
19 misses: AtomicU64,
20}
21
22impl CacheStats {
23 #[must_use]
25 pub fn new() -> Self {
26 CacheStats::default()
27 }
28
29 pub fn record_hit(&self) {
31 self.hits.fetch_add(1, Ordering::Relaxed);
32 }
33
34 pub fn record_miss(&self) {
36 self.misses.fetch_add(1, Ordering::Relaxed);
37 }
38
39 #[must_use]
41 pub fn hits(&self) -> u64 {
42 self.hits.load(Ordering::Relaxed)
43 }
44
45 #[must_use]
47 pub fn misses(&self) -> u64 {
48 self.misses.load(Ordering::Relaxed)
49 }
50
51 #[must_use]
55 pub fn hit_rate(&self) -> f64 {
56 let h = self.hits();
57 let m = self.misses();
58 let total = h + m;
59 if total == 0 {
60 0.0
61 } else {
62 h as f64 / total as f64
63 }
64 }
65
66 pub fn reset(&self) {
68 self.hits.store(0, Ordering::Relaxed);
69 self.misses.store(0, Ordering::Relaxed);
70 }
71}
72
73pub struct StatsCache<C> {
83 inner: C,
84 stats: Arc<CacheStats>,
85}
86
87impl<C> StatsCache<C>
88where
89 C: Cache<Vec<u8>, Vec<u8>>,
90{
91 pub fn new(inner: C) -> Self {
93 StatsCache {
94 inner,
95 stats: Arc::new(CacheStats::new()),
96 }
97 }
98
99 pub fn with_stats(inner: C, stats: Arc<CacheStats>) -> Self {
101 StatsCache { inner, stats }
102 }
103
104 #[must_use]
106 pub fn stats(&self) -> &Arc<CacheStats> {
107 &self.stats
108 }
109}
110
111impl<C> Cache<Vec<u8>, Vec<u8>> for StatsCache<C>
112where
113 C: Cache<Vec<u8>, Vec<u8>>,
114{
115 fn get(&mut self, key: &Vec<u8>) -> Option<&Vec<u8>> {
116 let result = self.inner.get(key);
117 if result.is_some() {
118 self.stats.record_hit();
119 } else {
120 self.stats.record_miss();
121 }
122 result
123 }
124
125 fn put(&mut self, key: Vec<u8>, value: Vec<u8>) -> Option<Vec<u8>> {
126 self.inner.put(key, value)
127 }
128
129 fn put_with_ttl(
130 &mut self,
131 key: Vec<u8>,
132 value: Vec<u8>,
133 ttl: std::time::Duration,
134 ) -> Option<Vec<u8>> {
135 self.inner.put_with_ttl(key, value, ttl)
136 }
137
138 fn len(&self) -> usize {
139 self.inner.len()
140 }
141
142 fn cap(&self) -> usize {
143 self.inner.cap()
144 }
145
146 fn remove(&mut self, key: &Vec<u8>) -> Option<Vec<u8>> {
147 self.inner.remove(key)
148 }
149
150 fn clear(&mut self) {
151 self.inner.clear();
152 }
153
154 fn peek(&self, key: &Vec<u8>) -> Option<&Vec<u8>> {
155 self.inner.peek(key)
156 }
157
158 fn contains_key(&self, key: &Vec<u8>) -> bool {
159 self.inner.contains_key(key)
160 }
161
162 fn resize(&mut self, new_cap: usize) {
163 self.inner.resize(new_cap);
164 }
165}
166
167#[cfg(test)]
168mod tests {
169 use super::*;
170 use crate::LruCache;
171
172 #[test]
173 fn stats_initial_state() {
174 let stats = CacheStats::new();
175 assert_eq!(stats.hits(), 0);
176 assert_eq!(stats.misses(), 0);
177 assert!((stats.hit_rate() - 0.0).abs() < f64::EPSILON);
178 }
179
180 #[test]
181 fn stats_record_hit_miss() {
182 let stats = CacheStats::new();
183 stats.record_hit();
184 stats.record_hit();
185 stats.record_miss();
186 assert_eq!(stats.hits(), 2);
187 assert_eq!(stats.misses(), 1);
188 let rate = stats.hit_rate();
189 assert!((rate - 2.0 / 3.0).abs() < 1e-10);
190 }
191
192 #[test]
193 fn stats_reset() {
194 let stats = CacheStats::new();
195 stats.record_hit();
196 stats.record_miss();
197 stats.reset();
198 assert_eq!(stats.hits(), 0);
199 assert_eq!(stats.misses(), 0);
200 }
201
202 #[test]
203 fn stats_cache_hit_and_miss() {
204 let inner = LruCache::<Vec<u8>, Vec<u8>>::new(4);
205 let mut cache = StatsCache::new(inner);
206
207 cache.put(b"hello".to_vec(), b"world".to_vec());
208
209 let v = cache.get(&b"hello".to_vec());
211 assert_eq!(v, Some(&b"world".to_vec()));
212 assert_eq!(cache.stats().hits(), 1);
213 assert_eq!(cache.stats().misses(), 0);
214
215 let v = cache.get(&b"missing".to_vec());
217 assert!(v.is_none());
218 assert_eq!(cache.stats().hits(), 1);
219 assert_eq!(cache.stats().misses(), 1);
220
221 let rate = cache.stats().hit_rate();
222 assert!((rate - 0.5).abs() < 1e-10);
223 }
224
225 #[test]
226 fn stats_cache_delegates_put_remove_clear() {
227 let inner = LruCache::<Vec<u8>, Vec<u8>>::new(4);
228 let mut cache = StatsCache::new(inner);
229
230 cache.put(b"k".to_vec(), b"v".to_vec());
231 assert_eq!(cache.len(), 1);
232
233 cache.remove(&b"k".to_vec());
234 assert_eq!(cache.len(), 0);
235
236 cache.put(b"a".to_vec(), b"1".to_vec());
237 cache.put(b"b".to_vec(), b"2".to_vec());
238 cache.clear();
239 assert!(cache.is_empty());
240 }
241}