boinkor-net / governor

A rate-limiting library for Rust (f.k.a. ratelimit_meter)
https://github.com/boinkor-net/governor
MIT License
550 stars 43 forks source link

until_ready weird behavior #196

Closed fcs-ts closed 1 year ago

fcs-ts commented 1 year ago

I am trying to consume a very simple API, but I am hitting some unexpected behavior when using until_ready to wait for the rate limiter to be available again.

My code looks like this

struct RateLimitedClient {
    client: reqwest::Client,
    limiter: governor::DefaultDirectRateLimiter,
}

impl RateLimitedClient {
    fn new(reqs_per_minute: u32) -> Result<RateLimitedClient> {
        let reqs_per_minute =
            NonZeroU32::new(reqs_per_minute).ok_or(anyhow!("reqs_per_minute can't be zero."))?;
        let quota = governor::Quota::per_minute(reqs_per_minute);
        Ok(RateLimitedClient {
            client: reqwest::Client::new(),
            limiter: governor::RateLimiter::direct(quota),
        })
    }
}

async fn make_request(
    url: &str,
    rate_limited_client: &RateLimitedClient
) -> Result<()> {
    rate_limited_client.limiter.until_ready().await;
    if rate_limited_client.limiter.check().is_err() {
        return Err(anyhow!("Rate limit blocked request"));
    };
    let res = rate_limited_client
        .client
        .get(url)
        .send()
        .await
        .context("Error on the request.")?;
}

But I am hitting the rate limiter error (i.e. check() not being ready) every time I reach the limit, which I understand I should be hitting because of the previous until_ready() call. I am missing something?

Thanks in advance!

antifuchs commented 1 year ago

Oof, I think the documentation for async functions may not be good enough yet: until_ready calls check on your behalf, waiting (and calling check) until it has succeeded and the rate limit lets your call through.

You don't need to call check a second time.

fcs-ts commented 1 year ago

Oooh, that makes total sense. Thanks for the prompt response :)