diff --git a/Cargo.lock b/Cargo.lock index 2560e13..257a2bd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -165,6 +165,7 @@ dependencies = [ "proptest", "quick_cache", "rustc-hash 2.1.1", + "serde", "serde_json", ] diff --git a/Cargo.toml b/Cargo.toml index 7f9efbb..fdf4a21 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,6 +35,7 @@ crate-type = ["lib"] [features] default = ["policy-s3-fifo", "policy-lru", "policy-fast-lru", "policy-lru-k", "policy-clock"] metrics = [] +serde = ["dep:serde"] concurrency = ["parking_lot"] # Eviction policy feature flags. Enable only the policies you need for smaller builds. @@ -81,6 +82,7 @@ policy-nru = [] [dependencies] parking_lot = { version = "0.12", optional = true } rustc-hash = "2.1" +serde = { version = "1", features = ["derive"], optional = true } [dev-dependencies] bench-support = { path = "bench-support" } diff --git a/fuzz/fuzz_targets/frequency_buckets_property_tests.rs b/fuzz/fuzz_targets/frequency_buckets_property_tests.rs index 15c2f79..ef00e28 100644 --- a/fuzz/fuzz_targets/frequency_buckets_property_tests.rs +++ b/fuzz/fuzz_targets/frequency_buckets_property_tests.rs @@ -101,7 +101,7 @@ fn test_min_freq_accuracy(data: &[u8]) { // Compute actual minimum frequency let mut actual_min: Option = None; - for (_, meta) in buckets.iter_entries() { + for (_, meta) in buckets.iter() { actual_min = match actual_min { None => Some(meta.freq), Some(min) => Some(min.min(meta.freq)), diff --git a/src/metrics/cell.rs b/src/metrics/cell.rs index 2bc0514..d72dac5 100644 --- a/src/metrics/cell.rs +++ b/src/metrics/cell.rs @@ -9,11 +9,6 @@ use std::cell::Cell; pub(crate) struct MetricsCell(Cell); impl MetricsCell { - #[inline] - pub fn new() -> Self { - Self(Cell::new(0)) - } - #[inline] pub fn get(&self) -> u64 { self.0.get() diff --git a/src/metrics/metrics_impl.rs b/src/metrics/metrics_impl.rs index b7f426d..b8eecae 100644 --- a/src/metrics/metrics_impl.rs +++ b/src/metrics/metrics_impl.rs @@ -1,3 +1,16 @@ +//! Concrete metrics recorders for each eviction policy. +//! +//! Each struct accumulates lightweight counters that the corresponding policy +//! increments on its hot path. The [`snapshot`](super::snapshot) module provides +//! read-only, `Copy` snapshots of these counters for bench/test consumption, +//! while [`exporter`](super::exporter) publishes them to monitoring backends. +//! +//! ## Field visibility +//! +//! Fields backed by plain `u64` are `pub` for direct reads. +//! Fields that require interior mutability (recorded through `&self` on read +//! paths) use the crate-internal `MetricsCell` and are `pub(crate)`. + use crate::metrics::cell::MetricsCell; use crate::metrics::traits::{ ArcMetricsRecorder, CarMetricsRecorder, ClockMetricsRecorder, ClockProMetricsRecorder, @@ -7,7 +20,8 @@ use crate::metrics::traits::{ S3FifoMetricsRecorder, SlruMetricsRecorder, TwoQMetricsRecorder, }; -#[derive(Debug, Default)] +/// FIFO / insertion-order cache metrics. +#[derive(Debug, Default, Clone, PartialEq, Eq)] pub struct CacheMetrics { pub get_calls: u64, pub get_hits: u64, @@ -29,7 +43,8 @@ pub struct CacheMetrics { pub(crate) age_rank_found: MetricsCell, } -#[derive(Debug, Default)] +/// LRU (least-recently-used) cache metrics. +#[derive(Debug, Default, Clone, PartialEq, Eq)] pub struct LruMetrics { pub get_calls: u64, pub get_hits: u64, @@ -50,7 +65,8 @@ pub struct LruMetrics { pub(crate) recency_rank_scan_steps: MetricsCell, } -#[derive(Debug, Default)] +/// LFU (least-frequently-used) cache metrics. +#[derive(Debug, Default, Clone, PartialEq, Eq)] pub struct LfuMetrics { pub get_calls: u64, pub get_hits: u64, @@ -72,7 +88,8 @@ pub struct LfuMetrics { pub increment_frequency_found: u64, } -#[derive(Debug, Default)] +/// LRU-K (K-distance) cache metrics. +#[derive(Debug, Default, Clone, PartialEq, Eq)] pub struct LruKMetrics { pub get_calls: u64, pub get_hits: u64, @@ -102,31 +119,6 @@ pub struct LruKMetrics { pub(crate) k_distance_rank_scan_steps: MetricsCell, } -impl CacheMetrics { - pub fn new() -> CacheMetrics { - Self { - get_calls: 0, - get_hits: 0, - get_misses: 0, - insert_calls: 0, - insert_updates: 0, - insert_new: 0, - evict_calls: 0, - evicted_entries: 0, - stale_skips: 0, - evict_scan_steps: 0, - pop_oldest_calls: 0, - pop_oldest_found: 0, - pop_oldest_empty_or_stale: 0, - peek_oldest_calls: MetricsCell::new(), - peek_oldest_found: MetricsCell::new(), - age_rank_calls: MetricsCell::new(), - age_rank_scan_steps: MetricsCell::new(), - age_rank_found: MetricsCell::new(), - } - } -} - impl CoreMetricsRecorder for CacheMetrics { fn record_get_hit(&mut self) { self.get_calls += 1; @@ -185,6 +177,7 @@ impl FifoMetricsRecorder for CacheMetrics { } } +#[doc(hidden)] impl FifoMetricsReadRecorder for &CacheMetrics { fn record_peek_oldest_call(&self) { self.peek_oldest_calls.incr(); @@ -279,6 +272,7 @@ impl LruMetricsRecorder for LruMetrics { } } +#[doc(hidden)] impl LruMetricsReadRecorder for &LruMetrics { fn record_peek_lru_call(&self) { self.peek_lru_calls.incr(); @@ -377,6 +371,7 @@ impl LfuMetricsRecorder for LfuMetrics { } } +#[doc(hidden)] impl LfuMetricsReadRecorder for &LfuMetrics { fn record_peek_lfu_call(&self) { self.peek_lfu_calls.incr(); @@ -505,6 +500,7 @@ impl LruKMetricsRecorder for LruKMetrics { } } +#[doc(hidden)] impl LruKMetricsReadRecorder for &LruKMetrics { fn record_peek_lru_k_call(&self) { self.peek_lru_k_calls.incr(); @@ -535,11 +531,8 @@ impl LruKMetricsReadRecorder for &LruKMetrics { } } -// --------------------------------------------------------------------------- -// CoreOnlyMetrics (LIFO, Random) -// --------------------------------------------------------------------------- - -#[derive(Debug, Default)] +/// Minimal metrics for policies with no policy-specific counters (LIFO, Random). +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)] pub struct CoreOnlyMetrics { pub get_calls: u64, pub get_hits: u64, @@ -578,11 +571,8 @@ impl CoreMetricsRecorder for CoreOnlyMetrics { fn record_clear(&mut self) {} } -// --------------------------------------------------------------------------- -// ArcMetrics -// --------------------------------------------------------------------------- - -#[derive(Debug, Default)] +/// ARC (adaptive replacement cache) metrics. +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)] pub struct ArcMetrics { pub get_calls: u64, pub get_hits: u64, @@ -652,11 +642,8 @@ impl ArcMetricsRecorder for ArcMetrics { } } -// --------------------------------------------------------------------------- -// CarMetrics -// --------------------------------------------------------------------------- - -#[derive(Debug, Default)] +/// CAR (clock with adaptive replacement) metrics. +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)] pub struct CarMetrics { pub get_calls: u64, pub get_hits: u64, @@ -722,11 +709,8 @@ impl CarMetricsRecorder for CarMetrics { } } -// --------------------------------------------------------------------------- -// ClockMetrics -// --------------------------------------------------------------------------- - -#[derive(Debug, Default)] +/// Clock (second-chance) cache metrics. +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)] pub struct ClockMetrics { pub get_calls: u64, pub get_hits: u64, @@ -776,11 +760,8 @@ impl ClockMetricsRecorder for ClockMetrics { } } -// --------------------------------------------------------------------------- -// ClockProMetrics -// --------------------------------------------------------------------------- - -#[derive(Debug, Default)] +/// Clock-PRO (adaptive hot/cold/test clock) metrics. +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)] pub struct ClockProMetrics { pub get_calls: u64, pub get_hits: u64, @@ -838,11 +819,8 @@ impl ClockProMetricsRecorder for ClockProMetrics { } } -// --------------------------------------------------------------------------- -// MfuMetrics -// --------------------------------------------------------------------------- - -#[derive(Debug, Default)] +/// MFU (most-frequently-used eviction) metrics. +#[derive(Debug, Default, Clone, PartialEq, Eq)] pub struct MfuMetrics { pub get_calls: u64, pub get_hits: u64, @@ -908,6 +886,7 @@ impl MfuMetricsRecorder for MfuMetrics { } } +#[doc(hidden)] impl MfuMetricsReadRecorder for &MfuMetrics { fn record_peek_mfu_call(&self) { self.peek_mfu_calls.incr(); @@ -923,11 +902,8 @@ impl MfuMetricsReadRecorder for &MfuMetrics { } } -// --------------------------------------------------------------------------- -// NruMetrics -// --------------------------------------------------------------------------- - -#[derive(Debug, Default)] +/// NRU (not-recently-used) cache metrics. +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)] pub struct NruMetrics { pub get_calls: u64, pub get_hits: u64, @@ -977,11 +953,8 @@ impl NruMetricsRecorder for NruMetrics { } } -// --------------------------------------------------------------------------- -// SlruMetrics -// --------------------------------------------------------------------------- - -#[derive(Debug, Default)] +/// SLRU (segmented LRU) cache metrics. +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)] pub struct SlruMetrics { pub get_calls: u64, pub get_hits: u64, @@ -1031,11 +1004,8 @@ impl SlruMetricsRecorder for SlruMetrics { } } -// --------------------------------------------------------------------------- -// TwoQMetrics -// --------------------------------------------------------------------------- - -#[derive(Debug, Default)] +/// Two-Q (A1in/A1out/Am queue) cache metrics. +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)] pub struct TwoQMetrics { pub get_calls: u64, pub get_hits: u64, @@ -1085,11 +1055,8 @@ impl TwoQMetricsRecorder for TwoQMetrics { } } -// --------------------------------------------------------------------------- -// S3FifoMetrics -// --------------------------------------------------------------------------- - -#[derive(Debug, Default, Clone)] +/// S3-FIFO (small/main/ghost queue) cache metrics. +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)] pub struct S3FifoMetrics { pub get_calls: u64, pub get_hits: u64, diff --git a/src/metrics/snapshot.rs b/src/metrics/snapshot.rs index e928465..1504958 100644 --- a/src/metrics/snapshot.rs +++ b/src/metrics/snapshot.rs @@ -1,4 +1,28 @@ -#[derive(Debug, Default, Clone, Copy)] +//! Point-in-time snapshots of cache metrics counters. +//! +//! Each eviction policy has a dedicated snapshot struct that captures both the +//! core counters (gets, inserts, evictions) and policy-specific signals at the +//! moment [`MetricsSnapshotProvider::snapshot`] is called. +//! +//! Snapshots are cheap `Copy` value types intended for assertion in tests, +//! export via [`PrometheusTextExporter`], or ad-hoc inspection with `Debug`. +//! +//! ## Example +//! +//! ``` +//! use cachekit::metrics::snapshot::CoreOnlyMetricsSnapshot; +//! +//! let snap = CoreOnlyMetricsSnapshot::default(); +//! assert_eq!(snap.get_calls, snap.get_hits + snap.get_misses); +//! ``` +//! +//! [`MetricsSnapshotProvider::snapshot`]: crate::metrics::traits::MetricsSnapshotProvider::snapshot +//! [`PrometheusTextExporter`]: crate::metrics::exporter::PrometheusTextExporter + +/// FIFO / insertion-order cache metrics. +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)] +#[non_exhaustive] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct CacheMetricsSnapshot { pub get_calls: u64, pub get_hits: u64, @@ -10,8 +34,10 @@ pub struct CacheMetricsSnapshot { pub evict_calls: u64, pub evicted_entries: u64, - pub stale_skips: u64, // queue entries popped that were already removed from map - pub evict_scan_steps: u64, // how many pop_front iterations inside eviction + /// Queue entries popped that were already removed from the map. + pub stale_skips: u64, + /// How many `pop_front` iterations inside a single eviction call. + pub evict_scan_steps: u64, pub pop_oldest_calls: u64, pub pop_oldest_found: u64, @@ -24,13 +50,18 @@ pub struct CacheMetricsSnapshot { pub age_rank_found: u64, pub age_rank_scan_steps: u64, - // gauges captured at snapshot time + /// Current number of entries in the cache (gauge). pub cache_len: usize, + /// Current length of the insertion-order queue (gauge). pub insertion_order_len: usize, + /// Configured maximum capacity (gauge). pub capacity: usize, } -#[derive(Debug, Default, Clone, Copy)] +/// LRU (Least Recently Used) cache metrics. +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)] +#[non_exhaustive] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct LruMetricsSnapshot { pub get_calls: u64, pub get_hits: u64, @@ -57,7 +88,10 @@ pub struct LruMetricsSnapshot { pub capacity: usize, } -#[derive(Debug, Default, Clone, Copy)] +/// LFU (Least Frequently Used) cache metrics. +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)] +#[non_exhaustive] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct LfuMetricsSnapshot { pub get_calls: u64, pub get_hits: u64, @@ -85,7 +119,10 @@ pub struct LfuMetricsSnapshot { pub capacity: usize, } -#[derive(Debug, Default, Clone, Copy)] +/// LRU-K cache metrics, combining standard LRU counters with K-distance tracking. +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)] +#[non_exhaustive] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct LruKMetricsSnapshot { pub get_calls: u64, pub get_hits: u64, @@ -122,7 +159,10 @@ pub struct LruKMetricsSnapshot { pub capacity: usize, } -#[derive(Debug, Default, Clone, Copy)] +/// Core-only metrics for policies that add no policy-specific counters. +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)] +#[non_exhaustive] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct CoreOnlyMetricsSnapshot { pub get_calls: u64, pub get_hits: u64, @@ -139,7 +179,10 @@ pub struct CoreOnlyMetricsSnapshot { pub capacity: usize, } -#[derive(Debug, Default, Clone, Copy)] +/// ARC (Adaptive Replacement Cache) metrics. +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)] +#[non_exhaustive] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct ArcMetricsSnapshot { pub get_calls: u64, pub get_hits: u64, @@ -164,7 +207,10 @@ pub struct ArcMetricsSnapshot { pub capacity: usize, } -#[derive(Debug, Default, Clone, Copy)] +/// CAR (Clock with Adaptive Replacement) cache metrics. +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)] +#[non_exhaustive] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct CarMetricsSnapshot { pub get_calls: u64, pub get_hits: u64, @@ -188,7 +234,10 @@ pub struct CarMetricsSnapshot { pub capacity: usize, } -#[derive(Debug, Default, Clone, Copy)] +/// CLOCK cache metrics. +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)] +#[non_exhaustive] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct ClockMetricsSnapshot { pub get_calls: u64, pub get_hits: u64, @@ -208,7 +257,10 @@ pub struct ClockMetricsSnapshot { pub capacity: usize, } -#[derive(Debug, Default, Clone, Copy)] +/// CLOCK-Pro cache metrics. +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)] +#[non_exhaustive] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct ClockProMetricsSnapshot { pub get_calls: u64, pub get_hits: u64, @@ -230,7 +282,10 @@ pub struct ClockProMetricsSnapshot { pub capacity: usize, } -#[derive(Debug, Default, Clone, Copy)] +/// MFU (Most Frequently Used) cache metrics. +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)] +#[non_exhaustive] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct MfuMetricsSnapshot { pub get_calls: u64, pub get_hits: u64, @@ -254,7 +309,10 @@ pub struct MfuMetricsSnapshot { pub capacity: usize, } -#[derive(Debug, Default, Clone, Copy)] +/// NRU (Not Recently Used) cache metrics. +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)] +#[non_exhaustive] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct NruMetricsSnapshot { pub get_calls: u64, pub get_hits: u64, @@ -274,7 +332,10 @@ pub struct NruMetricsSnapshot { pub capacity: usize, } -#[derive(Debug, Default, Clone, Copy)] +/// SLRU (Segmented LRU) cache metrics. +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)] +#[non_exhaustive] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct SlruMetricsSnapshot { pub get_calls: u64, pub get_hits: u64, @@ -294,7 +355,10 @@ pub struct SlruMetricsSnapshot { pub capacity: usize, } -#[derive(Debug, Default, Clone, Copy)] +/// 2Q (Two-Queue) cache metrics. +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)] +#[non_exhaustive] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct TwoQMetricsSnapshot { pub get_calls: u64, pub get_hits: u64, @@ -314,7 +378,10 @@ pub struct TwoQMetricsSnapshot { pub capacity: usize, } -#[derive(Debug, Default, Clone, Copy)] +/// S3-FIFO cache metrics. +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)] +#[non_exhaustive] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct S3FifoMetricsSnapshot { pub get_calls: u64, pub get_hits: u64, diff --git a/src/metrics/traits.rs b/src/metrics/traits.rs index 91db925..7f8ffd8 100644 --- a/src/metrics/traits.rs +++ b/src/metrics/traits.rs @@ -43,8 +43,25 @@ //! - **Environment split**: //! - Production: use lightweight recorders + exporters. //! - Bench/Test: use snapshot providers + resettable metrics. +//! +//! ## Example +//! +//! ``` +//! use cachekit::metrics::traits::{CoreMetricsRecorder, MetricsSnapshotProvider}; +//! +//! fn run_workload(m: &mut M) { +//! m.record_get_hit(); +//! m.record_get_miss(); +//! m.record_insert_call(); +//! m.record_insert_new(); +//! } +//! ``` /// Common counters for any cache policy. +/// +/// Every policy-specific recorder (e.g. [`FifoMetricsRecorder`], +/// [`LruMetricsRecorder`]) extends this trait so shared hit/miss/insert/evict +/// counters are always available. pub trait CoreMetricsRecorder { fn record_get_hit(&mut self); fn record_get_miss(&mut self); @@ -57,6 +74,8 @@ pub trait CoreMetricsRecorder { } /// Metrics for FIFO behavior (insertion order). +/// +/// See also [`FifoMetricsReadRecorder`] for read-only (`&self`) counters. pub trait FifoMetricsRecorder: CoreMetricsRecorder { fn record_evict_scan_step(&mut self); fn record_stale_skip(&mut self); @@ -65,10 +84,12 @@ pub trait FifoMetricsRecorder: CoreMetricsRecorder { fn record_pop_oldest_empty_or_stale(&mut self); } -/// Read-only FIFO metrics for &self methods (uses interior mutability). +/// Read-only FIFO metrics for `&self` methods (uses interior mutability). /// /// Use this for cache operations that only take `&self` (e.g., `peek_oldest`, /// `age_rank`) where a mutable recorder is not available. +/// +/// See also [`FifoMetricsRecorder`] for the mutable counterpart. pub trait FifoMetricsReadRecorder { fn record_peek_oldest_call(&self); fn record_peek_oldest_found(&self); @@ -78,6 +99,8 @@ pub trait FifoMetricsReadRecorder { } /// Metrics for LRU behavior (recency order). +/// +/// See also [`LruMetricsReadRecorder`] for read-only (`&self`) counters. pub trait LruMetricsRecorder: CoreMetricsRecorder { fn record_pop_lru_call(&mut self); fn record_pop_lru_found(&mut self); @@ -90,7 +113,9 @@ pub trait LruMetricsRecorder: CoreMetricsRecorder { fn record_recency_rank_scan_step(&mut self); } -/// Read-only LRU metrics for &self methods (uses interior mutability). +/// Read-only LRU metrics for `&self` methods (uses interior mutability). +/// +/// See also [`LruMetricsRecorder`] for the mutable counterpart. pub trait LruMetricsReadRecorder { fn record_peek_lru_call(&self); fn record_peek_lru_found(&self); @@ -100,6 +125,8 @@ pub trait LruMetricsReadRecorder { } /// Metrics for LFU behavior (frequency order). +/// +/// See also [`LfuMetricsReadRecorder`] for read-only (`&self`) counters. pub trait LfuMetricsRecorder: CoreMetricsRecorder { fn record_pop_lfu_call(&mut self); fn record_pop_lfu_found(&mut self); @@ -113,7 +140,9 @@ pub trait LfuMetricsRecorder: CoreMetricsRecorder { fn record_increment_frequency_found(&mut self); } -/// Read-only LFU metrics for &self methods (uses interior mutability). +/// Read-only LFU metrics for `&self` methods (uses interior mutability). +/// +/// See also [`LfuMetricsRecorder`] for the mutable counterpart. pub trait LfuMetricsReadRecorder { fn record_peek_lfu_call(&self); fn record_peek_lfu_found(&self); @@ -122,6 +151,9 @@ pub trait LfuMetricsReadRecorder { } /// Metrics for LRU-K behavior (K-distance order). +/// +/// Extends [`LruMetricsRecorder`] with backward K-distance counters. +/// See also [`LruKMetricsReadRecorder`] for read-only (`&self`) counters. pub trait LruKMetricsRecorder: LruMetricsRecorder { fn record_pop_lru_k_call(&mut self); fn record_pop_lru_k_found(&mut self); @@ -134,7 +166,9 @@ pub trait LruKMetricsRecorder: LruMetricsRecorder { fn record_k_distance_rank_scan_step(&mut self); } -/// Read-only LRU-K metrics for &self methods (uses interior mutability). +/// Read-only LRU-K metrics for `&self` methods (uses interior mutability). +/// +/// See also [`LruKMetricsRecorder`] for the mutable counterpart. pub trait LruKMetricsReadRecorder { fn record_peek_lru_k_call(&self); fn record_peek_lru_k_found(&self); @@ -181,6 +215,8 @@ pub trait ClockProMetricsRecorder: CoreMetricsRecorder { } /// Metrics for MFU behavior (most frequently used eviction). +/// +/// See also [`MfuMetricsReadRecorder`] for read-only (`&self`) counters. pub trait MfuMetricsRecorder: CoreMetricsRecorder { fn record_pop_mfu_call(&mut self); fn record_pop_mfu_found(&mut self); @@ -190,7 +226,9 @@ pub trait MfuMetricsRecorder: CoreMetricsRecorder { fn record_frequency_found(&mut self); } -/// Read-only MFU metrics for &self methods (uses interior mutability). +/// Read-only MFU metrics for `&self` methods (uses interior mutability). +/// +/// See also [`MfuMetricsRecorder`] for the mutable counterpart. pub trait MfuMetricsReadRecorder { fn record_peek_mfu_call(&self); fn record_peek_mfu_found(&self); @@ -226,43 +264,26 @@ pub trait S3FifoMetricsRecorder: CoreMetricsRecorder { } /// Snapshot provider for bench/testing. +/// +/// Returns a point-in-time copy of all counters as a snapshot struct `S`. +/// Pair with [`MetricsReset`] to clear counters between iterations, or +/// with [`MetricsExporter`] to publish snapshots to a monitoring backend. pub trait MetricsSnapshotProvider { fn snapshot(&self) -> S; } /// Reset metrics between tests or benchmark iterations. +/// +/// Typically used alongside [`MetricsSnapshotProvider`] to isolate +/// measurements across successive runs. pub trait MetricsReset { fn reset_metrics(&self); } /// Export/publish metrics to production monitoring backends. +/// +/// Consumes a snapshot produced by [`MetricsSnapshotProvider::snapshot`] +/// and writes it to an external system (e.g. Prometheus, StatsD). pub trait MetricsExporter { fn export(&self, snapshot: &S); } - -/// Shared counters for any policy. -#[derive(Debug, Default, Clone, Copy)] -pub struct CoreMetricsSnapshot { - pub get_calls: u64, - pub get_hits: u64, - pub get_misses: u64, - pub insert_calls: u64, - pub insert_updates: u64, - pub insert_new: u64, - pub evict_calls: u64, - pub evicted_entries: u64, -} - -/// FIFO-specific snapshot composed from the core snapshot. -#[derive(Debug, Default, Clone, Copy)] -pub struct FifoMetricsSnapshot { - pub core: CoreMetricsSnapshot, - pub pop_oldest_calls: u64, - pub pop_oldest_found: u64, - pub pop_oldest_empty_or_stale: u64, - pub peek_oldest_calls: u64, - pub peek_oldest_found: u64, - pub age_rank_calls: u64, - pub age_rank_found: u64, - pub age_rank_scan_steps: u64, -} diff --git a/src/policy/s3_fifo.rs b/src/policy/s3_fifo.rs index 7b76286..c4f899b 100644 --- a/src/policy/s3_fifo.rs +++ b/src/policy/s3_fifo.rs @@ -1528,7 +1528,7 @@ where /// Returns merged performance metrics (inner write-path + concurrent read-path). #[cfg(feature = "metrics")] pub fn metrics(&self) -> S3FifoMetrics { - let mut m = self.inner.read().metrics().clone(); + let mut m = *self.inner.read().metrics(); let rh = self.read_hits.load(Ordering::Relaxed); let rm = self.read_misses.load(Ordering::Relaxed); m.get_hits += rh;