1
1
use std:: fmt:: { self , Display , Formatter } ;
2
+ use std:: time:: Duration ;
2
3
3
4
use crate :: chroot:: Chroot ;
4
5
use crate :: error:: Error ;
@@ -157,12 +158,19 @@ pub fn parse_connect_string(s: &str, tls: bool) -> Result<(Vec<EndpointRef<'_>>,
157
158
pub struct IterableEndpoints {
158
159
cycle : bool ,
159
160
next : usize ,
161
+ start : usize ,
160
162
endpoints : Vec < Endpoint > ,
161
163
}
162
164
165
+ #[ derive( Copy , Clone , Debug ) ]
166
+ pub struct Index {
167
+ offset : usize ,
168
+ cycles : usize ,
169
+ }
170
+
163
171
impl IterableEndpoints {
164
172
pub fn new ( endpoints : impl Into < Vec < Endpoint > > ) -> Self {
165
- Self { cycle : false , next : 0 , endpoints : endpoints. into ( ) }
173
+ Self { cycle : false , start : 0 , next : 0 , endpoints : endpoints. into ( ) }
166
174
}
167
175
168
176
pub fn len ( & self ) -> usize {
@@ -174,35 +182,68 @@ impl IterableEndpoints {
174
182
}
175
183
176
184
pub fn cycle ( & mut self ) {
177
- if self . next >= self . endpoints . len ( ) {
178
- self . next = 0 ;
179
- }
180
185
self . cycle = true ;
181
186
}
182
187
183
- pub fn next ( & mut self ) -> Option < EndpointRef < ' _ > > {
184
- let next = self . next ;
185
- if next >= self . endpoints . len ( ) {
188
+ /// Resets all counters and shuffles endpoints for future iterations.
189
+ pub fn reset ( & mut self ) {
190
+ self . next = 0 ;
191
+ self . start = 0 ;
192
+ fastrand:: shuffle ( & mut self . endpoints ) ;
193
+ }
194
+
195
+ /// Starts an new iteration at where `peek` stopped.
196
+ pub fn start ( & mut self ) {
197
+ self . start = self . next ;
198
+ }
199
+
200
+ pub async fn next ( & mut self ) -> Option < EndpointRef < ' _ > > {
201
+ let index = self . index ( ) ?;
202
+ self . delay ( index) . await ;
203
+ self . step ( ) ;
204
+ Some ( self . endpoints [ index. offset ] . to_ref ( ) )
205
+ }
206
+
207
+ async fn delay ( & self , index : Index ) {
208
+ let timeout = Self :: timeout ( index, self . endpoints . len ( ) ) ;
209
+ if timeout != Duration :: ZERO {
210
+ tokio:: time:: sleep ( timeout) . await ;
211
+ }
212
+ }
213
+
214
+ fn timeout ( index : Index , size : usize ) -> Duration {
215
+ if index. cycles == 0 {
216
+ return Duration :: ZERO ;
217
+ }
218
+ let unit = Duration :: from_millis ( 100 ) ;
219
+ if index. offset == 0 {
220
+ let jitter = Duration :: from_millis ( fastrand:: u32 ( 0 ..100 ) . into ( ) ) ;
221
+ let base = Duration :: from_millis ( 1000 ) . min ( unit * size as u32 ) ;
222
+ base * 2u32 . pow ( index. cycles as u32 - 1 ) + jitter
223
+ } else {
224
+ let jitter = Duration :: from_millis ( fastrand:: u32 ( 0 ..50 ) . into ( ) ) ;
225
+ ( unit * ( index. offset as u32 ) ) / 2 + jitter
226
+ }
227
+ }
228
+
229
+ /// Index for `next` and `peek`.
230
+ fn index ( & self ) -> Option < Index > {
231
+ let i = self . next - self . start ;
232
+ let n = self . endpoints . len ( ) ;
233
+ if i >= n && !self . cycle {
186
234
return None ;
187
235
}
188
- self . step ( ) ;
189
- let host = & self . endpoints [ next ] ;
190
- Some ( host . to_ref ( ) )
236
+ let offset = i % self . endpoints . len ( ) ;
237
+ let cycles = i / self . endpoints . len ( ) ;
238
+ Some ( Index { offset , cycles } )
191
239
}
192
240
193
241
pub fn step ( & mut self ) {
194
242
self . next += 1 ;
195
- if self . cycle && self . next >= self . endpoints . len ( ) {
196
- self . next = 0 ;
197
- }
198
243
}
199
244
200
245
pub fn peek ( & self ) -> Option < EndpointRef < ' _ > > {
201
- let next = self . next ;
202
- if next >= self . endpoints . len ( ) {
203
- return None ;
204
- }
205
- Some ( self . endpoints [ next] . to_ref ( ) )
246
+ self . index ( ) . map ( |index| self . endpoints [ index. offset ] . to_ref ( ) )
206
247
}
207
248
}
208
249
@@ -283,21 +324,53 @@ mod tests {
283
324
) ;
284
325
}
285
326
327
+ #[ tokio:: test]
328
+ async fn test_iterable_endpoints_next ( ) {
329
+ use assertor:: * ;
330
+
331
+ use super :: { parse_connect_string, EndpointRef , Index , IterableEndpoints } ;
332
+ let ( endpoints, _) = parse_connect_string ( "host1:2181,tcp://host2,tcp+tls://host3:2182" , true ) . unwrap ( ) ;
333
+ let mut endpoints = IterableEndpoints :: from ( endpoints. as_slice ( ) ) ;
334
+ assert_eq ! ( endpoints. next( ) . await , Some ( EndpointRef :: new( "host1" , 2181 , true ) ) ) ;
335
+ assert_eq ! ( endpoints. next( ) . await , Some ( EndpointRef :: new( "host2" , 2181 , false ) ) ) ;
336
+ assert_eq ! ( endpoints. next( ) . await , Some ( EndpointRef :: new( "host3" , 2182 , true ) ) ) ;
337
+ assert_eq ! ( endpoints. next( ) . await , None ) ;
338
+
339
+ endpoints. cycle ( ) ;
340
+ let start = std:: time:: Instant :: now ( ) ;
341
+ assert_eq ! ( endpoints. next( ) . await , Some ( EndpointRef :: new( "host1" , 2181 , true ) ) ) ;
342
+ assert_eq ! ( endpoints. next( ) . await , Some ( EndpointRef :: new( "host2" , 2181 , false ) ) ) ;
343
+ assert_eq ! ( endpoints. next( ) . await , Some ( EndpointRef :: new( "host3" , 2182 , true ) ) ) ;
344
+ assert_eq ! ( endpoints. next( ) . await , Some ( EndpointRef :: new( "host1" , 2181 , true ) ) ) ;
345
+ let delay = IterableEndpoints :: timeout ( Index { offset : 0 , cycles : 1 } , 3 )
346
+ + IterableEndpoints :: timeout ( Index { offset : 1 , cycles : 1 } , 3 ) ;
347
+ let now = std:: time:: Instant :: now ( ) ;
348
+ assert_that ! ( now) . is_greater_than ( start + delay) ;
349
+ }
350
+
286
351
#[ test]
287
- fn test_iterable_endpoints ( ) {
352
+ fn test_iterable_endpoints_peek ( ) {
288
353
use super :: { parse_connect_string, EndpointRef , IterableEndpoints } ;
289
354
let ( endpoints, _) = parse_connect_string ( "host1:2181,tcp://host2,tcp+tls://host3:2182" , true ) . unwrap ( ) ;
290
355
let mut endpoints = IterableEndpoints :: from ( endpoints. as_slice ( ) ) ;
291
- assert_eq ! ( endpoints. next( ) , Some ( EndpointRef :: new( "host1" , 2181 , true ) ) ) ;
292
- assert_eq ! ( endpoints. next( ) , Some ( EndpointRef :: new( "host2" , 2181 , false ) ) ) ;
293
- assert_eq ! ( endpoints. next( ) , Some ( EndpointRef :: new( "host3" , 2182 , true ) ) ) ;
294
- assert_eq ! ( endpoints. next( ) , None ) ;
356
+ assert_eq ! ( endpoints. peek( ) , Some ( EndpointRef :: new( "host1" , 2181 , true ) ) ) ;
357
+ // Successive `peek` without `step` doesn't advance.
358
+ assert_eq ! ( endpoints. peek( ) , Some ( EndpointRef :: new( "host1" , 2181 , true ) ) ) ;
359
+ endpoints. step ( ) ;
360
+ assert_eq ! ( endpoints. peek( ) , Some ( EndpointRef :: new( "host2" , 2181 , false ) ) ) ;
361
+ endpoints. step ( ) ;
362
+ assert_eq ! ( endpoints. peek( ) , Some ( EndpointRef :: new( "host3" , 2182 , true ) ) ) ;
363
+ endpoints. step ( ) ;
364
+ assert_eq ! ( endpoints. peek( ) , None ) ;
295
365
296
366
endpoints. cycle ( ) ;
297
- assert_eq ! ( endpoints. next( ) , Some ( EndpointRef :: new( "host1" , 2181 , true ) ) ) ;
298
- assert_eq ! ( endpoints. next( ) , Some ( EndpointRef :: new( "host2" , 2181 , false ) ) ) ;
299
- assert_eq ! ( endpoints. next( ) , Some ( EndpointRef :: new( "host3" , 2182 , true ) ) ) ;
300
- assert_eq ! ( endpoints. next( ) , Some ( EndpointRef :: new( "host1" , 2181 , true ) ) ) ;
367
+ assert_eq ! ( endpoints. peek( ) , Some ( EndpointRef :: new( "host1" , 2181 , true ) ) ) ;
368
+ endpoints. step ( ) ;
369
+ assert_eq ! ( endpoints. peek( ) , Some ( EndpointRef :: new( "host2" , 2181 , false ) ) ) ;
370
+ endpoints. step ( ) ;
371
+ assert_eq ! ( endpoints. peek( ) , Some ( EndpointRef :: new( "host3" , 2182 , true ) ) ) ;
372
+ endpoints. step ( ) ;
373
+ assert_eq ! ( endpoints. peek( ) , Some ( EndpointRef :: new( "host1" , 2181 , true ) ) ) ;
301
374
}
302
375
303
376
#[ test]
0 commit comments