Skip to main content

retry_block/delay/
random.rs

1use std::{
2    ops::{Range as StdRange, RangeInclusive},
3    time::Duration,
4};
5
6use rand::{
7    distributions::{Distribution, Uniform},
8    thread_rng,
9};
10
11/// Each retry uses a duration randomly chosen from a range. (need `random` feature)
12#[derive(Debug, Clone)]
13pub struct Range {
14    distribution: Uniform<u64>,
15}
16
17impl Range {
18    /// Create a new `Range` between the given millisecond durations, excluding the maximum value.
19    ///
20    /// # Panics
21    ///
22    /// Panics if the minimum is greater than or equal to the maximum.
23    pub fn from_millis_exclusive(minimum: u64, maximum: u64) -> Self {
24        Range {
25            distribution: Uniform::new(minimum, maximum),
26        }
27    }
28
29    /// Create a new `Range` between the given millisecond durations, including the maximum value.
30    ///
31    /// # Panics
32    ///
33    /// Panics if the minimum is greater than or equal to the maximum.
34    pub fn from_millis_inclusive(minimum: u64, maximum: u64) -> Self {
35        Range {
36            distribution: Uniform::new_inclusive(minimum, maximum),
37        }
38    }
39}
40
41impl Iterator for Range {
42    type Item = Duration;
43
44    fn next(&mut self) -> Option<Duration> {
45        Some(Duration::from_millis(
46            self.distribution.sample(&mut thread_rng()),
47        ))
48    }
49}
50
51impl From<StdRange<Duration>> for Range {
52    fn from(range: StdRange<Duration>) -> Self {
53        Self::from_millis_exclusive(range.start.as_millis() as u64, range.end.as_millis() as u64)
54    }
55}
56
57impl From<RangeInclusive<Duration>> for Range {
58    fn from(range: RangeInclusive<Duration>) -> Self {
59        Self::from_millis_inclusive(
60            range.start().as_millis() as u64,
61            range.end().as_millis() as u64,
62        )
63    }
64}
65
66/// Apply full random jitter to a duration. (need `random` feature)
67pub fn jitter(duration: Duration) -> Duration {
68    jitter_rng(duration, &mut thread_rng())
69}
70
71pub fn jitter_rng(duration: Duration, rng: &mut impl rand::Rng) -> Duration {
72    duration.mul_f64(rng.gen())
73}
74
75#[cfg(test)]
76mod test {
77    use crate::delay::jitter_rng;
78    use rand::SeedableRng;
79    use rand_xorshift::XorShiftRng;
80    use std::time::Duration;
81    #[test]
82    fn test_jitter_1_sec() {
83        let mut rng = XorShiftRng::seed_from_u64(0);
84
85        let duration = Duration::from_millis(1000);
86        assert_ne!(
87            jitter_rng(duration, &mut rng),
88            jitter_rng(duration, &mut rng)
89        )
90    }
91}