use crate::random::pseudo_random_bounded;
#[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)
}
#[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() {
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);
}
}