Skip to main content

barter_integration/socket/
backoff.rs

1/// Calculates backoff duration between socket reconnection attempts.
2pub trait ReconnectBackoff {
3    /// Returns the backoff duration to wait before the next reconnection attempt.
4    fn reconnect_backoff(&mut self, reconnection_attempt: u32) -> std::time::Duration;
5}
6
7impl<F> ReconnectBackoff for F
8where
9    F: FnMut(u32) -> std::time::Duration,
10{
11    #[inline]
12    fn reconnect_backoff(&mut self, reconnection_attempt: u32) -> std::time::Duration {
13        self(reconnection_attempt)
14    }
15}
16
17/// Default exponential backoff strategy with a maximum delay cap.
18///
19/// Uses 2^n + 10ms formula, capped at 2^15ms.
20#[derive(Debug, Copy, Clone, PartialEq)]
21pub struct DefaultBackoff;
22
23impl ReconnectBackoff for DefaultBackoff {
24    fn reconnect_backoff(&mut self, reconnection_attempt: u32) -> std::time::Duration {
25        match reconnection_attempt {
26            0 => std::time::Duration::ZERO,
27            n => std::time::Duration::from_millis(2u64.pow(n.min(15)) + 10),
28        }
29    }
30}
31
32#[cfg(test)]
33mod tests {
34    use super::*;
35    use std::time::Duration;
36
37    #[test]
38    fn test_default_backoff_zero() {
39        let mut backoff = DefaultBackoff;
40        assert_eq!(backoff.reconnect_backoff(0), Duration::ZERO);
41    }
42
43    #[test]
44    fn test_default_backoff_first_attempt() {
45        let mut backoff = DefaultBackoff;
46        assert_eq!(backoff.reconnect_backoff(1), Duration::from_millis(12));
47    }
48
49    #[test]
50    fn test_default_backoff_at_cap() {
51        let mut backoff = DefaultBackoff;
52        // 2^15 + 10 = 32768 + 10 = 32778
53        assert_eq!(backoff.reconnect_backoff(15), Duration::from_millis(32778));
54    }
55
56    #[test]
57    fn test_default_backoff_beyond_cap() {
58        let mut backoff = DefaultBackoff;
59        // n=16 should be capped at 2^15 + 10
60        assert_eq!(backoff.reconnect_backoff(16), Duration::from_millis(32778));
61    }
62
63    #[test]
64    fn test_default_backoff_u32_max() {
65        let mut backoff = DefaultBackoff;
66        assert_eq!(
67            backoff.reconnect_backoff(u32::MAX),
68            Duration::from_millis(32778)
69        );
70    }
71
72    #[test]
73    fn test_closure_backoff() {
74        let mut backoff = |n: u32| Duration::from_secs(n as u64);
75        assert_eq!(backoff.reconnect_backoff(5), Duration::from_secs(5));
76    }
77}