use serde::{Deserialize, Serialize};
use std::time::{SystemTime, UNIX_EPOCH};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UnifiedCacheStats {
pub l1_stats: CacheLevelStats,
pub l2_stats: CacheLevelStats,
pub overall_stats: OverallCacheStats,
pub preheating_stats: CachePreheatingStats,
pub tuning_stats: TuningStats,
pub performance_metrics: PerformanceMetrics,
pub timestamp: u64,
}
impl Default for UnifiedCacheStats {
fn default() -> Self {
Self {
l1_stats: CacheLevelStats::default(),
l2_stats: CacheLevelStats::default(),
overall_stats: OverallCacheStats::default(),
preheating_stats: CachePreheatingStats::default(),
tuning_stats: TuningStats::default(),
performance_metrics: PerformanceMetrics::default(),
timestamp: SystemTime::now()
.duration_since(UNIX_EPOCH)
.map(|d| d.as_secs())
.unwrap_or(0),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CacheLevelStats {
pub hits: u64,
pub misses: u64,
pub entries: usize,
pub capacity: usize,
pub usage_bytes: u64,
pub max_usage_bytes: u64,
pub evictions: u64,
pub hit_rate: f64,
pub load_factor: f64,
pub avg_entry_size: f64,
}
impl Default for CacheLevelStats {
fn default() -> Self {
Self {
hits: 0,
misses: 0,
entries: 0,
capacity: 0,
usage_bytes: 0,
max_usage_bytes: 0,
evictions: 0,
hit_rate: 0.0,
load_factor: 0.0,
avg_entry_size: 0.0,
}
}
}
impl CacheLevelStats {
pub fn update_hit_rate(&mut self) {
let total_requests = self.hits + self.misses;
if total_requests > 0 {
self.hit_rate = self.hits as f64 / total_requests as f64;
}
}
pub fn update_load_factor(&mut self) {
if self.capacity > 0 {
self.load_factor = self.entries as f64 / self.capacity as f64;
}
}
pub fn update_avg_entry_size(&mut self) {
if self.entries > 0 {
self.avg_entry_size = self.usage_bytes as f64 / self.entries as f64;
}
}
pub fn update_calculated_fields(&mut self) {
self.update_hit_rate();
self.update_load_factor();
self.update_avg_entry_size();
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct OverallCacheStats {
pub total_hits: u64,
pub total_misses: u64,
pub overall_hit_rate: f64,
pub total_entries: usize,
pub total_memory_bytes: u64,
pub total_disk_bytes: u64,
pub efficiency_score: f64,
pub promotions: u64,
pub demotions: u64,
}
impl Default for OverallCacheStats {
fn default() -> Self {
Self {
total_hits: 0,
total_misses: 0,
overall_hit_rate: 0.0,
total_entries: 0,
total_memory_bytes: 0,
total_disk_bytes: 0,
efficiency_score: 0.0,
promotions: 0,
demotions: 0,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CachePreheatingStats {
pub preheat_attempts: u64,
pub preheat_successes: u64,
pub preheat_hit_rate: f64,
pub entries_preheated: u64,
pub preheated_entries_accessed: u64,
pub avg_prediction_confidence: f64,
pub time_saved_ms: u64,
pub cpu_time_used_ms: u64,
}
impl Default for CachePreheatingStats {
fn default() -> Self {
Self {
preheat_attempts: 0,
preheat_successes: 0,
preheat_hit_rate: 0.0,
entries_preheated: 0,
preheated_entries_accessed: 0,
avg_prediction_confidence: 0.0,
time_saved_ms: 0,
cpu_time_used_ms: 0,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TuningStats {
pub tuning_cycles: u64,
pub successful_adjustments: u64,
pub ttl_adjustments: u64,
pub capacity_adjustments: u64,
pub eviction_strategy_changes: u64,
pub performance_improvement: f64,
pub last_tuning_timestamp: u64,
pub stability_score: f64,
}
impl Default for TuningStats {
fn default() -> Self {
Self {
tuning_cycles: 0,
successful_adjustments: 0,
ttl_adjustments: 0,
capacity_adjustments: 0,
eviction_strategy_changes: 0,
performance_improvement: 0.0,
last_tuning_timestamp: 0,
stability_score: 1.0,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PerformanceMetrics {
pub avg_get_latency_us: f64,
pub avg_put_latency_us: f64,
pub avg_eviction_latency_us: f64,
pub ops_per_second: f64,
pub memory_allocation_rate: f64,
pub disk_io_rate: f64,
pub cpu_usage_percent: f64,
pub peak_memory_usage: u64,
}
impl Default for PerformanceMetrics {
fn default() -> Self {
Self {
avg_get_latency_us: 0.0,
avg_put_latency_us: 0.0,
avg_eviction_latency_us: 0.0,
ops_per_second: 0.0,
memory_allocation_rate: 0.0,
disk_io_rate: 0.0,
cpu_usage_percent: 0.0,
peak_memory_usage: 0,
}
}
}
impl UnifiedCacheStats {
pub fn new() -> Self {
Self::default()
}
pub fn update_overall_stats(&mut self) {
self.overall_stats.total_hits = self.l1_stats.hits + self.l2_stats.hits;
self.overall_stats.total_misses = self.l1_stats.misses + self.l2_stats.misses;
let total_requests = self.overall_stats.total_hits + self.overall_stats.total_misses;
if total_requests > 0 {
self.overall_stats.overall_hit_rate =
self.overall_stats.total_hits as f64 / total_requests as f64;
}
self.overall_stats.total_entries = self.l1_stats.entries + self.l2_stats.entries;
self.overall_stats.total_memory_bytes = self.l1_stats.usage_bytes;
self.overall_stats.total_disk_bytes = self.l2_stats.usage_bytes;
let hit_rate_score = self.overall_stats.overall_hit_rate;
let memory_efficiency = if self.l1_stats.max_usage_bytes > 0 {
1.0 - (self.l1_stats.usage_bytes as f64 / self.l1_stats.max_usage_bytes as f64)
} else {
1.0
};
self.overall_stats.efficiency_score = (hit_rate_score + memory_efficiency) / 2.0;
self.timestamp = SystemTime::now()
.duration_since(UNIX_EPOCH)
.map(|d| d.as_secs())
.unwrap_or(0);
}
pub fn summary_report(&self) -> String {
format!(
"Cache Statistics Summary:\n\
Overall Hit Rate: {:.2}%\n\
Total Entries: {}\n\
Memory Usage: {:.2} MB\n\
Disk Usage: {:.2} MB\n\
Efficiency Score: {:.2}\n\
Preheating Hit Rate: {:.2}%\n\
Tuning Cycles: {}",
self.overall_stats.overall_hit_rate * 100.0,
self.overall_stats.total_entries,
self.overall_stats.total_memory_bytes as f64 / (1024.0 * 1024.0),
self.overall_stats.total_disk_bytes as f64 / (1024.0 * 1024.0),
self.overall_stats.efficiency_score,
self.preheating_stats.preheat_hit_rate * 100.0,
self.tuning_stats.tuning_cycles
)
}
pub fn is_performing_well(&self, target_hit_rate: f64) -> bool {
self.overall_stats.overall_hit_rate >= target_hit_rate
&& self.overall_stats.efficiency_score >= 0.7
&& self.tuning_stats.stability_score >= 0.8
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_cache_level_stats() {
let mut stats = CacheLevelStats::default();
stats.hits = 80;
stats.misses = 20;
stats.entries = 100;
stats.capacity = 200;
stats.usage_bytes = 1024;
stats.update_calculated_fields();
assert_eq!(stats.hit_rate, 0.8);
assert_eq!(stats.load_factor, 0.5);
assert_eq!(stats.avg_entry_size, 10.24);
}
#[test]
fn test_unified_cache_stats() {
let mut stats = UnifiedCacheStats::new();
stats.l1_stats.hits = 70;
stats.l1_stats.misses = 10;
stats.l2_stats.hits = 20;
stats.l2_stats.misses = 5;
stats.update_overall_stats();
assert_eq!(stats.overall_stats.total_hits, 90);
assert_eq!(stats.overall_stats.total_misses, 15);
assert!((stats.overall_stats.overall_hit_rate - 0.857).abs() < 0.01);
}
#[test]
fn test_performance_check() {
let mut stats = UnifiedCacheStats::new();
stats.overall_stats.overall_hit_rate = 0.95;
stats.overall_stats.efficiency_score = 0.8;
stats.tuning_stats.stability_score = 0.9;
assert!(stats.is_performing_well(0.9));
assert!(!stats.is_performing_well(0.96));
}
}