11use std:: time:: { Duration , Instant } ;
22
3+ use rand:: { thread_rng, Rng } ;
4+
35pub ( crate ) trait RetryStrategy {
46 /// Return the next amount of time a failed request should delay before re-attempting.
57 fn next_delay ( & mut self , current_time : Instant ) -> Duration ;
@@ -17,18 +19,25 @@ pub(crate) struct BackoffRetry {
1719 base_delay : Duration ,
1820 max_delay : Duration ,
1921 backoff_factor : u32 ,
22+ include_jitter : bool ,
2023
2124 reset_interval : Duration ,
2225 next_delay : Duration ,
2326 good_since : Option < Instant > ,
2427}
2528
2629impl BackoffRetry {
27- pub fn new ( base_delay : Duration , max_delay : Duration , backoff_factor : u32 ) -> Self {
30+ pub fn new (
31+ base_delay : Duration ,
32+ max_delay : Duration ,
33+ backoff_factor : u32 ,
34+ include_jitter : bool ,
35+ ) -> Self {
2836 Self {
2937 base_delay,
3038 max_delay,
3139 backoff_factor,
40+ include_jitter,
3241 reset_interval : DEFAULT_RESET_RETRY_INTERVAL ,
3342 next_delay : base_delay,
3443 good_since : None ,
@@ -38,18 +47,22 @@ impl BackoffRetry {
3847
3948impl RetryStrategy for BackoffRetry {
4049 fn next_delay ( & mut self , current_time : Instant ) -> Duration {
41- let mut next_delay = self . next_delay ;
50+ let mut current_delay = self . next_delay ;
4251
4352 if let Some ( good_since) = self . good_since {
4453 if current_time - good_since >= self . reset_interval {
45- next_delay = self . base_delay ;
54+ current_delay = self . base_delay ;
4655 }
4756 }
4857
4958 self . good_since = None ;
50- self . next_delay = std:: cmp:: min ( self . max_delay , next_delay * self . backoff_factor ) ;
59+ self . next_delay = std:: cmp:: min ( self . max_delay , current_delay * self . backoff_factor ) ;
5160
52- next_delay
61+ if self . include_jitter {
62+ thread_rng ( ) . gen_range ( current_delay / 2 ..=current_delay)
63+ } else {
64+ current_delay
65+ }
5366 }
5467
5568 fn change_base_delay ( & mut self , base_delay : Duration ) {
@@ -76,7 +89,7 @@ mod tests {
7689 #[ test]
7790 fn test_fixed_retry ( ) {
7891 let base = Duration :: from_secs ( 10 ) ;
79- let mut retry = BackoffRetry :: new ( base, Duration :: from_secs ( 30 ) , 1 ) ;
92+ let mut retry = BackoffRetry :: new ( base, Duration :: from_secs ( 30 ) , 1 , false ) ;
8093 let start = Instant :: now ( ) - Duration :: from_secs ( 60 ) ;
8194
8295 assert_eq ! ( retry. next_delay( start) , base) ;
@@ -87,7 +100,7 @@ mod tests {
87100 #[ test]
88101 fn test_able_to_reset_base_delay ( ) {
89102 let base = Duration :: from_secs ( 10 ) ;
90- let mut retry = BackoffRetry :: new ( base, Duration :: from_secs ( 30 ) , 1 ) ;
103+ let mut retry = BackoffRetry :: new ( base, Duration :: from_secs ( 30 ) , 1 , false ) ;
91104 let start = Instant :: now ( ) ;
92105
93106 assert_eq ! ( retry. next_delay( start) , base) ;
@@ -102,7 +115,7 @@ mod tests {
102115 fn test_with_backoff ( ) {
103116 let base = Duration :: from_secs ( 10 ) ;
104117 let max = Duration :: from_secs ( 60 ) ;
105- let mut retry = BackoffRetry :: new ( base, max, 2 ) ;
118+ let mut retry = BackoffRetry :: new ( base, max, 2 , false ) ;
106119 let start = Instant :: now ( ) - Duration :: from_secs ( 60 ) ;
107120
108121 assert_eq ! ( retry. next_delay( start) , base) ;
@@ -117,12 +130,23 @@ mod tests {
117130 assert_eq ! ( retry. next_delay( start. add( Duration :: from_secs( 3 ) ) ) , max) ;
118131 }
119132
133+ #[ test]
134+ fn test_with_jitter ( ) {
135+ let base = Duration :: from_secs ( 10 ) ;
136+ let max = Duration :: from_secs ( 60 ) ;
137+ let mut retry = BackoffRetry :: new ( base, max, 1 , true ) ;
138+ let start = Instant :: now ( ) - Duration :: from_secs ( 60 ) ;
139+
140+ let delay = retry. next_delay ( start) ;
141+ assert ! ( base / 2 <= delay && delay <= base) ;
142+ }
143+
120144 #[ test]
121145 fn test_retry_holds_at_max ( ) {
122146 let base = Duration :: from_secs ( 20 ) ;
123147 let max = Duration :: from_secs ( 30 ) ;
124148
125- let mut retry = BackoffRetry :: new ( base, max, 2 ) ;
149+ let mut retry = BackoffRetry :: new ( base, max, 2 , false ) ;
126150 let start = Instant :: now ( ) ;
127151 retry. reset ( start) ;
128152
@@ -146,7 +170,7 @@ mod tests {
146170 let reset_interval = Duration :: from_secs ( 45 ) ;
147171
148172 // Prepare a retry strategy that has succeeded at a specific point.
149- let mut retry = BackoffRetry :: new ( base, max, 2 ) ;
173+ let mut retry = BackoffRetry :: new ( base, max, 2 , false ) ;
150174 retry. reset_interval = reset_interval;
151175 let start = Instant :: now ( ) - Duration :: from_secs ( 60 ) ;
152176 retry. reset ( start) ;
0 commit comments