Skip to main content

rez_next_cache/
cache_stats.rs

1//! Unified cache statistics and monitoring
2//!
3//! This module provides comprehensive statistics collection and monitoring
4//! for the intelligent caching system, integrating with existing cache
5//! statistics while adding new metrics for multi-level caching and
6//! predictive preheating.
7
8use serde::{Deserialize, Serialize};
9use std::time::{SystemTime, UNIX_EPOCH};
10
11/// Unified cache statistics
12///
13/// Aggregates statistics from all cache levels and intelligent features
14#[derive(Debug, Clone, Serialize, Deserialize)]
15pub struct UnifiedCacheStats {
16    /// L1 cache statistics
17    pub l1_stats: CacheLevelStats,
18    /// L2 cache statistics
19    pub l2_stats: CacheLevelStats,
20    /// Overall cache statistics
21    pub overall_stats: OverallCacheStats,
22    /// Predictive preheating statistics
23    pub preheating_stats: CachePreheatingStats,
24    /// Adaptive tuning statistics
25    pub tuning_stats: TuningStats,
26    /// Performance metrics
27    pub performance_metrics: PerformanceMetrics,
28    /// Statistics collection timestamp
29    pub timestamp: u64,
30}
31
32impl Default for UnifiedCacheStats {
33    fn default() -> Self {
34        Self {
35            l1_stats: CacheLevelStats::default(),
36            l2_stats: CacheLevelStats::default(),
37            overall_stats: OverallCacheStats::default(),
38            preheating_stats: CachePreheatingStats::default(),
39            tuning_stats: TuningStats::default(),
40            performance_metrics: PerformanceMetrics::default(),
41            timestamp: SystemTime::now()
42                .duration_since(UNIX_EPOCH)
43                .map(|d| d.as_secs())
44                .unwrap_or(0),
45        }
46    }
47}
48
49/// Statistics for a single cache level (L1, L2, etc.)
50#[derive(Debug, Clone, Serialize, Deserialize)]
51pub struct CacheLevelStats {
52    /// Total cache hits
53    pub hits: u64,
54    /// Total cache misses
55    pub misses: u64,
56    /// Current number of entries
57    pub entries: usize,
58    /// Maximum capacity
59    pub capacity: usize,
60    /// Estimated memory/disk usage in bytes
61    pub usage_bytes: u64,
62    /// Maximum allowed usage in bytes
63    pub max_usage_bytes: u64,
64    /// Cache evictions
65    pub evictions: u64,
66    /// Hit rate (0.0 to 1.0)
67    pub hit_rate: f64,
68    /// Load factor (entries / capacity)
69    pub load_factor: f64,
70    /// Average entry size in bytes
71    pub avg_entry_size: f64,
72}
73
74impl Default for CacheLevelStats {
75    fn default() -> Self {
76        Self {
77            hits: 0,
78            misses: 0,
79            entries: 0,
80            capacity: 0,
81            usage_bytes: 0,
82            max_usage_bytes: 0,
83            evictions: 0,
84            hit_rate: 0.0,
85            load_factor: 0.0,
86            avg_entry_size: 0.0,
87        }
88    }
89}
90
91impl CacheLevelStats {
92    /// Update hit rate based on current hits and misses
93    pub fn update_hit_rate(&mut self) {
94        let total_requests = self.hits + self.misses;
95        if total_requests > 0 {
96            self.hit_rate = self.hits as f64 / total_requests as f64;
97        }
98    }
99
100    /// Update load factor based on current entries and capacity
101    pub fn update_load_factor(&mut self) {
102        if self.capacity > 0 {
103            self.load_factor = self.entries as f64 / self.capacity as f64;
104        }
105    }
106
107    /// Update average entry size
108    pub fn update_avg_entry_size(&mut self) {
109        if self.entries > 0 {
110            self.avg_entry_size = self.usage_bytes as f64 / self.entries as f64;
111        }
112    }
113
114    /// Update all calculated fields
115    pub fn update_calculated_fields(&mut self) {
116        self.update_hit_rate();
117        self.update_load_factor();
118        self.update_avg_entry_size();
119    }
120}
121
122/// Overall cache system statistics
123#[derive(Debug, Clone, Serialize, Deserialize)]
124pub struct OverallCacheStats {
125    /// Total hits across all cache levels
126    pub total_hits: u64,
127    /// Total misses across all cache levels
128    pub total_misses: u64,
129    /// Overall hit rate
130    pub overall_hit_rate: f64,
131    /// Total entries across all levels
132    pub total_entries: usize,
133    /// Total memory usage across all levels
134    pub total_memory_bytes: u64,
135    /// Total disk usage
136    pub total_disk_bytes: u64,
137    /// Cache efficiency score (0.0 to 1.0)
138    pub efficiency_score: f64,
139    /// Data promotion count (L2 to L1)
140    pub promotions: u64,
141    /// Data demotion count (L1 to L2)
142    pub demotions: u64,
143}
144
145impl Default for OverallCacheStats {
146    fn default() -> Self {
147        Self {
148            total_hits: 0,
149            total_misses: 0,
150            overall_hit_rate: 0.0,
151            total_entries: 0,
152            total_memory_bytes: 0,
153            total_disk_bytes: 0,
154            efficiency_score: 0.0,
155            promotions: 0,
156            demotions: 0,
157        }
158    }
159}
160
161/// Predictive preheating statistics (cache level)
162#[derive(Debug, Clone, Serialize, Deserialize)]
163pub struct CachePreheatingStats {
164    /// Total preheating attempts
165    pub preheat_attempts: u64,
166    /// Successful preheating operations
167    pub preheat_successes: u64,
168    /// Preheating hit rate (preheated entries that were accessed)
169    pub preheat_hit_rate: f64,
170    /// Total entries preheated
171    pub entries_preheated: u64,
172    /// Entries preheated that were actually accessed
173    pub preheated_entries_accessed: u64,
174    /// Average prediction confidence
175    pub avg_prediction_confidence: f64,
176    /// Time saved by preheating (in milliseconds)
177    pub time_saved_ms: u64,
178    /// CPU time used for preheating (in milliseconds)
179    pub cpu_time_used_ms: u64,
180}
181
182impl Default for CachePreheatingStats {
183    fn default() -> Self {
184        Self {
185            preheat_attempts: 0,
186            preheat_successes: 0,
187            preheat_hit_rate: 0.0,
188            entries_preheated: 0,
189            preheated_entries_accessed: 0,
190            avg_prediction_confidence: 0.0,
191            time_saved_ms: 0,
192            cpu_time_used_ms: 0,
193        }
194    }
195}
196
197/// Adaptive tuning statistics
198#[derive(Debug, Clone, Serialize, Deserialize)]
199pub struct TuningStats {
200    /// Total tuning cycles performed
201    pub tuning_cycles: u64,
202    /// Successful tuning adjustments
203    pub successful_adjustments: u64,
204    /// TTL adjustments made
205    pub ttl_adjustments: u64,
206    /// Capacity adjustments made
207    pub capacity_adjustments: u64,
208    /// Eviction strategy changes
209    pub eviction_strategy_changes: u64,
210    /// Performance improvement from tuning (percentage)
211    pub performance_improvement: f64,
212    /// Last tuning timestamp
213    pub last_tuning_timestamp: u64,
214    /// Current tuning stability score (0.0 to 1.0)
215    pub stability_score: f64,
216}
217
218impl Default for TuningStats {
219    fn default() -> Self {
220        Self {
221            tuning_cycles: 0,
222            successful_adjustments: 0,
223            ttl_adjustments: 0,
224            capacity_adjustments: 0,
225            eviction_strategy_changes: 0,
226            performance_improvement: 0.0,
227            last_tuning_timestamp: 0,
228            stability_score: 1.0,
229        }
230    }
231}
232
233/// Performance metrics for cache operations
234#[derive(Debug, Clone, Serialize, Deserialize)]
235pub struct PerformanceMetrics {
236    /// Average get operation latency (in microseconds)
237    pub avg_get_latency_us: f64,
238    /// Average put operation latency (in microseconds)
239    pub avg_put_latency_us: f64,
240    /// Average eviction latency (in microseconds)
241    pub avg_eviction_latency_us: f64,
242    /// Operations per second
243    pub ops_per_second: f64,
244    /// Memory allocation rate (bytes per second)
245    pub memory_allocation_rate: f64,
246    /// Disk I/O rate (bytes per second)
247    pub disk_io_rate: f64,
248    /// CPU usage percentage for cache operations
249    pub cpu_usage_percent: f64,
250    /// Peak memory usage (in bytes)
251    pub peak_memory_usage: u64,
252}
253
254impl Default for PerformanceMetrics {
255    fn default() -> Self {
256        Self {
257            avg_get_latency_us: 0.0,
258            avg_put_latency_us: 0.0,
259            avg_eviction_latency_us: 0.0,
260            ops_per_second: 0.0,
261            memory_allocation_rate: 0.0,
262            disk_io_rate: 0.0,
263            cpu_usage_percent: 0.0,
264            peak_memory_usage: 0,
265        }
266    }
267}
268
269impl UnifiedCacheStats {
270    /// Create a new statistics instance with current timestamp
271    pub fn new() -> Self {
272        Self::default()
273    }
274
275    /// Update overall statistics from individual cache level stats
276    pub fn update_overall_stats(&mut self) {
277        self.overall_stats.total_hits = self.l1_stats.hits + self.l2_stats.hits;
278        self.overall_stats.total_misses = self.l1_stats.misses + self.l2_stats.misses;
279
280        let total_requests = self.overall_stats.total_hits + self.overall_stats.total_misses;
281        if total_requests > 0 {
282            self.overall_stats.overall_hit_rate =
283                self.overall_stats.total_hits as f64 / total_requests as f64;
284        }
285
286        self.overall_stats.total_entries = self.l1_stats.entries + self.l2_stats.entries;
287        self.overall_stats.total_memory_bytes = self.l1_stats.usage_bytes;
288        self.overall_stats.total_disk_bytes = self.l2_stats.usage_bytes;
289
290        // Calculate efficiency score based on hit rate and resource usage
291        let hit_rate_score = self.overall_stats.overall_hit_rate;
292        let memory_efficiency = if self.l1_stats.max_usage_bytes > 0 {
293            1.0 - (self.l1_stats.usage_bytes as f64 / self.l1_stats.max_usage_bytes as f64)
294        } else {
295            1.0
296        };
297        self.overall_stats.efficiency_score = (hit_rate_score + memory_efficiency) / 2.0;
298
299        // Update timestamp
300        self.timestamp = SystemTime::now()
301            .duration_since(UNIX_EPOCH)
302            .map(|d| d.as_secs())
303            .unwrap_or(0);
304    }
305
306    /// Get a summary report as a formatted string
307    pub fn summary_report(&self) -> String {
308        format!(
309            "Cache Statistics Summary:\n\
310             Overall Hit Rate: {:.2}%\n\
311             Total Entries: {}\n\
312             Memory Usage: {:.2} MB\n\
313             Disk Usage: {:.2} MB\n\
314             Efficiency Score: {:.2}\n\
315             Preheating Hit Rate: {:.2}%\n\
316             Tuning Cycles: {}",
317            self.overall_stats.overall_hit_rate * 100.0,
318            self.overall_stats.total_entries,
319            self.overall_stats.total_memory_bytes as f64 / (1024.0 * 1024.0),
320            self.overall_stats.total_disk_bytes as f64 / (1024.0 * 1024.0),
321            self.overall_stats.efficiency_score,
322            self.preheating_stats.preheat_hit_rate * 100.0,
323            self.tuning_stats.tuning_cycles
324        )
325    }
326
327    /// Check if the cache is performing well based on target metrics
328    pub fn is_performing_well(&self, target_hit_rate: f64) -> bool {
329        self.overall_stats.overall_hit_rate >= target_hit_rate
330            && self.overall_stats.efficiency_score >= 0.7
331            && self.tuning_stats.stability_score >= 0.8
332    }
333}
334
335#[cfg(test)]
336mod tests {
337    use super::*;
338
339    #[test]
340    fn test_cache_level_stats() {
341        let mut stats = CacheLevelStats {
342            hits: 80,
343            misses: 20,
344            entries: 100,
345            capacity: 200,
346            usage_bytes: 1024,
347            ..Default::default()
348        };
349
350        stats.update_calculated_fields();
351
352        assert_eq!(stats.hit_rate, 0.8);
353        assert_eq!(stats.load_factor, 0.5);
354        assert_eq!(stats.avg_entry_size, 10.24);
355    }
356
357    #[test]
358    fn test_unified_cache_stats() {
359        let mut stats = UnifiedCacheStats::new();
360        stats.l1_stats.hits = 70;
361        stats.l1_stats.misses = 10;
362        stats.l2_stats.hits = 20;
363        stats.l2_stats.misses = 5;
364
365        stats.update_overall_stats();
366
367        assert_eq!(stats.overall_stats.total_hits, 90);
368        assert_eq!(stats.overall_stats.total_misses, 15);
369        assert!((stats.overall_stats.overall_hit_rate - 0.857).abs() < 0.01);
370    }
371
372    #[test]
373    fn test_performance_check() {
374        let mut stats = UnifiedCacheStats::new();
375        stats.overall_stats.overall_hit_rate = 0.95;
376        stats.overall_stats.efficiency_score = 0.8;
377        stats.tuning_stats.stability_score = 0.9;
378
379        assert!(stats.is_performing_well(0.9));
380        assert!(!stats.is_performing_well(0.96));
381    }
382}