pub(crate) const SHADOW_ROLLUP_WINDOW_SECS: u32 = 30;
#[derive(Default)]
pub(crate) struct WindowedStat {
samples: Vec<u64>,
}
impl WindowedStat {
pub(crate) fn record(&mut self, value: u64) {
self.samples.push(value);
}
pub(crate) fn record_opt(&mut self, value: Option<u64>) {
if let Some(value) = value {
self.samples.push(value);
}
}
#[cfg(test)]
pub(crate) fn count(&self) -> usize {
self.samples.len()
}
pub(crate) fn min(&self) -> Option<u64> {
self.samples.iter().copied().min()
}
pub(crate) fn max(&self) -> Option<u64> {
self.samples.iter().copied().max()
}
pub(crate) fn mean(&self) -> Option<u64> {
if self.samples.is_empty() {
return None;
}
let sum: u128 = self.samples.iter().map(|&v| v as u128).sum();
Some((sum / self.samples.len() as u128) as u64)
}
pub(crate) fn p50(&self) -> Option<u64> {
if self.samples.is_empty() {
return None;
}
let mut sorted = self.samples.clone();
sorted.sort_unstable();
Some(sorted[sorted.len() / 2])
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn empty_stat_yields_none() {
let stat = WindowedStat::default();
assert_eq!(stat.count(), 0);
assert_eq!(stat.min(), None);
assert_eq!(stat.max(), None);
assert_eq!(stat.mean(), None);
assert_eq!(stat.p50(), None);
}
#[test]
fn record_opt_skips_none_samples() {
let mut stat = WindowedStat::default();
stat.record_opt(None);
stat.record_opt(Some(10));
stat.record_opt(None);
stat.record_opt(Some(20));
assert_eq!(stat.count(), 2);
assert_eq!(stat.min(), Some(10));
assert_eq!(stat.max(), Some(20));
assert_eq!(stat.mean(), Some(15));
}
#[test]
fn summary_stats_over_a_window() {
let mut stat = WindowedStat::default();
for v in [5u64, 1, 9, 3, 7] {
stat.record(v);
}
assert_eq!(stat.count(), 5);
assert_eq!(stat.min(), Some(1));
assert_eq!(stat.max(), Some(9));
assert_eq!(stat.mean(), Some(5)); assert_eq!(stat.p50(), Some(5)); }
#[test]
fn mean_rounds_down_and_cannot_overflow() {
let mut stat = WindowedStat::default();
for _ in 0..SHADOW_ROLLUP_WINDOW_SECS {
stat.record(u64::MAX);
}
assert_eq!(stat.mean(), Some(u64::MAX));
}
#[test]
fn p50_uses_upper_middle_median_for_even_counts() {
let mut stat = WindowedStat::default();
for v in [1u64, 2, 3, 4] {
stat.record(v);
}
assert_eq!(stat.p50(), Some(3));
}
}