boinkor-net / governor

A rate-limiting library for Rust (f.k.a. ratelimit_meter)

Home Page:https://github.com/boinkor-net/governor

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Possible regression from 0.4 to 0.5

AaronErhardt opened this issue · comments

Maintainer of actix-governor here.

After upgrading actix-governor's version of governor, our test cases started to tail.
It doesn't look like the rate-limiting itself is broken, but the reporting of the remaining burst size.

While looking through the changes I noticed that this test was added:

https://github.com/antifuchs/governor/blob/b8d99cf6b01c1969bce2e3497986b2ae59d33ac4/governor/tests/middleware.rs#L73-L102

According to the test, everything seemed to work as expected: After one check, the remaining burst size goes down by one.
Yet in our tests it went down from two to zero in just one step.

After some testing I found that using FakeRelativeClock made a difference.
Running with the fake clock works, but with a real clock it doesn't anymore.

This test case shows this behavior:

#[test]
fn state_snapshot_tracks_quota_accurately() {
    use governor::middleware::StateInformationMiddleware;
    use governor::{Quota, RateLimiter};
    use std::num::NonZeroU32;
    use std::time::Duration;

    let burst_size = NonZeroU32::new(2).unwrap();
    let period = Duration::from_millis(90);
    let quota = Quota::with_period(period).unwrap().allow_burst(burst_size);

    let clock = FakeRelativeClock::default();

    // First test
    let lim = RateLimiter::direct_with_clock(quota, &clock)
        .with_middleware::<StateInformationMiddleware>();

    assert_eq!(lim.check().unwrap().remaining_burst_capacity(), 1);
    assert_eq!(lim.check().unwrap().remaining_burst_capacity(), 0);
    assert_eq!(lim.check().map_err(|_| ()), Err(()), "should rate limit");

    // Now with a real clock
    let lim = RateLimiter::direct(quota)
        .with_middleware::<StateInformationMiddleware>();

    assert_eq!(lim.check().unwrap().remaining_burst_capacity(), 1); // <- This returns 0 instead
    assert_eq!(lim.check().unwrap().remaining_burst_capacity(), 0);
    assert_eq!(lim.check().map_err(|_| ()), Err(()), "should rate limit");
}

Hrmmmmm! Thanks for the report. This is pretty concerning - I hoped that this time, I'd have gotten the clock behavior right so tests reflect actual reality. I'll dig, but can't promise that I'll get it done before the holidays. /:

Actually, this seems to be easier than I thought: The tests involving futures&"real" clocks end up failing when the quota reporting is fixed, and exhibit very weird & flakey behavior. I suspect that quanta (the clock provider) is being a bit odd (it used to inject "calibration" runs the first time you'd measure, which added 50 or so millis of jitter to every test run).

Think I've got a fix for this, in #159! Hope tests pass this time around (:

Released in 0.5.1, which should hit docs.rs and crates.io in moments!

Awesome! Actix-governor no longer lags behind the official governor release: AaronErhardt/actix-governor#37

Thanks a lot for this quick fix, Andreas!

Thank you for the super detailed & effective report!