use super::ttl::TtlConfig;
#[derive(Debug, Default, Clone, Copy)]
pub(crate) struct CacheMetricsInner {
pub hits: u64,
pub misses: u64,
pub evictions: u64,
pub expired: u64,
pub insertions: u64,
pub collisions: u64,
pub pending_cached: u64,
pub pending_replayed: u64,
pub pending_dropped: u64,
pub pending_replay_failed: u64,
}
impl CacheMetricsInner {
pub fn new() -> Self {
Self::default()
}
#[inline]
pub(crate) fn record_hit(&mut self) {
self.hits = self.hits.saturating_add(1);
}
#[inline]
pub(crate) fn record_miss(&mut self) {
self.misses = self.misses.saturating_add(1);
}
#[inline]
pub(crate) fn record_eviction(&mut self) {
self.evictions = self.evictions.saturating_add(1);
}
#[inline]
pub(crate) fn record_expiration(&mut self) {
self.expired = self.expired.saturating_add(1);
}
#[inline]
pub(crate) fn record_insertion(&mut self) {
self.insertions = self.insertions.saturating_add(1);
}
#[inline]
pub(crate) fn record_collision(&mut self) {
self.collisions = self.collisions.saturating_add(1);
}
#[inline]
pub(crate) fn record_pending_cached(&mut self) {
self.pending_cached = self.pending_cached.saturating_add(1);
}
#[inline]
pub(crate) fn record_pending_replayed(&mut self) {
self.pending_replayed = self.pending_replayed.saturating_add(1);
}
#[inline]
pub(crate) fn record_pending_dropped(&mut self) {
self.pending_dropped = self.pending_dropped.saturating_add(1);
}
#[inline]
pub(crate) fn record_pending_dropped_n(&mut self, n: u64) {
self.pending_dropped = self.pending_dropped.saturating_add(n);
}
#[inline]
pub(crate) fn record_pending_replay_failed(&mut self) {
self.pending_replay_failed = self.pending_replay_failed.saturating_add(1);
}
#[inline]
pub(crate) fn record_pending_replay_failed_n(&mut self, n: u64) {
self.pending_replay_failed = self.pending_replay_failed.saturating_add(n);
}
pub fn snapshot(&self) -> CacheMetrics {
CacheMetrics {
hits: self.hits,
misses: self.misses,
evictions: self.evictions,
expired: self.expired,
insertions: self.insertions,
collisions: self.collisions,
pending_cached: self.pending_cached,
pending_replayed: self.pending_replayed,
pending_dropped: self.pending_dropped,
pending_replay_failed: self.pending_replay_failed,
}
}
}
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct CacheMetrics {
pub hits: u64,
pub misses: u64,
pub evictions: u64,
pub expired: u64,
pub insertions: u64,
pub collisions: u64,
pub pending_cached: u64,
pub pending_replayed: u64,
pub pending_dropped: u64,
pub pending_replay_failed: u64,
}
impl CacheMetrics {
pub fn hit_rate(&self) -> Option<f64> {
let total = self.hits.saturating_add(self.misses);
if total == 0 {
None
} else {
Some(self.hits as f64 / total as f64)
}
}
pub fn miss_rate(&self) -> Option<f64> {
self.hit_rate().map(|hr| 1.0 - hr)
}
pub fn total_lookups(&self) -> u64 {
self.hits.saturating_add(self.misses)
}
}
#[non_exhaustive]
#[derive(Debug, Clone)]
pub struct CacheInfo {
pub current_size: usize,
pub max_size_per_cache: usize,
pub num_caches: usize,
pub ttl_config: Option<TtlConfig>,
pub metrics: CacheMetrics,
pub pending_flow_count: usize,
}
#[non_exhaustive]
#[derive(Debug, Clone)]
pub struct ParserCacheInfo {
pub v9: CacheInfo,
pub ipfix: CacheInfo,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_metrics_recording() {
let mut metrics = CacheMetricsInner::new();
metrics.record_hit();
metrics.record_hit();
metrics.record_miss();
metrics.record_eviction();
metrics.record_expiration();
metrics.record_insertion();
let snapshot = metrics.snapshot();
assert_eq!(snapshot.hits, 2);
assert_eq!(snapshot.misses, 1);
assert_eq!(snapshot.evictions, 1);
assert_eq!(snapshot.expired, 1);
assert_eq!(snapshot.insertions, 1);
}
#[test]
fn test_hit_rate() {
let mut metrics = CacheMetricsInner::new();
let snapshot = metrics.snapshot();
assert_eq!(snapshot.hit_rate(), None);
metrics.record_hit();
metrics.record_hit();
metrics.record_hit();
metrics.record_miss();
let snapshot = metrics.snapshot();
assert_eq!(snapshot.hit_rate(), Some(0.75));
assert_eq!(snapshot.miss_rate(), Some(0.25));
assert_eq!(snapshot.total_lookups(), 4);
}
}