zenith-foundation 0.1.0

Zenith 核心基础设施:统一错误类型、FrameToken 所有权令牌、FramePool、分层资源账本、恒定时间比较
Documentation
//! 统一指数退避(全 workspace 唯一实现)
//!
//! 供 Supervisor 重启退避、Proxy 熔断器恢复退避等场景复用:
//! - [`exponential_backoff`]:纯函数确定性退避(可测试、可重放)
//! - [`exponential_backoff_with_jitter`]:全抖动(Full Jitter)变体,
//!   避免多客户端同步重试造成的惊群(thundering herd)
//!
//! 全部 saturating 算术,禁止溢出回绕。

use crate::random::pseudo_random_bounded;

/// 计算指数退避(毫秒):`base * multiplier^exponent`,封顶 `cap`
///
/// # Arguments
/// * `base_ms` - 初始退避(毫秒)
/// * `multiplier` - 倍率(>= 1;< 1 时按 1 处理)
/// * `exponent` - 指数(重试次数)
/// * `cap_ms` - 上限(毫秒)
#[inline]
#[must_use]
pub fn exponential_backoff(base_ms: u64, multiplier: u64, exponent: u32, cap_ms: u64) -> u64 {
    let mult = multiplier.max(1);
    let mut value = base_ms;
    for _ in 0..exponent {
        value = value.saturating_mul(mult);
        if value >= cap_ms {
            return cap_ms;
        }
    }
    value.min(cap_ms)
}

/// 计算带全抖动的指数退避(毫秒):`[0, exponential_backoff]` 均匀随机
///
/// Full Jitter(AWS 架构中心推荐):在多实例同时退避重试时打散同步性,
/// 避免惊群。随机源为线程本地 splitmix64(无安全要求,热路径零系统调用)。
#[inline]
#[must_use]
pub fn exponential_backoff_with_jitter(
    base_ms: u64,
    multiplier: u64,
    exponent: u32,
    cap_ms: u64,
) -> u64 {
    let upper = exponential_backoff(base_ms, multiplier, exponent, cap_ms);
    pseudo_random_bounded(upper.saturating_add(1))
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_exponential_backoff_growth() {
        assert_eq!(exponential_backoff(10, 2, 0, 5000), 10);
        assert_eq!(exponential_backoff(10, 2, 1, 5000), 20);
        assert_eq!(exponential_backoff(10, 2, 2, 5000), 40);
        assert_eq!(exponential_backoff(10, 2, 9, 5000), 5000);
    }

    #[test]
    fn test_exponential_backoff_cap() {
        assert_eq!(exponential_backoff(1000, 3, 100, 5000), 5000);
    }

    #[test]
    fn test_exponential_backoff_saturating() {
        // 不得溢出回绕
        assert_eq!(exponential_backoff(u64::MAX / 2, 4, 10, u64::MAX), u64::MAX);
    }

    #[test]
    fn test_exponential_backoff_multiplier_floor() {
        // multiplier < 1 按 1 处理(恒定 base)
        assert_eq!(exponential_backoff(100, 0, 5, 5000), 100);
    }

    #[test]
    fn test_jitter_within_bounds() {
        for _ in 0..1000 {
            let v = exponential_backoff_with_jitter(10, 2, 3, 5000);
            assert!(v <= 80, "jitter 输出不得超过确定性上界, got {v}");
        }
    }

    #[test]
    fn test_jitter_zero_base() {
        assert_eq!(exponential_backoff_with_jitter(0, 2, 3, 5000), 0);
    }
}