execution_policy/retry/
jitter.rs1use std::time::Duration;
2
3#[derive(Debug, Clone, Copy, PartialEq, Eq)]
5pub enum Jitter {
6 None,
8 Full,
10 Equal,
12}
13
14impl Jitter {
15 pub(crate) fn apply(&self, base: Duration, rng: u64) -> Duration {
17 if base.is_zero() {
18 return base;
19 }
20 let nanos = base.as_nanos() as u64;
21 let frac = |span: u64| -> u64 {
23 if span == 0 {
24 0
25 } else {
26 (((rng >> 11) as u128 * span as u128) >> 53) as u64
27 }
28 };
29 match self {
30 Jitter::None => base,
31 Jitter::Full => Duration::from_nanos(frac(nanos)),
32 Jitter::Equal => {
33 let half = nanos / 2;
34 Duration::from_nanos(half + frac(half))
35 }
36 }
37 }
38}
39
40#[cfg(test)]
41mod tests {
42 use super::*;
43
44 #[test]
45 fn none_is_identity() {
46 let d = Duration::from_millis(100);
47 assert_eq!(Jitter::None.apply(d, u64::MAX), d);
48 }
49
50 #[test]
51 fn full_is_within_bounds() {
52 let d = Duration::from_millis(100);
53 for rng in [0u64, 1, 12345, u64::MAX / 2, u64::MAX] {
54 let j = Jitter::Full.apply(d, rng);
55 assert!(j <= d, "full jitter {j:?} exceeded base {d:?}");
56 }
57 }
58
59 #[test]
60 fn equal_is_in_upper_half() {
61 let d = Duration::from_millis(100);
62 for rng in [0u64, 999, u64::MAX] {
63 let j = Jitter::Equal.apply(d, rng);
64 assert!(
65 j >= d / 2 && j <= d,
66 "equal jitter {j:?} out of [50ms,100ms]"
67 );
68 }
69 }
70
71 #[test]
72 fn zero_base_stays_zero() {
73 assert_eq!(Jitter::Full.apply(Duration::ZERO, 42), Duration::ZERO);
74 }
75}