use std::time::Duration;
use tokio::time::Instant;
const SCAN_WINDOW: Duration = Duration::from_secs(120);
const FIRST_RESCAN: Duration = Duration::from_millis(250);
const MAX_SCAN_INTERVAL: Duration = Duration::from_secs(5);
#[derive(Debug)]
pub(super) struct Scans {
window_end: Instant,
pub(super) next: Option<Instant>,
interval: Duration,
}
impl Scans {
pub(super) fn starting(now: Instant) -> Self {
Self {
window_end: now + SCAN_WINDOW,
next: Some(now),
interval: FIRST_RESCAN,
}
}
pub(super) fn advance(&mut self, now: Instant) {
let next = now + self.interval;
self.interval = (self.interval * 2).min(MAX_SCAN_INTERVAL);
self.next = (next <= self.window_end).then_some(next);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn scans_slow_down_and_stop_when_the_window_closes() {
let start = Instant::now();
let mut scans = Scans::starting(start);
let mut taken = Vec::new();
while let Some(due) = scans.next {
taken.push(due.duration_since(start));
scans.advance(due);
}
let ms = |ms| Duration::from_millis(ms);
assert_eq!(
taken[..7],
[
ms(0),
ms(250),
ms(750),
ms(1750),
ms(3750),
ms(7750),
ms(12750)
]
);
assert!(taken.windows(2).all(|w| w[0] + MAX_SCAN_INTERVAL >= w[1]));
let last = *taken.last().unwrap();
assert!(last <= SCAN_WINDOW && last + MAX_SCAN_INTERVAL > SCAN_WINDOW);
}
}