KeyedRateLimiter

Struct KeyedRateLimiter 

Source
pub struct KeyedRateLimiter<K: Eq + Hash + Clone, A: Algorithm<C::Instant> = DefaultAlgorithm, C: Clock = DefaultClock, H: BuildHasher + Clone = RandomState>{ /* private fields */ }
Expand description

An in-memory rate limiter that regulates a single rate limit for multiple keys.

Keyed rate limiters can be used to e.g. enforce a per-IC address or a per-customer request limit on the server side.

This implementation of the keyed rate limiter uses evmap, a read lock-free, concurrent hash map. Addition of new keys (e.g. a new customer making their first request) is synchronized and happens one at a time (it synchronizes writes to minimize the effects from evmap’s eventually consistent behavior on key addition), while reads of existing keys all happen simultaneously, then get synchronized by the rate limiting algorithm itself.

use ratelimit_meter::{KeyedRateLimiter};
let mut limiter = KeyedRateLimiter::<&str>::new(nonzero!(1u32), Duration::from_secs(5));
assert_eq!(Ok(()), limiter.check("customer1")); // allowed!
assert_ne!(Ok(()), limiter.check("customer1")); // ...but now customer1 must wait 5 seconds.

assert_eq!(Ok(()), limiter.check("customer2")); // it's customer2's first request!

§Expiring old keys

If a key has not been checked in a long time, that key can be expired safely (the next rate limit check for that key would behave as if the key was not present in the map, after all). To remove the unused keys and free up space, use the cleanup method:

use ratelimit_meter::{KeyedRateLimiter};
let mut limiter = KeyedRateLimiter::<&str>::new(nonzero!(100u32), Duration::from_secs(5));
limiter.check("hi there");
// time passes...

// remove all keys that have been expireable for 10 minutes:
limiter.cleanup(Duration::from_secs(600));

Implementations§

Source§

impl<C, A, K> KeyedRateLimiter<K, A, C>

Source

pub fn new(capacity: NonZeroU32, per_time_unit: Duration) -> Self

Construct a new rate limiter that allows capacity cells per time unit through.

§Examples
use ratelimit_meter::{KeyedRateLimiter};
let _limiter = KeyedRateLimiter::<&str>::new(nonzero!(100u32), Duration::from_secs(5));
Source

pub fn per_second(capacity: NonZeroU32) -> Self

Construct a new keyed rate limiter that allows capacity cells per second.

§Examples

Constructing a rate limiter keyed by &str that lets through 100 cells per second:

use ratelimit_meter::{KeyedRateLimiter, GCRA};
let _limiter = KeyedRateLimiter::<&str, GCRA>::per_second(nonzero!(100u32));
Source

pub fn build_with_capacity( capacity: NonZeroU32, ) -> Builder<K, C, A, RandomState>

Return a constructor that can be used to construct a keyed rate limiter with the builder pattern.

Source

pub fn check( &mut self, key: K, ) -> Result<(), <A as Algorithm<C::Instant>>::NegativeDecision>

Tests if a single cell for the given key can be accommodated at Instant::now(). If it can be, check updates the rate limiter state on that key to account for the conforming cell and returns Ok(()).

If the cell is non-conforming (i.e., it can’t be accomodated at this time stamp), check_at returns Err with information about the earliest time at which a cell could be considered conforming under that key.

Source

pub fn check_n( &mut self, key: K, n: u32, ) -> Result<(), NegativeMultiDecision<<A as Algorithm<C::Instant>>::NegativeDecision>>

Tests if n cells for the given key can be accommodated at the current time stamp. If (and only if) all cells in the batch can be accomodated, the MultiDecider updates the rate limiter state on that key to account for all cells and returns Ok(()).

If the entire batch of cells would not be conforming but the rate limiter has the capacity to accomodate the cells at any point in time, check_n_at returns error NegativeMultiDecision::BatchNonConforming, holding the number of cells and the rate limiter’s negative outcome result.

If n exceeds the bucket capacity, check_n_at returns NegativeMultiDecision::InsufficientCapacity, indicating that a batch of this many cells can never succeed.

Source

pub fn check_at( &mut self, key: K, at: C::Instant, ) -> Result<(), <A as Algorithm<C::Instant>>::NegativeDecision>

Tests whether a single cell for the given key can be accommodated at the given time stamp. See check.

Source

pub fn check_n_at( &mut self, key: K, n: u32, at: C::Instant, ) -> Result<(), NegativeMultiDecision<<A as Algorithm<C::Instant>>::NegativeDecision>>

Tests if n cells for the given key can be accommodated at the given time (Instant::now()), using check_n

Source

pub fn cleanup<D: Into<Option<Duration>>>(&mut self, min_age: D) -> Vec<K>

Removes the keys from this rate limiter that can be expired safely and returns the keys that were removed.

To be eligible for expiration, a key’s rate limiter state must be at least min_age past its last relevance (see RateLimitState.last_touched).

This method works in two parts, but both parts block new keys from getting added while they’re running:

  • First, it collects the keys that are eligible for expiration.
  • Then, it expires these keys.

Note that this only affects new keys that need to be added. Rate-limiting operations on existing keys continue concurrently.

§Race conditions

Since this is happening concurrently with other operations, race conditions can & will occur. It’s possible that cells are accounted between the time cleanup_at is called and their expiry. These cells will be lost.

The time window in which this can occur is hopefully short enough that this is an acceptable risk of loss in accuracy.

Source

pub fn cleanup_at<D: Into<Option<Duration>>, I: Into<Option<C::Instant>>>( &mut self, min_age: D, at: I, ) -> Vec<K>

Removes the keys from this rate limiter that can be expired safely at the given time stamp. See cleanup. It returns the list of expired keys.

Trait Implementations§

Source§

impl<K: Clone + Eq + Hash + Clone, A: Clone + Algorithm<C::Instant>, C: Clone + Clock, H: Clone + BuildHasher + Clone> Clone for KeyedRateLimiter<K, A, C, H>

Source§

fn clone(&self) -> KeyedRateLimiter<K, A, C, H>

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl<A, K, C: Clock> Debug for KeyedRateLimiter<K, A, C>

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more

Auto Trait Implementations§

§

impl<K, A = LeakyBucket, C = MonotonicClock, H = RandomState> !Freeze for KeyedRateLimiter<K, A, C, H>

§

impl<K, A = LeakyBucket, C = MonotonicClock, H = RandomState> !RefUnwindSafe for KeyedRateLimiter<K, A, C, H>

§

impl<K, A, C, H> Send for KeyedRateLimiter<K, A, C, H>
where <A as Algorithm<<C as Clock>::Instant>>::BucketState: Sized, C: Send, K: Send, H: Send,

§

impl<K, A = LeakyBucket, C = MonotonicClock, H = RandomState> !Sync for KeyedRateLimiter<K, A, C, H>

§

impl<K, A, C, H> Unpin for KeyedRateLimiter<K, A, C, H>
where <A as Algorithm<<C as Clock>::Instant>>::BucketState: Sized, A: Unpin, C: Unpin,

§

impl<K, A = LeakyBucket, C = MonotonicClock, H = RandomState> !UnwindSafe for KeyedRateLimiter<K, A, C, H>

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.