udoprog / leaky-bucket

A token-based rate limiter based on the leaky bucket algorithm.
Apache License 2.0
88 stars 9 forks source link

Allow manually refilling a RateLimiter #17

Open raftario opened 1 year ago

raftario commented 1 year ago

For scenarios where a task may preemptively acquire more tokens than it ends up requiring and wants to make its unused ones available

udoprog commented 1 year ago

We currently release tokens once an acquire future is dropped, so in a way this is already implemented but perhaps not to the way you want it.

Do you have an representative code example of what you'd want it to look like?

raftario commented 1 year ago

Something like this

// Operation could use up to 32 tokens
limiter.acquire(32).await;
// Future polled to completion
// Operation performed but ended only using 24 tokens
limiter.release(8);
udoprog commented 1 year ago

Ah, so you're just looking for a way to manually add permits to the rate limiter?

That's should be fairly easy to implement. It's essentially what the core task does. So we just need to borrow it ;)

jamesmunns commented 2 weeks ago

Noting that I could probably use this too, if I have a chance I might look at implementing this.

I have a use case where we need to get tokens for all matching rules (each with separate buckets), and if we get tokens from A and B, but C fails or times out, we could "give back" the acquired A and B tokens as they were unused.

raftario commented 1 week ago

I don't know whether this is something you'd want in this crate since it could be implemented on top of a simple method thay fills the bucket but a nice additional API for this could look something like this. Given how common the pattern of racing futures and dropping whichever ones don't win this could be really nice to have.

fn reserve(&self, permits: usize) -> impl Future<Reservation>;

impl Reservation {
  fn consume(&mut self, permits: usize) {
    self.used += permits;
  }
}

impl Drop for Reservation {
  fn drop(&mut self) {
    self.limiter.release(self.reserved - self.used);
  }
}