Skip to main content

cache_rs/metrics/
slru.rs

1//! SLRU Cache Metrics
2//!
3//! Metrics specific to the SLRU (Segmented Least Recently Used) cache algorithm.
4
5extern crate alloc;
6
7use super::{CacheMetrics, CoreCacheMetrics};
8use alloc::collections::BTreeMap;
9use alloc::string::{String, ToString};
10
11/// SLRU-specific metrics (extends CoreCacheMetrics)
12///
13/// This struct contains metrics specific to the SLRU (Segmented LRU) cache algorithm.
14/// SLRU divides the cache into probationary and protected segments, so these metrics
15/// focus on segment utilization and promotion/demotion patterns.
16#[derive(Debug, Clone)]
17pub struct SlruCacheMetrics {
18    /// Core metrics common to all cache algorithms
19    pub core: CoreCacheMetrics,
20
21    /// Number of items currently in the probationary segment
22    pub probationary_size: u64,
23
24    /// Number of items currently in the protected segment
25    pub protected_size: u64,
26
27    /// Maximum allowed size for the protected segment
28    pub protected_max_size: u64,
29
30    /// Total number of promotions from probationary to protected segment
31    pub total_promotions: u64,
32
33    /// Total number of demotions from protected to probationary segment
34    pub total_demotions: u64,
35
36    /// Number of cache hits in the probationary segment
37    pub probationary_hits: u64,
38
39    /// Number of cache hits in the protected segment
40    pub protected_hits: u64,
41
42    /// Number of evictions from the probationary segment
43    pub probationary_evictions: u64,
44
45    /// Number of evictions from the protected segment
46    pub protected_evictions: u64,
47}
48
49impl SlruCacheMetrics {
50    /// Creates a new SlruCacheMetrics instance with the specified parameters
51    ///
52    /// # Arguments
53    /// * `max_cache_size_bytes` - The maximum allowed cache size in bytes
54    /// * `protected_max_size` - The maximum number of items in protected segment
55    pub fn new(max_cache_size_bytes: u64, protected_max_size: u64) -> Self {
56        Self {
57            core: CoreCacheMetrics::new(max_cache_size_bytes),
58            probationary_size: 0,
59            protected_size: 0,
60            protected_max_size,
61            total_promotions: 0,
62            total_demotions: 0,
63            probationary_hits: 0,
64            protected_hits: 0,
65            probationary_evictions: 0,
66            protected_evictions: 0,
67        }
68    }
69
70    /// Records a promotion from probationary to protected segment
71    pub fn record_promotion(&mut self) {
72        self.total_promotions += 1;
73    }
74
75    /// Records a demotion from protected to probationary segment
76    pub fn record_demotion(&mut self) {
77        self.total_demotions += 1;
78    }
79
80    /// Records a cache hit in the probationary segment
81    ///
82    /// # Arguments
83    /// * `object_size` - Size of the object that was served from cache (in bytes)
84    pub fn record_probationary_hit(&mut self, object_size: u64) {
85        self.core.record_hit(object_size);
86        self.probationary_hits += 1;
87    }
88
89    /// Records a cache hit in the protected segment
90    ///
91    /// # Arguments
92    /// * `object_size` - Size of the object that was served from cache (in bytes)
93    pub fn record_protected_hit(&mut self, object_size: u64) {
94        self.core.record_hit(object_size);
95        self.protected_hits += 1;
96    }
97
98    /// Records an eviction from the probationary segment
99    ///
100    /// # Arguments
101    /// * `evicted_size` - Size of the evicted object (in bytes)
102    pub fn record_probationary_eviction(&mut self, evicted_size: u64) {
103        self.core.record_eviction(evicted_size);
104        self.probationary_evictions += 1;
105    }
106
107    /// Records an eviction from the protected segment
108    ///
109    /// # Arguments
110    /// * `evicted_size` - Size of the evicted object (in bytes)
111    pub fn record_protected_eviction(&mut self, evicted_size: u64) {
112        self.core.record_eviction(evicted_size);
113        self.protected_evictions += 1;
114    }
115
116    /// Records a removal (explicit remove) from the probationary segment.
117    ///
118    /// Unlike `record_probationary_eviction`, this does **not** increment the
119    /// eviction counter. Use this when the user explicitly removes an entry
120    /// via `remove()`.
121    ///
122    /// # Arguments
123    /// * `removed_size` - Size of the removed object (in bytes)
124    pub fn record_probationary_removal(&mut self, removed_size: u64) {
125        self.core.record_removal(removed_size);
126    }
127
128    /// Records a removal (explicit remove) from the protected segment.
129    ///
130    /// Unlike `record_protected_eviction`, this does **not** increment the
131    /// eviction counter. Use this when the user explicitly removes an entry
132    /// via `remove()`.
133    ///
134    /// # Arguments
135    /// * `removed_size` - Size of the removed object (in bytes)
136    pub fn record_protected_removal(&mut self, removed_size: u64) {
137        self.core.record_removal(removed_size);
138    }
139
140    /// Updates the segment sizes
141    ///
142    /// # Arguments
143    /// * `probationary_size` - Current number of items in probationary segment
144    /// * `protected_size` - Current number of items in protected segment
145    pub fn update_segment_sizes(&mut self, probationary_size: u64, protected_size: u64) {
146        self.probationary_size = probationary_size;
147        self.protected_size = protected_size;
148    }
149
150    /// Calculates the protection ratio (protected hits / total hits)
151    ///
152    /// # Returns
153    /// Ratio of hits in protected segment vs total hits, or 0.0 if no hits
154    pub fn protection_ratio(&self) -> f64 {
155        if self.core.cache_hits > 0 {
156            self.protected_hits as f64 / self.core.cache_hits as f64
157        } else {
158            0.0
159        }
160    }
161
162    /// Calculates the promotion efficiency (promotions / probationary hits)
163    ///
164    /// # Returns
165    /// How often probationary hits lead to promotions, or 0.0 if no probationary hits
166    pub fn promotion_efficiency(&self) -> f64 {
167        if self.probationary_hits > 0 {
168            self.total_promotions as f64 / self.probationary_hits as f64
169        } else {
170            0.0
171        }
172    }
173
174    /// Calculates protected segment utilization
175    ///
176    /// # Returns
177    /// Ratio of current protected size to maximum protected size
178    pub fn protected_utilization(&self) -> f64 {
179        if self.protected_max_size > 0 {
180            self.protected_size as f64 / self.protected_max_size as f64
181        } else {
182            0.0
183        }
184    }
185
186    /// Converts SLRU metrics to a BTreeMap for reporting
187    ///
188    /// This method returns all metrics relevant to the SLRU cache algorithm,
189    /// including both core metrics and SLRU-specific segment metrics.
190    ///
191    /// Uses BTreeMap to ensure consistent, deterministic ordering of metrics.
192    ///
193    /// # Returns
194    /// A BTreeMap containing all SLRU cache metrics as key-value pairs
195    pub fn to_btreemap(&self) -> BTreeMap<String, f64> {
196        let mut metrics = self.core.to_btreemap();
197
198        // SLRU-specific segment metrics
199        metrics.insert(
200            "probationary_size".to_string(),
201            self.probationary_size as f64,
202        );
203        metrics.insert("protected_size".to_string(), self.protected_size as f64);
204        metrics.insert(
205            "protected_max_size".to_string(),
206            self.protected_max_size as f64,
207        );
208        metrics.insert(
209            "protected_utilization".to_string(),
210            self.protected_utilization(),
211        );
212
213        // Movement metrics
214        metrics.insert("total_promotions".to_string(), self.total_promotions as f64);
215        metrics.insert("total_demotions".to_string(), self.total_demotions as f64);
216
217        // Segment-specific hit metrics
218        metrics.insert(
219            "probationary_hits".to_string(),
220            self.probationary_hits as f64,
221        );
222        metrics.insert("protected_hits".to_string(), self.protected_hits as f64);
223        metrics.insert("protection_ratio".to_string(), self.protection_ratio());
224
225        // Segment-specific eviction metrics
226        metrics.insert(
227            "probationary_evictions".to_string(),
228            self.probationary_evictions as f64,
229        );
230        metrics.insert(
231            "protected_evictions".to_string(),
232            self.protected_evictions as f64,
233        );
234
235        // Efficiency metrics
236        metrics.insert(
237            "promotion_efficiency".to_string(),
238            self.promotion_efficiency(),
239        );
240
241        if self.core.requests > 0 {
242            metrics.insert(
243                "promotion_rate".to_string(),
244                self.total_promotions as f64 / self.core.requests as f64,
245            );
246            metrics.insert(
247                "demotion_rate".to_string(),
248                self.total_demotions as f64 / self.core.requests as f64,
249            );
250        }
251
252        metrics
253    }
254}
255
256impl CacheMetrics for SlruCacheMetrics {
257    /// Returns all SLRU cache metrics as key-value pairs in deterministic order
258    ///
259    /// # Returns
260    /// A BTreeMap containing all metrics tracked by this SLRU cache instance
261    fn metrics(&self) -> BTreeMap<String, f64> {
262        self.to_btreemap()
263    }
264
265    /// Returns the algorithm name for this cache implementation
266    ///
267    /// # Returns
268    /// "SLRU" - identifying this as a Segmented Least Recently Used cache
269    fn algorithm_name(&self) -> &'static str {
270        "SLRU"
271    }
272}