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 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209
use std::prelude::v1::*;
use crate::nanos::Nanos;
#[cfg(feature = "jitter")]
use rand::distributions::uniform::{SampleBorrow, SampleUniform, UniformInt, UniformSampler};
#[cfg(feature = "jitter")]
use rand::distributions::{Distribution, Uniform};
#[cfg(feature = "jitter")]
use rand::{thread_rng, Rng};
use std::ops::Add;
use std::time::Duration;
#[cfg(feature = "std")]
use std::time::Instant;
/// 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
/// # #[cfg(all(feature = "jitter", not(feature = "no_std")))]
/// # fn main() {
/// # 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))
/// # }
/// # #[cfg(not(all(feature = "jitter", not(feature = "no_std"))))]
/// # fn main() {}
/// ```
///
/// In a `std` build (the default), Jitter can also be added to an `Instant`:
///
/// ```rust
/// # #[cfg(all(feature = "jitter", feature = "std"))]
/// # fn main() {
/// # use governor::Jitter;
/// # use std::time::{Duration, Instant};
/// 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(any(not(feature = "jitter"), not(feature = "std")))] fn main() {}
/// ```
#[derive(Debug, PartialEq, Eq, Default, Clone, Copy)]
#[cfg_attr(feature = "docs", doc(cfg(jitter)))]
pub struct Jitter {
min: Nanos,
max: Nanos,
}
impl Jitter {
#[cfg(feature = "std")]
/// The "empty" jitter interval - no jitter at all.
pub(crate) const NONE: Jitter = Jitter {
min: Nanos::new(0),
max: Nanos::new(0),
};
/// Constructs a new Jitter interval, waiting at most a duration of `max`.
///
/// ```rust
/// # #[cfg(all(feature = "jitter", not(feature = "no_std")))]
/// # fn main() {
/// # use std::time::Duration;
/// # use governor::Jitter;
/// let jitter = Jitter::up_to(Duration::from_secs(20));
/// let now = Duration::from_secs(0);
/// assert!(jitter + now <= Duration::from_secs(20)); // always.
/// # }
/// # #[cfg(not(all(feature = "jitter", not(feature = "no_std"))))]
/// # fn main() {}
/// ```
#[cfg(any(all(feature = "jitter", not(feature = "no_std")), feature = "std"))]
pub fn up_to(max: Duration) -> Jitter {
Jitter {
min: Nanos::from(0),
max: max.into(),
}
}
/// Constructs a new Jitter interval, waiting at least `min` and at most `min+interval`.
#[cfg(any(all(feature = "jitter", not(feature = "no_std")), feature = "std"))]
pub fn new(min: Duration, interval: Duration) -> Jitter {
let min: Nanos = min.into();
let max: Nanos = min + Nanos::from(interval);
Jitter { min, max }
}
/// Returns a random amount of jitter within the configured interval.
#[cfg(feature = "jitter")]
pub(crate) fn get(&self) -> Nanos {
if self.min == self.max {
return self.min;
}
let uniform = Uniform::new(self.min, self.max);
uniform.sample(&mut thread_rng())
}
/// Returns a random amount of jitter within the configured interval.
#[cfg(not(feature = "jitter"))]
pub(crate) fn get(&self) -> Nanos {
self.min
}
}
/// A random distribution of nanoseconds
#[cfg(feature = "jitter")]
#[derive(Clone, Copy, Debug)]
pub struct UniformJitter(UniformInt<u64>);
#[cfg(feature = "jitter")]
impl UniformSampler for UniformJitter {
type X = Nanos;
fn new<B1, B2>(low: B1, high: B2) -> Self
where
B1: SampleBorrow<Self::X> + Sized,
B2: SampleBorrow<Self::X> + Sized,
{
UniformJitter(UniformInt::new(
low.borrow().as_u64(),
high.borrow().as_u64(),
))
}
fn new_inclusive<B1, B2>(low: B1, high: B2) -> Self
where
B1: SampleBorrow<Self::X> + Sized,
B2: SampleBorrow<Self::X> + Sized,
{
UniformJitter(UniformInt::new_inclusive(
low.borrow().as_u64(),
high.borrow().as_u64(),
))
}
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Self::X {
Nanos::from(self.0.sample(rng))
}
}
#[cfg(feature = "jitter")]
impl SampleUniform for Nanos {
type Sampler = UniformJitter;
}
impl Add<Duration> for Jitter {
type Output = Duration;
fn add(self, rhs: Duration) -> Duration {
let amount: Duration = self.get().into();
rhs + amount
}
}
impl Add<Nanos> for Jitter {
type Output = Nanos;
fn add(self, rhs: Nanos) -> Nanos {
rhs + self.get()
}
}
#[cfg(feature = "std")]
impl Add<Instant> for Jitter {
type Output = Instant;
fn add(self, rhs: Instant) -> Instant {
let amount: Duration = self.get().into();
rhs + amount
}
}
#[cfg(all(feature = "jitter", not(feature = "no_std"), test))]
mod test {
use super::*;
#[test]
fn jitter_impl_coverage() {
let basic = Jitter::up_to(Duration::from_secs(20));
let verbose = Jitter::new(Duration::from_secs(0), Duration::from_secs(20));
assert_eq!(basic, verbose);
}
#[test]
fn uniform_sampler_coverage() {
let low = Duration::from_secs(0);
let high = Duration::from_secs(20);
let sampler = UniformJitter::new_inclusive(Nanos::from(low), Nanos::from(high));
assert!(format!("{:?}", sampler).len() > 0);
assert!(format!("{:?}", sampler.clone()).len() > 0);
}
}