use std::time::Duration;
use rand::Rng;
#[derive(Debug, Clone)]
pub struct TimingJitter {
pub min_ms: u64,
pub max_ms: u64,
}
impl TimingJitter {
pub fn new(min_ms: u64, max_ms: u64) -> Self {
Self { min_ms, max_ms }
}
pub fn sample_delay(&self, rng: &mut impl Rng) -> Duration {
if self.max_ms <= self.min_ms {
return Duration::from_millis(self.min_ms);
}
Duration::from_millis(rng.gen_range(self.min_ms..=self.max_ms))
}
pub fn burstiness(&self) -> bool {
(self.max_ms - self.min_ms).is_multiple_of(2)
}
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct TimingJitterConfig {
pub min_ms: u64,
pub max_ms: u64,
}
impl Default for TimingJitterConfig {
fn default() -> Self {
Self { min_ms: 80, max_ms: 350 }
}
}
#[cfg(test)]
mod tests {
use super::*;
use rand::{SeedableRng, rngs::StdRng};
#[test]
fn delay_is_within_bounds() {
let jitter = TimingJitter::new(10, 20);
let mut rng = StdRng::seed_from_u64(7);
for _ in 0..20 {
let d = jitter.sample_delay(&mut rng);
assert!(d.as_millis() >= 10 && d.as_millis() <= 20);
}
}
#[test]
fn zero_range_is_stable() {
let jitter = TimingJitter::new(12, 12);
let mut rng = StdRng::seed_from_u64(7);
assert_eq!(jitter.sample_delay(&mut rng), Duration::from_millis(12));
}
}