Skip to content

Commit 44fae32

Browse files
committed
Add JitteredRetryPolicy
1 parent 444d0c4 commit 44fae32

File tree

2 files changed

+32
-2
lines changed

2 files changed

+32
-2
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ build = "build.rs"
1515
prost = "0.11.6"
1616
reqwest = { version = "0.11.13", features = ["rustls-tls"] }
1717
tokio = { version = "1", default-features = false, features = ["time"] }
18+
rand = "0.8.5"
1819

1920
[target.'cfg(genproto)'.build-dependencies]
2021
prost-build = { version = "0.11.3" }

src/util/retry.rs

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use rand::Rng;
12
use std::error::Error;
23
use std::future::Future;
34
use std::marker::PhantomData;
@@ -21,7 +22,8 @@ use std::time::Duration;
2122
/// #
2223
/// let retry_policy = ExponentialBackoffRetryPolicy::new(Duration::from_millis(100))
2324
/// .with_max_attempts(5)
24-
/// .with_max_total_delay(Duration::from_secs(2));
25+
/// .with_max_total_delay(Duration::from_secs(2))
26+
/// .with_max_jitter(Duration::from_millis(30));
2527
///
2628
/// let result = retry(operation, &retry_policy);
2729
///```
@@ -72,6 +74,11 @@ pub trait RetryPolicy: Sized {
7274
fn with_max_total_delay(self, max_total_delay: Duration) -> MaxTotalDelayRetryPolicy<Self> {
7375
MaxTotalDelayRetryPolicy { inner_policy: self, max_total_delay }
7476
}
77+
78+
/// Returns a new `RetryPolicy` that adds jitter(random delay) to underlying policy.
79+
fn with_max_jitter(self, max_jitter: Duration) -> JitteredRetryPolicy<Self> {
80+
JitteredRetryPolicy { inner_policy: self, max_jitter }
81+
}
7582
}
7683

7784
/// Represents the context of a retry operation.
@@ -157,4 +164,26 @@ impl<T: RetryPolicy> RetryPolicy for MaxTotalDelayRetryPolicy<T> {
157164
}
158165
next_delay
159166
}
160-
}
167+
}
168+
169+
/// Decorates the given `RetryPolicy` and adds jitter (random delay) to it. This can make retries
170+
/// more spread out and less likely to all fail at once.
171+
pub struct JitteredRetryPolicy<T: RetryPolicy> {
172+
/// The underlying retry policy to use.
173+
inner_policy: T,
174+
/// The maximum amount of random jitter to apply to the delay.
175+
max_jitter: Duration,
176+
}
177+
178+
impl<T: RetryPolicy> RetryPolicy for JitteredRetryPolicy<T> {
179+
type E = T::E;
180+
fn next_delay(&self, context: &RetryContext<Self::E>) -> Option<Duration> {
181+
if let Some(base_delay) = self.inner_policy.next_delay(context) {
182+
let mut rng = rand::thread_rng();
183+
let jitter = Duration::from_micros(rng.gen_range(0..self.max_jitter.as_micros() as u64));
184+
Some(base_delay + jitter)
185+
} else {
186+
None
187+
}
188+
}
189+
}

0 commit comments

Comments
 (0)