#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub struct CacheStats {
pub hits: u64,
pub misses: u64,
pub loads: u64,
pub single_flight_joins: u64,
pub stale_load_discards: u64,
pub invalidations: u64,
pub evictions: u64,
}
impl CacheStats {
pub fn total_requests(&self) -> u64 {
self.hits + self.misses
}
pub fn hit_ratio(&self) -> Option<f64> {
let total = self.total_requests();
if total == 0 {
None
} else {
Some(self.hits as f64 / total as f64)
}
}
pub fn has_single_flight_activity(&self) -> bool {
self.single_flight_joins > 0
}
pub fn has_stale_load_discards(&self) -> bool {
self.stale_load_discards > 0
}
}
#[derive(Debug, Clone, Copy, Default, PartialEq)]
pub struct CacheDiagnostics {
pub stats: CacheStats,
pub estimated_entries: u64,
}
impl CacheDiagnostics {
pub fn total_requests(&self) -> u64 {
self.stats.total_requests()
}
pub fn hit_ratio(&self) -> Option<f64> {
self.stats.hit_ratio()
}
pub fn is_empty(&self) -> bool {
self.estimated_entries == 0
}
}
#[cfg(test)]
mod tests {
use super::{CacheDiagnostics, CacheStats};
#[test]
fn stats_helpers_cover_empty_and_non_empty_snapshots() {
let empty = CacheStats::default();
assert_eq!(empty.total_requests(), 0);
assert_eq!(empty.hit_ratio(), None);
assert!(!empty.has_single_flight_activity());
assert!(!empty.has_stale_load_discards());
let active = CacheStats {
hits: 3,
misses: 1,
single_flight_joins: 2,
stale_load_discards: 1,
..CacheStats::default()
};
assert_eq!(active.total_requests(), 4);
assert_eq!(active.hit_ratio(), Some(0.75));
assert!(active.has_single_flight_activity());
assert!(active.has_stale_load_discards());
}
#[test]
fn diagnostics_helpers_delegate_to_stats() {
let diagnostics = CacheDiagnostics {
stats: CacheStats {
hits: 1,
misses: 1,
..CacheStats::default()
},
estimated_entries: 1,
};
assert_eq!(diagnostics.total_requests(), 2);
assert_eq!(diagnostics.hit_ratio(), Some(0.5));
assert!(!diagnostics.is_empty());
let empty = CacheDiagnostics::default();
assert_eq!(empty.hit_ratio(), None);
assert!(empty.is_empty());
}
}