subms_block_cache/features/
metrics.rs1use std::hash::Hash;
13use std::sync::atomic::{AtomicU64, Ordering};
14
15use crate::BlockCache;
16
17#[derive(Debug, Default)]
18pub struct CacheMetrics {
19 pub hits: AtomicU64,
20 pub misses: AtomicU64,
21 pub evictions: AtomicU64,
22 pub admissions: AtomicU64,
23 pub contention_events: AtomicU64,
24}
25
26impl CacheMetrics {
27 pub fn new() -> Self {
28 Self::default()
29 }
30
31 pub fn hits(&self) -> u64 {
32 self.hits.load(Ordering::Relaxed)
33 }
34 pub fn misses(&self) -> u64 {
35 self.misses.load(Ordering::Relaxed)
36 }
37 pub fn evictions(&self) -> u64 {
38 self.evictions.load(Ordering::Relaxed)
39 }
40 pub fn admissions(&self) -> u64 {
41 self.admissions.load(Ordering::Relaxed)
42 }
43 pub fn contention_events(&self) -> u64 {
44 self.contention_events.load(Ordering::Relaxed)
45 }
46
47 pub fn hit_ratio(&self) -> f64 {
48 let h = self.hits() as f64;
49 let m = self.misses() as f64;
50 let total = h + m;
51 if total == 0.0 { 0.0 } else { h / total }
52 }
53
54 pub fn record_hit(&self) {
55 self.hits.fetch_add(1, Ordering::Relaxed);
56 }
57 pub fn record_miss(&self) {
58 self.misses.fetch_add(1, Ordering::Relaxed);
59 }
60 pub fn record_eviction(&self) {
61 self.evictions.fetch_add(1, Ordering::Relaxed);
62 }
63 pub fn record_admission(&self) {
64 self.admissions.fetch_add(1, Ordering::Relaxed);
65 }
66 pub fn record_contention(&self) {
67 self.contention_events.fetch_add(1, Ordering::Relaxed);
68 }
69}
70
71pub struct MetricsCache<K, V> {
73 inner: BlockCache<K, V>,
74 metrics: CacheMetrics,
75}
76
77impl<K: Hash + Eq + Clone, V> MetricsCache<K, V> {
78 pub fn with_capacity(capacity: usize) -> Self {
79 Self {
80 inner: BlockCache::with_capacity(capacity),
81 metrics: CacheMetrics::new(),
82 }
83 }
84
85 pub fn capacity(&self) -> usize {
86 self.inner.capacity()
87 }
88 pub fn len(&self) -> usize {
89 self.inner.len()
90 }
91 pub fn is_empty(&self) -> bool {
92 self.inner.is_empty()
93 }
94 pub fn metrics(&self) -> &CacheMetrics {
95 &self.metrics
96 }
97
98 pub fn get(&mut self, key: &K) -> Option<&V> {
99 let r = self.inner.get(key);
100 if r.is_some() {
101 self.metrics.record_hit();
102 } else {
103 self.metrics.record_miss();
104 }
105 r
106 }
107
108 pub fn put(&mut self, key: K, value: V) -> Option<(K, V)> {
109 let r = self.inner.put(key, value);
110 self.metrics.record_admission();
111 if r.is_some() {
112 self.metrics.record_eviction();
113 }
114 r
115 }
116
117 pub fn remove(&mut self, key: &K) -> Option<V> {
119 self.inner.remove(key)
120 }
121
122 pub fn clear(&mut self) {
123 self.inner.clear();
124 }
125}
126
127#[cfg(test)]
128#[path = "metrics_tests.rs"]
129mod tests;