Skip to main content

crates_docs/tools/docs/cache/
stats.rs

1//! Cache statistics for document cache
2
3use std::sync::atomic::{AtomicU64, Ordering};
4
5/// Cache statistics tracker
6#[derive(Debug, Default)]
7pub struct CacheStats {
8    /// Total cache hits
9    hits: AtomicU64,
10    /// Total cache misses
11    misses: AtomicU64,
12    /// Total cache sets
13    sets: AtomicU64,
14}
15
16impl CacheStats {
17    /// Create new cache statistics
18    #[must_use]
19    pub fn new() -> Self {
20        Self::default()
21    }
22
23    /// Record a cache hit
24    pub fn record_hit(&self) {
25        self.hits.fetch_add(1, Ordering::Relaxed);
26    }
27
28    /// Record a cache miss
29    pub fn record_miss(&self) {
30        self.misses.fetch_add(1, Ordering::Relaxed);
31    }
32
33    /// Record a cache set operation
34    pub fn record_set(&self) {
35        self.sets.fetch_add(1, Ordering::Relaxed);
36    }
37
38    /// Get total hits
39    #[must_use]
40    pub fn hits(&self) -> u64 {
41        self.hits.load(Ordering::Relaxed)
42    }
43
44    /// Get total misses
45    #[must_use]
46    pub fn misses(&self) -> u64 {
47        self.misses.load(Ordering::Relaxed)
48    }
49
50    /// Get total sets
51    #[must_use]
52    pub fn sets(&self) -> u64 {
53        self.sets.load(Ordering::Relaxed)
54    }
55
56    /// Increment and get current hits count (atomic operation)
57    #[must_use]
58    pub fn inc_hits(&self) -> u64 {
59        self.hits.fetch_add(1, Ordering::Relaxed) + 1
60    }
61
62    /// Increment and get current misses count (atomic operation)
63    #[must_use]
64    pub fn inc_misses(&self) -> u64 {
65        self.misses.fetch_add(1, Ordering::Relaxed) + 1
66    }
67
68    /// Increment and get current sets count (atomic operation)
69    #[must_use]
70    pub fn inc_sets(&self) -> u64 {
71        self.sets.fetch_add(1, Ordering::Relaxed) + 1
72    }
73
74    /// Get total requests (hits + misses)
75    #[must_use]
76    pub fn total_requests(&self) -> u64 {
77        self.hits() + self.misses()
78    }
79
80    /// Calculate hit rate (0.0 to 1.0)
81    #[must_use]
82    #[allow(clippy::cast_precision_loss)]
83    pub fn hit_rate(&self) -> f64 {
84        let total = self.total_requests();
85        if total == 0 {
86            return 0.0;
87        }
88        self.hits() as f64 / total as f64
89    }
90
91    /// Get all stats as a tuple (hits, misses, sets)
92    #[must_use]
93    pub fn as_tuple(&self) -> (u64, u64, u64) {
94        (self.hits(), self.misses(), self.sets())
95    }
96
97    /// Reset all statistics
98    pub fn reset(&self) {
99        self.hits.store(0, Ordering::Relaxed);
100        self.misses.store(0, Ordering::Relaxed);
101        self.sets.store(0, Ordering::Relaxed);
102    }
103}
104
105impl Clone for CacheStats {
106    fn clone(&self) -> Self {
107        Self {
108            hits: AtomicU64::new(self.hits.load(Ordering::Relaxed)),
109            misses: AtomicU64::new(self.misses.load(Ordering::Relaxed)),
110            sets: AtomicU64::new(self.sets.load(Ordering::Relaxed)),
111        }
112    }
113}
114
115#[cfg(test)]
116mod tests {
117    use super::*;
118
119    #[test]
120    fn test_cache_stats_new() {
121        let stats = CacheStats::new();
122        assert_eq!(stats.hits(), 0);
123        assert_eq!(stats.misses(), 0);
124        assert_eq!(stats.sets(), 0);
125    }
126
127    #[test]
128    fn test_cache_stats_record() {
129        let stats = CacheStats::new();
130
131        stats.record_hit();
132        stats.record_hit();
133        stats.record_miss();
134        stats.record_set();
135        stats.record_set();
136        stats.record_set();
137
138        assert_eq!(stats.hits(), 2);
139        assert_eq!(stats.misses(), 1);
140        assert_eq!(stats.sets(), 3);
141    }
142
143    #[test]
144    fn test_cache_stats_hit_rate() {
145        let stats = CacheStats::new();
146
147        assert!((stats.hit_rate() - 0.0).abs() < f64::EPSILON);
148
149        stats.record_hit();
150        stats.record_hit();
151        stats.record_miss();
152
153        let rate = stats.hit_rate();
154        assert!((rate - 0.666_666_666_666_666_6).abs() < f64::EPSILON);
155    }
156
157    #[test]
158    fn test_cache_stats_total_requests() {
159        let stats = CacheStats::new();
160
161        stats.record_hit();
162        stats.record_hit();
163        stats.record_miss();
164        stats.record_miss();
165
166        assert_eq!(stats.total_requests(), 4);
167    }
168
169    #[test]
170    fn test_cache_stats_reset() {
171        let stats = CacheStats::new();
172
173        stats.record_hit();
174        stats.record_miss();
175        stats.record_set();
176
177        stats.reset();
178
179        assert_eq!(stats.hits(), 0);
180        assert_eq!(stats.misses(), 0);
181        assert_eq!(stats.sets(), 0);
182    }
183
184    #[test]
185    fn test_cache_stats_clone() {
186        let stats = CacheStats::new();
187        stats.record_hit();
188        stats.record_miss();
189
190        let cloned = stats.clone();
191
192        assert_eq!(cloned.hits(), 1);
193        assert_eq!(cloned.misses(), 1);
194    }
195}