Skip to content

Commit ad393eb

Browse files
committed
Merge version peek and reset
Implementing peeking Add more peek functions Fix failing tests Implemented peek Fix n key peek function Added peek tests for n advances Implemented reset Added reset test for dashmap stable formatting fix failing no)_std tests Fix formatting Clippy lints Add test for peek Add tests Add tests Add dashmap tests assert coverage test Address coverage formatting
1 parent 6894267 commit ad393eb

14 files changed

+834
-40
lines changed

governor/src/gcra.rs

+247-10
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,46 @@ impl Gcra {
129129
})
130130
}
131131

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+
132172
/// Tests whether all `n` cells could be accommodated and updates the rate limiter state, if so.
133173
pub(crate) fn test_n_all_and_update<
134174
K,
@@ -176,12 +216,68 @@ impl Gcra {
176216
}
177217
})
178218
}
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+
}
179272
}
180273

181274
#[cfg(test)]
182275
mod test {
183276
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;
185281
use std::num::NonZeroU32;
186282

187283
use proptest::prelude::*;
@@ -190,34 +286,40 @@ mod test {
190286
#[cfg(feature = "std")]
191287
#[test]
192288
fn gcra_derives() {
193-
use all_asserts::assert_gt;
194289
use nonzero_ext::nonzero;
195290

196291
let g = Gcra::new(Quota::per_second(nonzero!(1u32)));
197292
let g2 = Gcra::new(Quota::per_second(nonzero!(2u32)));
198293
assert_eq!(g, g);
199294
assert_ne!(g, g2);
200-
assert_gt!(format!("{:?}", g).len(), 0);
295+
assert!(!format!("{:?}", g).is_empty());
201296
}
202297

203298
/// Exercise derives and convenience impls on NotUntil to make coverage happy
204299
#[cfg(feature = "std")]
205300
#[test]
206301
fn notuntil_impls() {
207-
use crate::RateLimiter;
208-
use all_asserts::assert_gt;
209-
use clock::FakeRelativeClock;
210-
use nonzero_ext::nonzero;
211-
212302
let clock = FakeRelativeClock::default();
213303
let quota = Quota::per_second(nonzero!(1u32));
214304
let lb = RateLimiter::direct_with_clock(quota, &clock);
305+
for _ in 0..2 {
306+
assert!(lb.peek().is_ok());
307+
}
215308
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());
216318
assert!(lb
217319
.check()
218320
.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());
221323
assert_eq!(format!("{}", nu), "rate-limited until Nanos(1s)");
222324
assert_eq!(nu.quota(), quota);
223325
})
@@ -255,4 +357,139 @@ mod test {
255357
assert_eq!(quota, back);
256358
})
257359
}
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+
}
258495
}

governor/src/state.rs

+8-2
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,13 @@ pub trait StateStore {
4646
fn measure_and_replace<T, F, E>(&self, key: &Self::Key, f: F) -> Result<T, E>
4747
where
4848
F: Fn(Option<Nanos>) -> Result<(T, Nanos), E>;
49+
/// `measure_and_peek` does the same as `measure_and_replace` except
50+
/// it will not change the state. This is useful if you need to know the next
51+
/// decision in advance
52+
fn measure_and_peek<T, F, E>(&self, key: &Self::Key, f: F) -> Option<Result<T, E>>
53+
where
54+
F: Fn(Option<Nanos>) -> Result<(T, Nanos), E>;
55+
fn reset(&self, key: &Self::Key);
4956
}
5057

5158
/// A rate limiter.
@@ -134,12 +141,11 @@ where
134141
mod test {
135142
use super::*;
136143
use crate::Quota;
137-
use all_asserts::assert_gt;
138144
use nonzero_ext::nonzero;
139145

140146
#[test]
141147
fn ratelimiter_impl_coverage() {
142148
let lim = RateLimiter::direct(Quota::per_second(nonzero!(3u32)));
143-
assert_gt!(format!("{:?}", lim).len(), 0);
149+
assert!(!format!("{:?}", lim).is_empty());
144150
}
145151
}

governor/src/state/direct.rs

+22
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,15 @@ where
8080
)
8181
}
8282

83+
pub fn peek(&self) -> Result<MW::PositiveOutcome, MW::NegativeOutcome> {
84+
self.gcra.peek_test::<NotKeyed, C::Instant, S, MW>(
85+
self.start,
86+
&NotKeyed::NonKey,
87+
&self.state,
88+
self.clock.now(),
89+
)
90+
}
91+
8392
/// Allow *only all* `n` cells through the rate limiter.
8493
///
8594
/// This method can succeed in only one way and fail in two ways:
@@ -107,6 +116,19 @@ where
107116
self.clock.now(),
108117
)
109118
}
119+
120+
pub fn peek_n(
121+
&self,
122+
n: NonZeroU32,
123+
) -> Result<MW::PositiveOutcome, NegativeMultiDecision<MW::NegativeOutcome>> {
124+
self.gcra.test_n_all_peek::<NotKeyed, C::Instant, S, MW>(
125+
self.start,
126+
&NotKeyed::NonKey,
127+
n,
128+
&self.state,
129+
self.clock.now(),
130+
)
131+
}
110132
}
111133

112134
#[cfg(feature = "std")]

governor/src/state/direct/future.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -118,14 +118,13 @@ where
118118

119119
#[cfg(test)]
120120
mod test {
121-
use all_asserts::assert_gt;
122121

123122
use super::*;
124123

125124
#[test]
126125
fn insufficient_capacity_impl_coverage() {
127126
let i = InsufficientCapacity(1);
128127
assert_eq!(i.0, i.clone().0);
129-
assert_gt!(format!("{}", i).len(), 0);
128+
assert!(!format!("{}", i).is_empty());
130129
}
131130
}

0 commit comments

Comments
 (0)