pub struct KeyedTrackers<K, T: DeltaTracker> { /* private fields */ }Expand description
A keyed set of delta trackers with a hard bound on its size.
§Per-cycle usage
A collector observes every key the OS still reports, then drops the rest:
use core::time::Duration;
use std::time::Instant;
use monitrs_core::rates::{CounterWidth, KeyedRateTrackers};
let mut rx: KeyedRateTrackers<String> = KeyedRateTrackers::new(CounterWidth::Bits64);
let t0 = Instant::now();
// First cycle: two interfaces, both warming up.
assert!(rx.observe("eth0".to_owned(), 1_000, t0).is_warming_up());
assert!(rx.observe("wlan0".to_owned(), 500, t0).is_warming_up());
// Second cycle: wlan0 is gone, so it is dropped rather than left to accrue.
let t1 = t0 + Duration::from_secs(1);
let eth0 = rx.observe("eth0".to_owned(), 3_000, t1);
rx.retain(|name| name == "eth0");
assert_eq!(eth0.fresh().map(|rate| rate.per_second()), Some(2_000.0));
assert_eq!(rx.len(), 1);
// wlan0 comes back with a counter that restarted: it re-baselines instead of
// reporting the whole counter as one second of traffic.
let t2 = t1 + Duration::from_secs(1);
assert!(rx.observe("wlan0".to_owned(), 90_000, t2).is_warming_up());Implementations§
Source§impl<K, T> KeyedTrackers<K, T>
impl<K, T> KeyedTrackers<K, T>
Sourcepub fn new(config: T::Config) -> Self
pub fn new(config: T::Config) -> Self
Builds an empty set with DEFAULT_MAX_TRACKED and no gap guard.
Sourcepub fn with_max_tracked(self, max_tracked: usize) -> Self
pub fn with_max_tracked(self, max_tracked: usize) -> Self
Overrides the hard size cap (§10.3).
A cap of zero tracks nothing and reports every key as skipped, which is a branch-free way to disable an expensive metric under load (§16.2).
Sourcepub fn with_max_gap(self, max_gap: Duration) -> Self
pub fn with_max_gap(self, max_gap: Duration) -> Self
Treats a gap longer than max_gap between two readings of one key as the
key having disappeared and come back (§8.2).
This is a safety net, not the primary mechanism: a collector that calls
KeyedTrackers::retain or KeyedTrackers::forget each cycle never
needs it. Set it to a small multiple of the sampling interval so ordinary
jitter does not trip it, and remember that suspend/resume looks exactly
like a disappearance from in here — reporting it as one is the honest
answer, because the counter advanced during a period this sample cannot
account for.
Sourcepub fn observe(
&mut self,
key: K,
reading: T::Reading,
at: Instant,
) -> MetricState<T::Value>
pub fn observe( &mut self, key: K, reading: T::Reading, at: Instant, ) -> MetricState<T::Value>
Folds one reading for key in and publishes the result.
A key seen for the first time warms up rather than reporting zero (§8.2).
at must be monotonic.
Sourcepub fn forget(&mut self, key: &K) -> bool
pub fn forget(&mut self, key: &K) -> bool
Drops key entirely, so a later re-appearance re-baselines.
This is the explicit answer to every identity change §8.2 lists: a device
that vanished, a renamed interface, an exited PID. Returns whether the key
was being tracked. The caller publishes the matching
UnavailableReason — DeviceDisappeared, InterfaceRenamed, or
ProcessExited — for the sample in which it noticed.
Sourcepub fn retain(&mut self, keep: impl FnMut(&K) -> bool) -> usize
pub fn retain(&mut self, keep: impl FnMut(&K) -> bool) -> usize
Keeps only the keys keep accepts, returning how many were dropped.
The cheap per-cycle way to stay bounded: call it with the set of keys the OS still reports. Deliberately not counted as an eviction, because it is the caller acting on knowledge rather than the set defending its budget.
Sourcepub fn prune_idle(&mut self, now: Instant, max_idle: Duration) -> usize
pub fn prune_idle(&mut self, now: Instant, max_idle: Duration) -> usize
Drops trackers that have not seen a reading within max_idle.
The backstop for PID churn: a process that exits is never observed again, so it ages out even if the collector never says it is gone (§10.3). A tracker that never completed a reading holds no baseline worth keeping and is dropped too.
Sourcepub fn contains_key(&self, key: &K) -> bool
pub fn contains_key(&self, key: &K) -> bool
Whether key currently has a tracker.
Sourcepub fn tracker(&self, key: &K) -> Option<&T>
pub fn tracker(&self, key: &K) -> Option<&T>
The tracker for key, for callers that need its raw baseline.
Sourcepub const fn max_tracked(&self) -> usize
pub const fn max_tracked(&self) -> usize
The hard size cap this set enforces.
Sourcepub const fn evictions(&self) -> u64
pub const fn evictions(&self) -> u64
How many trackers this set has dropped to stay inside its budget.
Worth surfacing through crate::model::CollectorHealth: a non-zero and
rising count means the cap is too low for the workload, and rates for the
churning keys are being restarted rather than measured.