use rand::Rng;
use std::cmp::min;
use std::time::Duration;
pub fn calculate_backoff_delay(retry_count: u32) -> Duration {
let base_delay_ms: u64 = 500; let max_delay_ms: u64 = 32_000; let delay_ms = min(
base_delay_ms.checked_shl(retry_count).unwrap_or(u64::MAX),
max_delay_ms,
);
let jitter_span = (delay_ms / 4).max(1);
let jitter_ms = rand::thread_rng().gen_range(0..jitter_span);
Duration::from_millis(min(delay_ms + jitter_ms, max_delay_ms))
}
#[cfg(test)]
mod tests {
use super::calculate_backoff_delay;
use std::time::Duration;
#[test]
fn backoff_grows_and_caps() {
for (retry, min_ms) in [(0u32, 500u64), (1, 1000), (2, 2000), (6, 32000)] {
let d = calculate_backoff_delay(retry);
assert!(d >= Duration::from_millis(min_ms), "retry {retry}: {d:?}");
assert!(d <= Duration::from_millis(32_000), "retry {retry}: {d:?}");
}
}
#[test]
fn backoff_saturates_on_large_retry_count() {
let d = calculate_backoff_delay(1000);
assert!(d <= Duration::from_millis(32_000));
}
}