1use chrono::{DateTime, Duration, Utc};
2use std::time;
3
4pub fn next_retry_at(attempts: u32) -> DateTime<Utc> {
5 let base_secs: u64 = 10;
6 let cap_secs: u64 = 3600;
7
8 let backoff = base_secs
9 .saturating_mul(2u64.saturating_pow(attempts))
10 .min(cap_secs);
11
12 let jitter = rand::random::<u64>() % (backoff / 4 + 1);
13 let delay = time::Duration::from_secs(backoff + jitter);
14 Utc::now() + Duration::from_std(delay).unwrap_or_default()
15}
16
17#[cfg(test)]
18mod tests {
19 use super::*;
20
21 #[test]
22 fn test_exponential_growth() {
23 let t0 = next_retry_at(0);
24 let t1 = next_retry_at(1);
25 let t2 = next_retry_at(2);
26
27 assert!(t1 > t0);
28 assert!(t2 > t1);
29 }
30
31 #[test]
32 fn test_backoff_cap() {
33 let t100 = next_retry_at(100);
34 let t101 = next_retry_at(101);
35
36 let diff = t101.signed_duration_since(t100);
37 assert!(diff.num_seconds() < 3600);
38 }
39}