1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
use crate::lib::*;

/// An interval specification for deviating from the nominal wait time.
///
/// Jitter can be added to wait time `Duration`s to ensure that multiple tasks waiting on the same
/// rate limit don't wake up at the same time and attempt to measure at the same time.
///
/// Methods on rate limiters that work asynchronously like
/// [`DirectRateLimiter.until_ready_with_jitter`](struct.DirectRateLimiter.html#method.until_ready_with_jitter)
/// exist to automatically apply jitter to wait periods, thereby reducing the chance of a
/// thundering herd problem.  
///
/// # Examples
///
/// Jitter can be added manually to a `Duration`:
///
/// ```rust
/// # use governor::Jitter;
/// # use std::time::Duration;
/// let reference = Duration::from_secs(24);
/// let jitter = Jitter::new(Duration::from_secs(1), Duration::from_secs(1));
/// let result = jitter + reference;
/// assert!(result >= reference + Duration::from_secs(1));
/// assert!(result < reference + Duration::from_secs(2))
/// ```
///
/// In a `std` build (the default), Jitter can also be added to an `Instant`:
///
/// ```rust
/// # use governor::Jitter;
/// # use std::time::{Duration, Instant};
/// # #[cfg(feature = "std")]
/// # fn main() {
/// let reference = Instant::now();
/// let jitter = Jitter::new(Duration::from_secs(1), Duration::from_secs(1));
/// let result = jitter + reference;
/// assert!(result >= reference + Duration::from_secs(1));
/// assert!(result < reference + Duration::from_secs(2))
/// # }
/// # #[cfg(not(feature = "std"))] fn main() {}
/// ```
#[derive(Debug, PartialEq, Default, Clone, Copy)]
pub struct Jitter {
    min: Duration,
    interval: Duration,
}

impl Jitter {
    #[cfg(feature = "std")]
    /// The "empty" jitter interval - no jitter at all.
    pub(crate) const NONE: Jitter = Jitter {
        min: Duration::from_secs(0),
        interval: Duration::from_secs(0),
    };

    /// Constructs a new Jitter interval, waiting at most a duration of `max`.
    pub fn up_to(max: Duration) -> Jitter {
        Jitter {
            min: Duration::new(0, 0),
            interval: max,
        }
    }

    /// Constructs a new Jitter interval, waiting at least `min` and at most `min+interval`.
    pub const fn new(min: Duration, interval: Duration) -> Jitter {
        Jitter { min, interval }
    }

    /// Returns a random amount of jitter within the configured interval.
    pub(crate) fn get(&self) -> Duration {
        let range = rand::random::<f32>();
        self.min + self.interval.mul_f32(range)
    }
}

impl Add<Duration> for Jitter {
    type Output = Duration;

    fn add(self, rhs: Duration) -> Duration {
        self.get() + rhs
    }
}

#[cfg(feature = "std")]
impl Add<Instant> for Jitter {
    type Output = Instant;

    fn add(self, rhs: Instant) -> Instant {
        rhs + self.get()
    }
}