Skip to main content

zenith_foundation/
backoff.rs

1//! 统一指数退避(全 workspace 唯一实现)
2//!
3//! 供 Supervisor 重启退避、Proxy 熔断器恢复退避等场景复用:
4//! - [`exponential_backoff`]:纯函数确定性退避(可测试、可重放)
5//! - [`exponential_backoff_with_jitter`]:全抖动(Full Jitter)变体,
6//!   避免多客户端同步重试造成的惊群(thundering herd)
7//!
8//! 全部 saturating 算术,禁止溢出回绕。
9
10use crate::random::pseudo_random_bounded;
11
12/// 计算指数退避(毫秒):`base * multiplier^exponent`,封顶 `cap`
13///
14/// # Arguments
15/// * `base_ms` - 初始退避(毫秒)
16/// * `multiplier` - 倍率(>= 1;< 1 时按 1 处理)
17/// * `exponent` - 指数(重试次数)
18/// * `cap_ms` - 上限(毫秒)
19#[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/// 计算带全抖动的指数退避(毫秒):`[0, exponential_backoff]` 均匀随机
34///
35/// Full Jitter(AWS 架构中心推荐):在多实例同时退避重试时打散同步性,
36/// 避免惊群。随机源为线程本地 splitmix64(无安全要求,热路径零系统调用)。
37#[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        // 不得溢出回绕
69        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        // multiplier < 1 按 1 处理(恒定 base)
75        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}