tryhard/
backoff_strategies.rs

1//! The types of backoff strategies that are supported
2
3use crate::RetryPolicy;
4use std::time::Duration;
5
6/// Trait for computing the amount of delay between attempts.
7pub trait BackoffStrategy<'a, E> {
8    /// The delay type. Will normally be either [`Duration`] or [`RetryPolicy`].
9    ///
10    /// [`Duration`]: https://doc.rust-lang.org/stable/std/time/struct.Duration.html
11    /// [`RetryPolicy`]: ../enum.RetryPolicy.html
12    type Output;
13
14    /// Compute the amount of delay given the number of attempts so far and the most previous
15    /// error.
16    fn delay(&mut self, attempt: u32, error: &'a E) -> Self::Output;
17}
18
19/// No backoff. This will make the future be retried immediately without any delay in between
20/// attempts.
21#[derive(Debug, Clone, Copy)]
22pub struct NoBackoff;
23
24impl<'a, E> BackoffStrategy<'a, E> for NoBackoff {
25    type Output = Duration;
26
27    #[inline]
28    fn delay(&mut self, _attempt: u32, _error: &'a E) -> Duration {
29        Duration::new(0, 0)
30    }
31}
32
33/// Exponential backoff. The delay will double each time.
34#[derive(Debug, Clone, Copy)]
35pub struct ExponentialBackoff {
36    pub(crate) delay: Duration,
37}
38
39impl ExponentialBackoff {
40    /// Create a new `ExponentialBackoff` with an initial delay.
41    pub fn new(initial_delay: Duration) -> Self {
42        Self {
43            delay: initial_delay,
44        }
45    }
46}
47
48impl<'a, E> BackoffStrategy<'a, E> for ExponentialBackoff {
49    type Output = Duration;
50
51    #[inline]
52    fn delay(&mut self, _attempt: u32, _error: &'a E) -> Duration {
53        let prev_delay = self.delay;
54        self.delay = self.delay.saturating_mul(2);
55        prev_delay
56    }
57}
58
59/// Fixed backoff. The delay wont change between attempts.
60#[derive(Debug, Clone, Copy)]
61pub struct FixedBackoff {
62    pub(crate) delay: Duration,
63}
64
65impl FixedBackoff {
66    /// Create a new `FixedBackoff` with an initial delay.
67    pub fn new(initial_delay: Duration) -> Self {
68        Self {
69            delay: initial_delay,
70        }
71    }
72}
73
74impl<'a, E> BackoffStrategy<'a, E> for FixedBackoff {
75    type Output = Duration;
76
77    #[inline]
78    fn delay(&mut self, _attempt: u32, _error: &'a E) -> Duration {
79        self.delay
80    }
81}
82
83/// Linear backoff. The delay will scale linearly with the number of attempts.
84#[derive(Debug, Clone, Copy)]
85pub struct LinearBackoff {
86    pub(crate) delay: Duration,
87}
88
89impl LinearBackoff {
90    /// Create a new `LinearBackoff` with an initial delay.
91    pub fn new(initial_delay: Duration) -> Self {
92        Self {
93            delay: initial_delay,
94        }
95    }
96}
97
98impl<'a, E> BackoffStrategy<'a, E> for LinearBackoff {
99    type Output = Duration;
100
101    #[inline]
102    fn delay(&mut self, attempt: u32, _error: &'a E) -> Duration {
103        self.delay.saturating_mul(attempt)
104    }
105}
106
107impl<'a, F, E, T> BackoffStrategy<'a, E> for F
108where
109    E: 'a,
110    F: FnMut(u32, &'a E) -> T,
111    T: Into<RetryPolicy>,
112{
113    type Output = RetryPolicy;
114
115    #[inline]
116    fn delay(&mut self, attempt: u32, error: &'a E) -> RetryPolicy {
117        self(attempt, error).into()
118    }
119}