Skip to main content

rig_core/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/// The default [`RetryPolicy`] when initializing an event source
74pub const DEFAULT_RETRY: ExponentialBackoff = ExponentialBackoff::new(
75    Duration::from_millis(300),
76    2.,
77    Some(Duration::from_secs(5)),
78    None,
79);