retry_backoff/backoffs/
google_cloud_workflows.rs

1//
2#[derive(Debug, Clone, PartialEq)]
3pub struct Backoff {
4    pub initial_delay_secs: f32,
5    pub max_delay_secs: f32,
6    pub multiplier: f32,
7}
8
9impl Default for Backoff {
10    fn default() -> Self {
11        default_backoff()
12    }
13}
14
15/// [Object: retry.default_backoff](https://cloud.google.com/workflows/docs/reference/stdlib/retry/default_backoff)
16pub fn default_backoff() -> Backoff {
17    Backoff::new(1.0, 60.0, 1.25)
18}
19
20impl Backoff {
21    pub fn new(initial_delay_secs: f32, max_delay_secs: f32, multiplier: f32) -> Self {
22        Self {
23            initial_delay_secs,
24            max_delay_secs,
25            multiplier,
26        }
27    }
28
29    #[cfg(feature = "std")]
30    pub fn delay(&self, attempts: usize) -> core::time::Duration {
31        use core::{cmp::min, time::Duration};
32
33        match attempts {
34            0 => unreachable!(),
35            1 => Duration::from_millis((self.initial_delay_secs * 1000.0).round() as u64),
36            n => Duration::from_millis(min(
37                (self.multiplier.powi((n - 1) as i32) * 1000.0).round() as u64,
38                (self.max_delay_secs * 1000.0).round() as u64,
39            )),
40        }
41    }
42}
43
44//
45#[cfg(feature = "std")]
46impl crate::retry_backoff::RetryBackoff for Backoff {
47    fn delay(&self, attempts: usize) -> core::time::Duration {
48        Self::delay(self, attempts)
49    }
50
51    fn name(&self) -> &str {
52        "GoogleCloudWorkflows"
53    }
54}
55
56#[cfg(test)]
57mod tests {
58    use super::*;
59
60    #[test]
61    fn test_default_backoff() {
62        let backoff = default_backoff();
63        assert_eq!(backoff.initial_delay_secs, 1.0);
64        assert_eq!(backoff.max_delay_secs, 60.0);
65        assert_eq!(backoff.multiplier, 1.25);
66
67        assert_eq!(Backoff::default(), default_backoff());
68    }
69
70    #[cfg(feature = "std")]
71    #[test]
72    fn test_delay() {
73        use core::time::Duration;
74
75        // Ref https://cloud.google.com/workflows/docs/reference/syntax/retrying#try-retry
76        let backoff = Backoff::new(1.0, 60.0, 2.0);
77
78        for (attempts, secs) in &[
79            (1, 1),
80            (2, 2),
81            (3, 4),
82            (4, 8),
83            (5, 16),
84            (6, 32),
85            (7, 60),
86            (8, 60),
87            (9, 60),
88            (10, 60),
89        ] {
90            assert_eq!(backoff.delay(*attempts), Duration::from_secs(*secs));
91        }
92    }
93
94    #[cfg(feature = "std")]
95    #[test]
96    fn test_impl_retry_backoff() {
97        use core::time::Duration;
98
99        use crate::retry_backoff::RetryBackoff;
100
101        // Ref https://cloud.google.com/workflows/docs/reference/syntax/retrying#try-retry
102        let backoff = Backoff::new(1.0, 60.0, 2.0);
103
104        for (attempts, secs) in &[
105            (1, 1),
106            (2, 2),
107            (3, 4),
108            (4, 8),
109            (5, 16),
110            (6, 32),
111            (7, 60),
112            (8, 60),
113            (9, 60),
114            (10, 60),
115        ] {
116            assert_eq!(
117                RetryBackoff::delay(&backoff, *attempts),
118                Duration::from_secs(*secs)
119            );
120        }
121        assert_eq!(RetryBackoff::name(&backoff), "GoogleCloudWorkflows");
122    }
123}