rs-zero 0.2.8

Rust-first microservice framework inspired by go-zero engineering practices
Documentation
use std::{
    collections::hash_map::DefaultHasher,
    hash::{Hash, Hasher},
    time::Duration,
};

/// Applies deterministic per-key TTL jitter.
///
/// `ratio` is clamped to `[0.0, 1.0]`. A ratio of `0.05` produces a TTL in
/// approximately `[0.95, 1.05] * base`, which reduces synchronized expiry.
pub fn jitter_ttl(base: Duration, ratio: f64, seed: impl Hash) -> Duration {
    if base.is_zero() {
        return base;
    }

    let ratio = ratio.clamp(0.0, 1.0);
    if ratio == 0.0 {
        return base;
    }

    let mut hasher = DefaultHasher::new();
    seed.hash(&mut hasher);
    let bucket = (hasher.finish() % 10_001) as f64 / 10_000.0;
    let factor = 1.0 - ratio + bucket * ratio * 2.0;
    Duration::from_secs_f64((base.as_secs_f64() * factor).max(0.001))
}

#[cfg(test)]
mod tests {
    use std::time::Duration;

    use super::jitter_ttl;

    #[test]
    fn jitter_stays_inside_ratio_bounds() {
        let ttl = jitter_ttl(Duration::from_secs(100), 0.05, "user:1");
        assert!(ttl >= Duration::from_secs(95));
        assert!(ttl <= Duration::from_secs(105));
    }

    #[test]
    fn zero_ratio_keeps_original_ttl() {
        let ttl = Duration::from_secs(60);
        assert_eq!(jitter_ttl(ttl, 0.0, "key"), ttl);
    }
}