@@ -33,7 +33,7 @@ impl ExponentialBackoff {
33
33
}
34
34
35
35
pub fn delay ( & self ) -> Duration {
36
- let mut delay = self . base . saturating_mul ( 1 << self . attempt ) ;
36
+ let mut delay = self . base . saturating_mul ( 1u32 << self . attempt . min ( 31 ) ) ;
37
37
if delay > self . ceiling {
38
38
delay = self . ceiling ;
39
39
}
@@ -50,3 +50,71 @@ impl ExponentialBackoff {
50
50
self . attempt = 0 ;
51
51
}
52
52
}
53
+
54
+ #[ cfg( test) ]
55
+ mod tests {
56
+ use super :: * ;
57
+ use std:: time:: Instant ;
58
+
59
+ #[ test]
60
+ fn test_delay ( ) {
61
+ let mut backoff =
62
+ ExponentialBackoff :: new ( Duration :: from_millis ( 500 ) , Duration :: from_secs ( 5 ) ) ;
63
+
64
+ // First delay should be base (0.5s)
65
+ assert_eq ! ( backoff. next_attempt( ) , Duration :: from_millis( 500 ) ) ;
66
+
67
+ // Second delay should be 1s (base * 2^1)
68
+ assert_eq ! ( backoff. next_attempt( ) , Duration :: from_secs( 1 ) ) ;
69
+
70
+ // Third delay should be 2s (base * 2^2)
71
+ assert_eq ! ( backoff. next_attempt( ) , Duration :: from_secs( 2 ) ) ;
72
+
73
+ // Fourth delay should be 4s (base * 2^3)
74
+ assert_eq ! ( backoff. next_attempt( ) , Duration :: from_secs( 4 ) ) ;
75
+
76
+ // Seventh delay should be ceiling (5s)
77
+ assert_eq ! ( backoff. next_attempt( ) , Duration :: from_secs( 5 ) ) ;
78
+
79
+ // Eighth delay should also be ceiling (5s)
80
+ assert_eq ! ( backoff. next_attempt( ) , Duration :: from_secs( 5 ) ) ;
81
+ }
82
+
83
+ #[ test]
84
+ fn test_overflow_delay ( ) {
85
+ let mut backoff =
86
+ ExponentialBackoff :: new ( Duration :: from_millis ( 500 ) , Duration :: from_secs ( 45 ) ) ;
87
+
88
+ // 31st should be ceiling (45s) without overflowing
89
+ backoff. attempt = 31 ;
90
+ assert_eq ! ( backoff. next_attempt( ) , Duration :: from_secs( 45 ) ) ;
91
+ assert_eq ! ( backoff. next_attempt( ) , Duration :: from_secs( 45 ) ) ;
92
+
93
+ backoff. attempt = 123456 ;
94
+ assert_eq ! ( backoff. next_attempt( ) , Duration :: from_secs( 45 ) ) ;
95
+ }
96
+
97
+ #[ tokio:: test]
98
+ async fn test_sleep_async ( ) {
99
+ let mut backoff =
100
+ ExponentialBackoff :: new ( Duration :: from_secs_f32 ( 0.1 ) , Duration :: from_secs_f32 ( 0.2 ) ) ;
101
+
102
+ let start = Instant :: now ( ) ;
103
+ backoff. sleep_async ( ) . await ;
104
+ let elapsed = start. elapsed ( ) ;
105
+
106
+ assert ! ( elapsed >= Duration :: from_secs_f32( 0.1 ) && elapsed < Duration :: from_secs_f32( 0.15 ) ) ;
107
+
108
+ let start = Instant :: now ( ) ;
109
+ backoff. sleep_async ( ) . await ;
110
+ let elapsed = start. elapsed ( ) ;
111
+
112
+ assert ! ( elapsed >= Duration :: from_secs_f32( 0.2 ) && elapsed < Duration :: from_secs_f32( 0.25 ) ) ;
113
+
114
+ let start = Instant :: now ( ) ;
115
+ backoff. sleep_async ( ) . await ;
116
+ let elapsed = start. elapsed ( ) ;
117
+
118
+ assert ! ( elapsed >= Duration :: from_secs_f32( 0.2 ) && elapsed < Duration :: from_secs_f32( 0.25 ) ) ;
119
+ }
120
+ }
0 commit comments