nautilus_polymarket/common/
retry.rs1use std::time::Duration;
19
20use rand::RngExt;
21
22#[must_use]
29pub fn auto_load_retry_delay(attempt: u32, base_secs: f64, max_secs: f64) -> Duration {
30 auto_load_retry_delay_with_jitter(
31 attempt,
32 base_secs,
33 max_secs,
34 rand::rng().random_range(0.0..1.0),
35 )
36}
37
38#[must_use]
39pub(crate) fn auto_load_retry_delay_with_jitter(
40 attempt: u32,
41 base_secs: f64,
42 max_secs: f64,
43 jitter_unit: f64,
44) -> Duration {
45 let base = base_secs.max(0.0);
46 let max = max_secs.max(base);
47 let raw = base * 2f64.powi(attempt.min(30) as i32);
48 let capped = raw.min(max);
49 let total = capped + capped * 0.25 * jitter_unit.clamp(0.0, 1.0);
50 Duration::from_secs_f64(total.max(0.0))
51}
52
53#[cfg(test)]
54mod tests {
55 use rstest::rstest;
56
57 use super::*;
58
59 #[rstest]
60 fn test_attempt_zero_no_jitter_returns_base() {
61 let d = auto_load_retry_delay_with_jitter(0, 5.0, 15.0, 0.0);
62 assert_eq!(d, Duration::from_secs_f64(5.0));
63 }
64
65 #[rstest]
66 fn test_attempt_zero_full_jitter_returns_base_plus_25_percent() {
67 let d = auto_load_retry_delay_with_jitter(0, 5.0, 15.0, 1.0);
68 assert!((d.as_secs_f64() - 6.25).abs() < 1e-9);
69 }
70
71 #[rstest]
72 fn test_attempt_exponentiates_until_max_cap() {
73 let d = auto_load_retry_delay_with_jitter(2, 5.0, 15.0, 0.0);
74 assert_eq!(d, Duration::from_secs_f64(15.0));
75 }
76
77 #[rstest]
78 fn test_capped_delay_still_takes_jitter() {
79 let d = auto_load_retry_delay_with_jitter(2, 5.0, 15.0, 1.0);
80 assert!((d.as_secs_f64() - 18.75).abs() < 1e-9);
81 }
82
83 #[rstest]
84 fn test_high_attempt_count_does_not_overflow() {
85 let d = auto_load_retry_delay_with_jitter(50, 5.0, 15.0, 0.0);
86 assert_eq!(d, Duration::from_secs_f64(15.0));
87 }
88
89 #[rstest]
90 fn test_zero_base_returns_zero() {
91 let d = auto_load_retry_delay_with_jitter(3, 0.0, 15.0, 0.5);
92 assert_eq!(d, Duration::ZERO);
93 }
94}