1- use std:: { marker:: PhantomData , time} ;
2- use tower:: { retry:: backoff:: Backoff , util:: rng:: Rng } ;
1+ use std:: time;
2+ use tower:: {
3+ retry:: backoff:: Backoff ,
4+ util:: rng:: { HasherRng , Rng } ,
5+ } ;
36
47/// A jittered [exponential backoff] strategy.
58///
6366 type Future = tokio:: time:: Sleep ;
6467
6568 fn next_backoff ( & mut self ) -> Self :: Future {
66- self . retries += 1 ;
67-
6869 let duration = self . backoff ( ) ;
70+ self . tried ( ) ;
71+
6972 tokio:: time:: sleep ( duration)
7073 }
7174}
@@ -76,7 +79,7 @@ pub struct ExponentialBackoffBuilder<R> {
7679 multiplier : f64 ,
7780 jitter : f64 ,
7881 max_delay : time:: Duration ,
79- _rng : PhantomData < R > ,
82+ rng : R ,
8083}
8184
8285type Result < T > = std:: result:: Result < T , InvalidBackoff > ;
@@ -86,34 +89,31 @@ type Result<T> = std::result::Result<T, InvalidBackoff>;
8689#[ error( "Invalid backoff configuration: {0}" ) ]
8790pub struct InvalidBackoff ( & ' static str ) ;
8891
89- impl < R > ExponentialBackoffBuilder < R >
90- where
91- R : Rng + Default ,
92- {
92+ impl < R > ExponentialBackoffBuilder < R > {
9393 /// Backoff configuration with the default values specified at https://github.com/grpc/grpc/blob/master/doc/connection-backoff.md.
9494 ///
9595 /// This should be useful for callers who want to configure backoff with
9696 /// non-default values only for a subset of the options.
9797 ///
9898 /// Copied from [google.golang.org/grpc@v1.48.0/backoff/backoff.go]
99- pub fn default ( ) -> Self {
100- Self {
99+ pub fn default ( ) -> ExponentialBackoffBuilder < HasherRng > {
100+ ExponentialBackoffBuilder {
101101 base_delay : time:: Duration :: from_secs ( 1 ) ,
102102 multiplier : 1.6 ,
103103 jitter : 0.2 ,
104104 max_delay : time:: Duration :: from_secs ( 120 ) ,
105- _rng : PhantomData ,
105+ rng : HasherRng :: default ( ) ,
106106 }
107107 }
108108
109109 /// Common configuration for fast backoff.
110- pub fn fast_config ( ) -> Self {
111- Self {
110+ pub fn fast_config ( ) -> ExponentialBackoffBuilder < HasherRng > {
111+ ExponentialBackoffBuilder {
112112 base_delay : time:: Duration :: from_millis ( 100 ) ,
113113 multiplier : 1.6 ,
114114 jitter : 0.2 ,
115115 max_delay : time:: Duration :: from_secs ( 5 ) ,
116- _rng : PhantomData ,
116+ rng : HasherRng :: default ( ) ,
117117 }
118118 }
119119
@@ -142,6 +142,17 @@ where
142142 self
143143 }
144144
145+ /// Set the random number generator to use for jittering.
146+ pub fn with_rng < T > ( self , rng : T ) -> ExponentialBackoffBuilder < T > {
147+ ExponentialBackoffBuilder {
148+ base_delay : self . base_delay ,
149+ multiplier : self . multiplier ,
150+ jitter : self . jitter ,
151+ max_delay : self . max_delay ,
152+ rng,
153+ }
154+ }
155+
145156 /// Construct a new [`ExponentialBackoff`] instance from the builder.
146157 pub fn build ( self ) -> Result < ExponentialBackoff < R > > {
147158 if self . base_delay > self . max_delay {
@@ -168,7 +179,7 @@ where
168179 jitter : self . jitter ,
169180 multiplier : self . multiplier ,
170181 max_delay : self . max_delay ,
171- rng : R :: default ( ) ,
182+ rng : self . rng ,
172183 retries : 0 ,
173184 } )
174185 }
@@ -180,26 +191,20 @@ mod tests {
180191 use core:: time:: Duration ;
181192 use tower:: util:: rng:: Rng ;
182193
183- #[ test]
184- fn default_config ( ) {
185- struct ConstRng ;
186-
187- impl Rng for ConstRng {
188- fn next_f64 ( & mut self ) -> f64 {
189- 0.5
190- }
194+ struct Const ( f64 ) ;
191195
192- fn next_u64 ( & mut self ) -> u64 {
193- panic ! ( "not implemented" )
194- }
196+ impl Rng for Const {
197+ fn next_f64 ( & mut self ) -> f64 {
198+ self . 0
195199 }
196200
197- impl Default for ConstRng {
198- fn default ( ) -> Self {
199- ConstRng
200- }
201+ fn next_u64 ( & mut self ) -> u64 {
202+ 0
201203 }
204+ }
202205
206+ #[ test]
207+ fn default_config ( ) {
203208 let backoffs: Vec < Duration > = vec ! [
204209 Duration :: from_secs( 1 ) ,
205210 Duration :: from_secs( 1 ) + Duration :: from_millis( 600 ) ,
@@ -216,8 +221,39 @@ mod tests {
216221 Duration :: from_mins( 2 ) ,
217222 ] ;
218223
219- let mut backoff: ExponentialBackoff < ConstRng > = ExponentialBackoffBuilder :: default ( )
220- . with_jitter ( 0.5 )
224+ let mut backoff = ExponentialBackoffBuilder :: < Const > :: default ( )
225+ . with_rng ( Const ( 0.5 ) )
226+ . build ( )
227+ . unwrap ( ) ;
228+
229+ for expected in backoffs {
230+ let duration = backoff. backoff ( ) ;
231+ backoff. tried ( ) ;
232+
233+ assert ! ( duration - expected <= Duration :: from_millis( 10 ) ) ;
234+ }
235+ }
236+
237+ #[ test]
238+ fn default_config_max_jitter ( ) {
239+ let backoffs: Vec < Duration > = vec ! [
240+ Duration :: from_secs( 1 ) ,
241+ Duration :: from_secs( 1 ) + Duration :: from_millis( 920 ) ,
242+ Duration :: from_secs( 3 ) + Duration :: from_millis( 70 ) ,
243+ Duration :: from_secs( 4 ) + Duration :: from_millis( 910 ) ,
244+ Duration :: from_secs( 7 ) + Duration :: from_millis( 860 ) ,
245+ Duration :: from_secs( 12 ) + Duration :: from_millis( 580 ) ,
246+ Duration :: from_secs( 20 ) + Duration :: from_millis( 130 ) ,
247+ Duration :: from_secs( 32 ) + Duration :: from_millis( 210 ) ,
248+ Duration :: from_secs( 51 ) + Duration :: from_millis( 530 ) ,
249+ Duration :: from_mins( 1 ) + Duration :: from_secs( 22 ) + Duration :: from_millis( 460 ) ,
250+ Duration :: from_mins( 2 ) + Duration :: from_secs( 11 ) + Duration :: from_millis( 940 ) ,
251+ Duration :: from_mins( 2 ) + Duration :: from_secs( 24 ) ,
252+ Duration :: from_mins( 2 ) + Duration :: from_secs( 24 ) ,
253+ ] ;
254+
255+ let mut backoff: ExponentialBackoff < Const > = ExponentialBackoffBuilder :: < Const > :: default ( )
256+ . with_rng ( Const ( 1.0 ) )
221257 . build ( )
222258 . unwrap ( ) ;
223259
0 commit comments