pub(crate) const MAX_RETRY_COUNT: i64 = 8;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum RetryDecision {
Retry { next_count: i64 },
Abandon { next_count: i64 },
}
pub(crate) const fn decide_retry(retry_count: i64) -> RetryDecision {
let next_count = retry_count + 1;
if next_count >= MAX_RETRY_COUNT {
RetryDecision::Abandon { next_count }
} else {
RetryDecision::Retry { next_count }
}
}
pub(crate) fn backoff_delay_ms(next_count: i64) -> i64 {
let shift = u32::try_from(next_count.clamp(0, 5)).unwrap_or(0);
60_000_i64.saturating_mul(1_i64.checked_shl(shift).unwrap_or(32))
}
pub(crate) fn now_unix_ms() -> i64 {
use std::time::{SystemTime, UNIX_EPOCH};
SystemTime::now()
.duration_since(UNIX_EPOCH)
.map_or(0, |d| i64::try_from(d.as_millis()).unwrap_or(i64::MAX))
}
pub(crate) fn truncate(s: &str, max_chars: usize) -> String {
if s.chars().count() <= max_chars {
return s.to_owned();
}
s.chars().take(max_chars).collect()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn decide_retry_matches_prior_inline_arithmetic() {
for rc in 0..(MAX_RETRY_COUNT - 1) {
assert_eq!(
decide_retry(rc),
RetryDecision::Retry { next_count: rc + 1 },
"retry_count {rc} should still retry"
);
}
assert_eq!(
decide_retry(MAX_RETRY_COUNT - 1),
RetryDecision::Abandon {
next_count: MAX_RETRY_COUNT
}
);
assert_eq!(
decide_retry(MAX_RETRY_COUNT),
RetryDecision::Abandon {
next_count: MAX_RETRY_COUNT + 1
}
);
}
#[test]
fn max_retry_count_is_unified_to_eight() {
assert_eq!(MAX_RETRY_COUNT, 8);
}
#[test]
fn backoff_delay_matches_observations_formula() {
assert_eq!(backoff_delay_ms(0), 60_000);
assert_eq!(backoff_delay_ms(1), 120_000);
assert_eq!(backoff_delay_ms(2), 240_000);
assert_eq!(backoff_delay_ms(3), 480_000);
assert_eq!(backoff_delay_ms(4), 960_000);
assert_eq!(backoff_delay_ms(5), 1_920_000);
assert_eq!(backoff_delay_ms(6), 1_920_000);
assert_eq!(backoff_delay_ms(99), 1_920_000);
assert_eq!(backoff_delay_ms(-1), 60_000);
}
#[test]
fn truncate_returns_short_input_verbatim_and_clips_long_input() {
assert_eq!(truncate("abc", 2048), "abc");
let long: String = "x".repeat(5000);
let clipped = truncate(&long, 2048);
assert_eq!(clipped.chars().count(), 2048);
let inline: String = long.chars().take(2048).collect();
assert_eq!(clipped, inline);
}
#[test]
fn now_unix_ms_is_monotonic_nonzero() {
let a = now_unix_ms();
assert!(a > 0);
let b = now_unix_ms();
assert!(b >= a);
}
}