headless_lms_utils/
backoff.rs1use rand::RngExt;
5
6use crate::prelude::*;
7
8pub fn exponential_backoff_secs(base_secs: i64, max_secs: i64, attempt: i32) -> i64 {
11 let shift = attempt.clamp(0, 62) as u32;
12 base_secs
13 .saturating_mul(2_i64.saturating_pow(shift))
14 .min(max_secs)
15}
16
17pub fn next_attempt_at(now: DateTime<Utc>, delay_secs: i64, jitter_max_secs: i64) -> DateTime<Utc> {
20 let jitter = rand::rng().random_range(0..=jitter_max_secs);
21 now + chrono::Duration::seconds(delay_secs.saturating_add(jitter))
22}
23
24pub fn window_expired(
26 reference: Option<DateTime<Utc>>,
27 now: DateTime<Utc>,
28 max_age_secs: i64,
29) -> bool {
30 reference.is_some_and(|reference| (now - reference).num_seconds() >= max_age_secs)
31}