|
1 | 1 | use std::error::Error; |
2 | 2 | use std::future::Future; |
| 3 | +use std::marker::PhantomData; |
3 | 4 | use std::time::Duration; |
4 | 5 |
|
5 | 6 | /// A function that performs and retries the given operation according to a retry policy. |
| 7 | +/// |
| 8 | +/// **Example** |
| 9 | +/// ```rust |
| 10 | +/// # use std::time::Duration; |
| 11 | +/// # use vss_client::error::VssError; |
| 12 | +/// # use vss_client::util::retry::{ExponentialBackoffRetryPolicy, retry, RetryPolicy}; |
| 13 | +/// # |
| 14 | +/// # async fn operation() -> Result<i32, VssError> { |
| 15 | +/// # tokio::time::sleep(Duration::from_millis(10)).await; |
| 16 | +/// # Ok(42) |
| 17 | +/// # } |
| 18 | +/// # |
| 19 | +/// let retry_policy = ExponentialBackoffRetryPolicy::new(Duration::from_millis(100)); |
| 20 | +/// |
| 21 | +/// let result = retry(operation, &retry_policy); |
| 22 | +///``` |
6 | 23 | pub async fn retry<R, F, Fut, T, E>(mut operation: F, retry_policy: &R) -> Result<T, E> |
7 | 24 | where |
8 | 25 | R: RetryPolicy<E = E>, |
@@ -58,3 +75,31 @@ pub struct RetryContext<'a, E: Error> { |
58 | 75 | /// The error encountered in the previous attempt. |
59 | 76 | error: &'a E, |
60 | 77 | } |
| 78 | + |
| 79 | +/// The exponential backoff strategy is a retry approach that doubles the delay between retries. |
| 80 | +/// A combined exponential backoff and jitter strategy is recommended that is ["Exponential Backoff and Jitter"](https://aws.amazon.com/blogs/architecture/exponential-backoff-and-jitter/). |
| 81 | +/// This is helpful to avoid [Thundering Herd Problem](https://en.wikipedia.org/wiki/Thundering_herd_problem). |
| 82 | +pub struct ExponentialBackoffRetryPolicy<E> { |
| 83 | + /// The base delay duration for the backoff algorithm. First retry is `base_delay` after first attempt. |
| 84 | + base_delay: Duration, |
| 85 | + phantom: PhantomData<E>, |
| 86 | +} |
| 87 | + |
| 88 | +impl<E: Error> ExponentialBackoffRetryPolicy<E> { |
| 89 | + /// Constructs a new instance using `base_delay`. |
| 90 | + /// |
| 91 | + /// `base_delay` is the base delay duration for the backoff algorithm. First retry is `base_delay` |
| 92 | + /// after first attempt. |
| 93 | + pub fn new(base_delay: Duration) -> ExponentialBackoffRetryPolicy<E> { |
| 94 | + Self { base_delay, phantom: PhantomData } |
| 95 | + } |
| 96 | +} |
| 97 | + |
| 98 | +impl<E: Error> RetryPolicy for ExponentialBackoffRetryPolicy<E> { |
| 99 | + type E = E; |
| 100 | + fn next_delay(&self, context: &RetryContext<Self::E>) -> Option<Duration> { |
| 101 | + let backoff_factor = 2_u32.pow(context.attempts_made) - 1; |
| 102 | + let delay = self.base_delay * backoff_factor; |
| 103 | + Some(delay) |
| 104 | + } |
| 105 | +} |
0 commit comments