@@ -129,6 +129,46 @@ impl Gcra {
129
129
} )
130
130
}
131
131
132
+ pub ( crate ) fn peek_test <
133
+ K ,
134
+ P : clock:: Reference ,
135
+ S : StateStore < Key = K > ,
136
+ MW : RateLimitingMiddleware < P > ,
137
+ > (
138
+ & self ,
139
+ start : P ,
140
+ key : & K ,
141
+ state : & S ,
142
+ t0 : P ,
143
+ ) -> Result < MW :: PositiveOutcome , MW :: NegativeOutcome > {
144
+ let t0 = t0. duration_since ( start) ;
145
+ let tau = self . tau ;
146
+ let t = self . t ;
147
+ match state. measure_and_peek ( key, |tat| {
148
+ let tat = tat. unwrap_or_else ( || self . starting_state ( t0) ) ;
149
+ let earliest_time = tat. saturating_sub ( tau) ;
150
+ if t0 < earliest_time {
151
+ Err ( MW :: disallow (
152
+ key,
153
+ StateSnapshot :: new ( self . t , self . tau , earliest_time, earliest_time) ,
154
+ start,
155
+ ) )
156
+ } else {
157
+ let next = cmp:: max ( tat, t0) + t;
158
+ Ok ( (
159
+ MW :: allow ( key, StateSnapshot :: new ( self . t , self . tau , t0, next) ) ,
160
+ next,
161
+ ) )
162
+ }
163
+ } ) {
164
+ Some ( outcome) => outcome,
165
+ None => Ok ( MW :: allow (
166
+ key,
167
+ StateSnapshot :: new ( self . t , self . tau , t0, tau) ,
168
+ ) ) ,
169
+ }
170
+ }
171
+
132
172
/// Tests whether all `n` cells could be accommodated and updates the rate limiter state, if so.
133
173
pub ( crate ) fn test_n_all_and_update <
134
174
K ,
@@ -176,12 +216,68 @@ impl Gcra {
176
216
}
177
217
} )
178
218
}
219
+
220
+ pub ( crate ) fn test_n_all_peek <
221
+ K ,
222
+ P : clock:: Reference ,
223
+ S : StateStore < Key = K > ,
224
+ MW : RateLimitingMiddleware < P > ,
225
+ > (
226
+ & self ,
227
+ start : P ,
228
+ key : & K ,
229
+ n : NonZeroU32 ,
230
+ state : & S ,
231
+ t0 : P ,
232
+ ) -> Result < MW :: PositiveOutcome , NegativeMultiDecision < MW :: NegativeOutcome > > {
233
+ let t0 = t0. duration_since ( start) ;
234
+ let tau = self . tau ;
235
+ let t = self . t ;
236
+ let additional_weight = t * ( n. get ( ) - 1 ) as u64 ;
237
+
238
+ // check that we can allow enough cells through. Note that `additional_weight` is the
239
+ // value of the cells *in addition* to the first cell - so add that first cell back.
240
+ if additional_weight + t > tau {
241
+ return Err ( NegativeMultiDecision :: InsufficientCapacity (
242
+ ( tau. as_u64 ( ) / t. as_u64 ( ) ) as u32 ,
243
+ ) ) ;
244
+ }
245
+ match state. measure_and_peek ( key, |tat| {
246
+ let tat = tat. unwrap_or_else ( || self . starting_state ( t0) ) ;
247
+ let earliest_time = ( tat + additional_weight) . saturating_sub ( tau) ;
248
+ if t0 < earliest_time {
249
+ Err ( NegativeMultiDecision :: BatchNonConforming (
250
+ n. get ( ) ,
251
+ MW :: disallow (
252
+ key,
253
+ StateSnapshot :: new ( self . t , self . tau , earliest_time, earliest_time) ,
254
+ start,
255
+ ) ,
256
+ ) )
257
+ } else {
258
+ let next = cmp:: max ( tat, t0) + t + additional_weight;
259
+ Ok ( (
260
+ MW :: allow ( key, StateSnapshot :: new ( self . t , self . tau , t0, next) ) ,
261
+ next,
262
+ ) )
263
+ }
264
+ } ) {
265
+ Some ( outcome) => outcome,
266
+ None => Ok ( MW :: allow (
267
+ key,
268
+ StateSnapshot :: new ( self . t , self . tau , t0, tau) ,
269
+ ) ) ,
270
+ }
271
+ }
179
272
}
180
273
181
274
#[ cfg( test) ]
182
275
mod test {
183
276
use super :: * ;
184
- use crate :: Quota ;
277
+ use crate :: RateLimiter ;
278
+ use crate :: { clock:: FakeRelativeClock , Quota } ;
279
+ use no_std_compat:: prelude:: v1:: * ;
280
+ use nonzero_ext:: nonzero;
185
281
use std:: num:: NonZeroU32 ;
186
282
187
283
use proptest:: prelude:: * ;
@@ -190,34 +286,40 @@ mod test {
190
286
#[ cfg( feature = "std" ) ]
191
287
#[ test]
192
288
fn gcra_derives ( ) {
193
- use all_asserts:: assert_gt;
194
289
use nonzero_ext:: nonzero;
195
290
196
291
let g = Gcra :: new ( Quota :: per_second ( nonzero ! ( 1u32 ) ) ) ;
197
292
let g2 = Gcra :: new ( Quota :: per_second ( nonzero ! ( 2u32 ) ) ) ;
198
293
assert_eq ! ( g, g) ;
199
294
assert_ne ! ( g, g2) ;
200
- assert_gt ! ( format!( "{:?}" , g) . len ( ) , 0 ) ;
295
+ assert ! ( ! format!( "{:?}" , g) . is_empty ( ) ) ;
201
296
}
202
297
203
298
/// Exercise derives and convenience impls on NotUntil to make coverage happy
204
299
#[ cfg( feature = "std" ) ]
205
300
#[ test]
206
301
fn notuntil_impls ( ) {
207
- use crate :: RateLimiter ;
208
- use all_asserts:: assert_gt;
209
- use clock:: FakeRelativeClock ;
210
- use nonzero_ext:: nonzero;
211
-
212
302
let clock = FakeRelativeClock :: default ( ) ;
213
303
let quota = Quota :: per_second ( nonzero ! ( 1u32 ) ) ;
214
304
let lb = RateLimiter :: direct_with_clock ( quota, & clock) ;
305
+ for _ in 0 ..2 {
306
+ assert ! ( lb. peek( ) . is_ok( ) ) ;
307
+ }
215
308
assert ! ( lb. check( ) . is_ok( ) ) ;
309
+ assert ! ( lb
310
+ . peek( )
311
+ . map_err( |nu| {
312
+ assert_eq!( nu. earliest_possible( ) , Nanos :: from( 1_000_000_000 ) ) ;
313
+ assert!( !format!( "{:?}" , nu) . is_empty( ) ) ;
314
+ assert_eq!( format!( "{}" , nu) , "rate-limited until Nanos(1s)" ) ;
315
+ assert_eq!( nu. quota( ) , quota) ;
316
+ } )
317
+ . is_err( ) ) ;
216
318
assert ! ( lb
217
319
. check( )
218
320
. map_err( |nu| {
219
- assert_eq!( nu, nu ) ;
220
- assert_gt! ( format!( "{:?}" , nu) . len ( ) , 0 ) ;
321
+ assert_eq!( nu. earliest_possible ( ) , Nanos :: from ( 1_000_000_000 ) ) ;
322
+ assert! ( ! format!( "{:?}" , nu) . is_empty ( ) ) ;
221
323
assert_eq!( format!( "{}" , nu) , "rate-limited until Nanos(1s)" ) ;
222
324
assert_eq!( nu. quota( ) , quota) ;
223
325
} )
@@ -255,4 +357,139 @@ mod test {
255
357
assert_eq!( quota, back) ;
256
358
} )
257
359
}
360
+
361
+ #[ test]
362
+ fn peek_key_test_and_update_works ( ) {
363
+ let clock = FakeRelativeClock :: default ( ) ;
364
+ let quota = Quota :: per_second ( nonzero ! ( 1u32 ) ) ;
365
+ let lk = RateLimiter :: hashmap_with_clock ( quota, & clock) ;
366
+ let key = 1u32 ;
367
+ let key2 = 2u32 ;
368
+ for _ in 0 ..2 {
369
+ assert ! ( lk. peek_key( & key) . is_ok( ) ) ;
370
+ }
371
+ for _ in 0 ..2 {
372
+ assert ! ( lk. peek_key( & key2) . is_ok( ) ) ;
373
+ }
374
+ assert ! ( lk. check_key( & key) . is_ok( ) ) ;
375
+ for _ in 0 ..2 {
376
+ assert ! ( lk. peek_key( & key2) . is_ok( ) ) ;
377
+ }
378
+ assert ! ( lk
379
+ . check_key( & key)
380
+ . map_err( |nu| {
381
+ assert_eq!( nu. earliest_possible( ) , Nanos :: from( 1_000_000_000 ) ) ;
382
+ assert!( !format!( "{:?}" , nu) . is_empty( ) ) ;
383
+ assert_eq!( format!( "{}" , nu) , "rate-limited until Nanos(1s)" ) ;
384
+ assert_eq!( nu. quota( ) , quota) ;
385
+ } )
386
+ . is_err( ) ) ;
387
+ assert ! ( lk
388
+ . check_key( & key)
389
+ . map_err( |nu| {
390
+ assert_eq!( nu. earliest_possible( ) , Nanos :: from( 1_000_000_000 ) ) ;
391
+ assert!( !format!( "{:?}" , nu) . is_empty( ) ) ;
392
+ assert_eq!( format!( "{}" , nu) , "rate-limited until Nanos(1s)" ) ;
393
+ assert_eq!( nu. quota( ) , quota) ;
394
+ } )
395
+ . is_err( ) ) ;
396
+ assert ! ( lk. check_key( & key2) . is_ok( ) ) ;
397
+ assert ! ( lk
398
+ . check_key( & key2)
399
+ . map_err( |nu| {
400
+ assert_eq!( nu. earliest_possible( ) , Nanos :: from( 1_000_000_000 ) ) ;
401
+ assert!( !format!( "{:?}" , nu) . is_empty( ) ) ;
402
+ assert_eq!( format!( "{}" , nu) , "rate-limited until Nanos(1s)" ) ;
403
+ assert_eq!( nu. quota( ) , quota) ;
404
+ } )
405
+ . is_err( ) ) ;
406
+ assert ! ( lk
407
+ . check_key( & key2)
408
+ . map_err( |nu| {
409
+ assert_eq!( nu. earliest_possible( ) , Nanos :: from( 1_000_000_000 ) ) ;
410
+ assert!( !format!( "{:?}" , nu) . is_empty( ) ) ;
411
+ assert_eq!( format!( "{}" , nu) , "rate-limited until Nanos(1s)" ) ;
412
+ assert_eq!( nu. quota( ) , quota) ;
413
+ } )
414
+ . is_err( ) ) ;
415
+ clock. advance ( Duration :: from_millis ( 500 ) ) ;
416
+ assert ! ( lk
417
+ . peek_key( & key)
418
+ . map_err( |nu| {
419
+ assert_eq!( nu. earliest_possible( ) , Nanos :: from( 1_000_000_000 ) ) ;
420
+ assert!( !format!( "{:?}" , nu) . is_empty( ) ) ;
421
+ assert_eq!( format!( "{}" , nu) , "rate-limited until Nanos(1s)" ) ;
422
+ assert_eq!( nu. quota( ) , quota) ;
423
+ } )
424
+ . is_err( ) ) ;
425
+ assert ! ( lk
426
+ . peek_key( & key)
427
+ . map_err( |nu| {
428
+ assert_eq!( nu. earliest_possible( ) , Nanos :: from( 1_000_000_000 ) ) ;
429
+ assert!( !format!( "{:?}" , nu) . is_empty( ) ) ;
430
+ assert_eq!( format!( "{}" , nu) , "rate-limited until Nanos(1s)" ) ;
431
+ assert_eq!( nu. quota( ) , quota) ;
432
+ } )
433
+ . is_err( ) ) ;
434
+ clock. advance ( Duration :: from_millis ( 500 ) ) ;
435
+ for _ in 0 ..2 {
436
+ assert ! ( lk. peek_key( & key) . is_ok( ) ) ;
437
+ }
438
+ for _ in 0 ..2 {
439
+ assert ! ( lk. peek_key( & key2) . is_ok( ) ) ;
440
+ }
441
+ assert ! ( lk. check_key( & key) . is_ok( ) ) ;
442
+ assert ! ( lk. check_key( & key2) . is_ok( ) ) ;
443
+ assert ! ( lk. check_key( & key) . is_err( ) ) ;
444
+ assert ! ( lk. check_key( & key2) . is_err( ) ) ;
445
+ assert_eq ! ( lk. reset_key( & key) , ( ) ) ;
446
+ assert_eq ! ( lk. reset_key( & key2) , ( ) ) ;
447
+ assert_eq ! ( lk. reset_key( & 3 ) , ( ) ) ;
448
+ assert ! ( lk. check_key( & key) . is_ok( ) ) ;
449
+ assert ! ( lk. check_key( & key2) . is_ok( ) ) ;
450
+ }
451
+
452
+ #[ test]
453
+ fn peek_n_key_test_and_update_works ( ) {
454
+ let clock = FakeRelativeClock :: default ( ) ;
455
+ let quota = Quota :: per_second ( nonzero ! ( 2u32 ) ) ;
456
+ let lk = RateLimiter :: hashmap_with_clock ( quota, & clock) ;
457
+ let key = 1u32 ;
458
+ let key2 = 2u32 ;
459
+ for _ in 0 ..2 {
460
+ assert ! ( lk. peek_key_n( & key, nonzero!( 2u32 ) ) . is_ok( ) ) ;
461
+ }
462
+ for _ in 0 ..2 {
463
+ assert ! ( lk. peek_key_n( & key2, nonzero!( 2u32 ) ) . is_ok( ) ) ;
464
+ }
465
+ for _ in 0 ..2 {
466
+ assert ! ( lk
467
+ . peek_key_n( & key, nonzero!( 3u32 ) )
468
+ . map_err( |nu| {
469
+ assert_eq!( nu, NegativeMultiDecision :: InsufficientCapacity ( 2 ) ) ;
470
+ } )
471
+ . is_err( ) ) ;
472
+ }
473
+ for _ in 0 ..2 {
474
+ assert ! ( lk
475
+ . peek_key_n( & key2, nonzero!( 3u32 ) )
476
+ . map_err( |nu| {
477
+ assert_eq!( nu, NegativeMultiDecision :: InsufficientCapacity ( 2 ) ) ;
478
+ } )
479
+ . is_err( ) ) ;
480
+ }
481
+ assert ! ( lk. check_key_n( & key, nonzero!( 2u32 ) ) . is_ok( ) ) ;
482
+ assert ! ( lk. check_key_n( & key, nonzero!( 1u32 ) ) . is_err( ) ) ;
483
+ assert ! ( lk. check_key_n( & key2, nonzero!( 2u32 ) ) . is_ok( ) ) ;
484
+ assert ! ( lk. check_key_n( & key2, nonzero!( 1u32 ) ) . is_err( ) ) ;
485
+ for _ in 0 ..2 {
486
+ assert ! ( lk. peek_key_n( & key, nonzero!( 1u32 ) ) . is_err( ) ) ;
487
+ }
488
+ for _ in 0 ..2 {
489
+ assert ! ( lk. peek_key_n( & key2, nonzero!( 1u32 ) ) . is_err( ) ) ;
490
+ }
491
+ assert_eq ! ( lk. reset_key( & key) , ( ) ) ;
492
+ assert ! ( lk. check_key_n( & key, nonzero!( 2u32 ) ) . is_ok( ) ) ;
493
+ // TODO: impl Reference for FakeRelativeClock and test returned error
494
+ }
258
495
}
0 commit comments