Skip to main content

rig/http_client/
retry.rs

1//! Helpers to handle connection delays when receiving errors
2
3use super::Error;
4use std::time::Duration;
5
6pub trait RetryPolicy {
7    /// Submit a new retry delay based on the [`enum@Error`], last retry number and duration, if
8    /// available. A policy may also return `None` if it does not want to retry
9    fn retry(&self, error: &Error, last_retry: Option<(usize, Duration)>) -> Option<Duration>;
10
11    /// Set a new reconnection time if received from an event
12    fn set_reconnection_time(&mut self, duration: Duration);
13}
14
15/// A [`RetryPolicy`] which backs off exponentially
16#[derive(Debug, Clone)]
17pub struct ExponentialBackoff {
18    /// The start of the backoff
19    pub start: Duration,
20    /// The factor of which to backoff by
21    pub factor: f64,
22    /// The maximum duration to delay
23    pub max_duration: Option<Duration>,
24    /// The maximum number of retries before giving up
25    pub max_retries: Option<usize>,
26}
27
28impl ExponentialBackoff {
29    /// Create a new exponential backoff retry policy
30    pub const fn new(
31        start: Duration,
32        factor: f64,
33        max_duration: Option<Duration>,
34        max_retries: Option<usize>,
35    ) -> Self {
36        Self {
37            start,
38            factor,
39            max_duration,
40            max_retries,
41        }
42    }
43}
44
45impl RetryPolicy for ExponentialBackoff {
46    fn retry(&self, _error: &Error, last_retry: Option<(usize, Duration)>) -> Option<Duration> {
47        if let Some((retry_num, last_duration)) = last_retry {
48            if self
49                .max_retries
50                .is_none_or(|max_retries| retry_num < max_retries)
51            {
52                let duration = last_duration.mul_f64(self.factor);
53                if let Some(max_duration) = self.max_duration {
54                    Some(duration.min(max_duration))
55                } else {
56                    Some(duration)
57                }
58            } else {
59                None
60            }
61        } else {
62            Some(self.start)
63        }
64    }
65    fn set_reconnection_time(&mut self, duration: Duration) {
66        self.start = duration;
67        if let Some(max_duration) = self.max_duration {
68            self.max_duration = Some(max_duration.max(duration))
69        }
70    }
71}
72
73/// A [`RetryPolicy`] which always emits the same delay
74#[derive(Debug, Clone)]
75pub struct Constant {
76    /// The delay to return
77    pub delay: Duration,
78    /// The maximum number of retries to return before giving up
79    pub max_retries: Option<usize>,
80}
81
82impl Constant {
83    /// Create a new constant retry policy
84    pub const fn new(delay: Duration, max_retries: Option<usize>) -> Self {
85        Self { delay, max_retries }
86    }
87}
88
89impl RetryPolicy for Constant {
90    fn retry(&self, _error: &Error, last_retry: Option<(usize, Duration)>) -> Option<Duration> {
91        if let Some((retry_num, _)) = last_retry {
92            if self
93                .max_retries
94                .is_none_or(|max_retries| retry_num < max_retries)
95            {
96                Some(self.delay)
97            } else {
98                None
99            }
100        } else {
101            Some(self.delay)
102        }
103    }
104    fn set_reconnection_time(&mut self, duration: Duration) {
105        self.delay = duration;
106    }
107}
108
109/// A [`RetryPolicy`] which never retries
110#[derive(Debug, Clone, Copy, Default)]
111pub struct Never;
112
113impl RetryPolicy for Never {
114    fn retry(&self, _error: &Error, _last_retry: Option<(usize, Duration)>) -> Option<Duration> {
115        None
116    }
117    fn set_reconnection_time(&mut self, _duration: Duration) {}
118}
119
120/// The default [`RetryPolicy`] when initializing an event source
121pub const DEFAULT_RETRY: ExponentialBackoff = ExponentialBackoff::new(
122    Duration::from_millis(300),
123    2.,
124    Some(Duration::from_secs(5)),
125    None,
126);