zenith_foundation/
backoff.rs1use crate::random::pseudo_random_bounded;
11
12#[inline]
20#[must_use]
21pub fn exponential_backoff(base_ms: u64, multiplier: u64, exponent: u32, cap_ms: u64) -> u64 {
22 let mult = multiplier.max(1);
23 let mut value = base_ms;
24 for _ in 0..exponent {
25 value = value.saturating_mul(mult);
26 if value >= cap_ms {
27 return cap_ms;
28 }
29 }
30 value.min(cap_ms)
31}
32
33#[inline]
38#[must_use]
39pub fn exponential_backoff_with_jitter(
40 base_ms: u64,
41 multiplier: u64,
42 exponent: u32,
43 cap_ms: u64,
44) -> u64 {
45 let upper = exponential_backoff(base_ms, multiplier, exponent, cap_ms);
46 pseudo_random_bounded(upper.saturating_add(1))
47}
48
49#[cfg(test)]
50mod tests {
51 use super::*;
52
53 #[test]
54 fn test_exponential_backoff_growth() {
55 assert_eq!(exponential_backoff(10, 2, 0, 5000), 10);
56 assert_eq!(exponential_backoff(10, 2, 1, 5000), 20);
57 assert_eq!(exponential_backoff(10, 2, 2, 5000), 40);
58 assert_eq!(exponential_backoff(10, 2, 9, 5000), 5000);
59 }
60
61 #[test]
62 fn test_exponential_backoff_cap() {
63 assert_eq!(exponential_backoff(1000, 3, 100, 5000), 5000);
64 }
65
66 #[test]
67 fn test_exponential_backoff_saturating() {
68 assert_eq!(exponential_backoff(u64::MAX / 2, 4, 10, u64::MAX), u64::MAX);
70 }
71
72 #[test]
73 fn test_exponential_backoff_multiplier_floor() {
74 assert_eq!(exponential_backoff(100, 0, 5, 5000), 100);
76 }
77
78 #[test]
79 fn test_jitter_within_bounds() {
80 for _ in 0..1000 {
81 let v = exponential_backoff_with_jitter(10, 2, 3, 5000);
82 assert!(v <= 80, "jitter 输出不得超过确定性上界, got {v}");
83 }
84 }
85
86 #[test]
87 fn test_jitter_zero_base() {
88 assert_eq!(exponential_backoff_with_jitter(0, 2, 3, 5000), 0);
89 }
90}