Skip to main content

RateLimiter

Struct RateLimiter 

Source
pub struct RateLimiter { /* private fields */ }
Expand description

The sliding-window rate limiter over one shared in-memory store.

Build it once at startup with RateLimiter::new (fail closed on a non-positive limit or window) and share the handle across requests; the internal store is a mutex-guarded LRU, safe for concurrent services.

Implementations§

Source§

impl RateLimiter

Source

pub fn new(config: RateLimitConfig) -> Result<Self, RateLimitConfigError>

Validate the config and build the limiter.

§Errors

Fails closed with a RateLimitConfigError when rate_limit or rate_limit_window is zero (the reference rejects both with ge=1); nothing but RateLimitConfig::default-shaped values is substituted silently.

Source

pub fn with_config_and_clock( config: RateLimitConfig, clock: Clock, ) -> Result<Self, RateLimitConfigError>

Validate the config and build the limiter over an injected clock: the combined seam new and with_clock cover separately. Production builds use RateLimiter::new; adapters and stage tests that drive window sliding with a fake clock under a production-shaped config use this one.

§Errors

Same fail-closed behavior as RateLimiter::new: a zero rate_limit or rate_limit_window is a RateLimitConfigError.

Source

pub fn with_config( &self, config: RateLimitConfig, ) -> Result<Self, RateLimitConfigError>

A limiter over config that shares this one’s window store, clock and distributed store: the live-config swap a dynamic rule drives (the limits change, the recorded windows stay).

§Errors

Same fail-closed validation as RateLimiter::new.

Source

pub fn with_distributed_store( self, store: Arc<dyn SlidingWindowStore>, redis_prefix: &str, redis_fail_open: bool, ) -> Self

Swap the wall clock. Test seam: production builds use the system clock; deterministic window-sliding coverage injects a fake. Run the limiter over a distributed window store (the reference enable_redis && redis_handler conjunction): every tier’s hit records into {prefix}rate_limit:rate:{ip}[:{endpoint hash}] through one transaction of the reference’s four operations, and the decision compares the post-recording count with count <= limit (the Redis formulation; the same boundary as the in-memory count < limit).

redis_fail_open = true mirrors the reference redis_fail_open: a failing backend call degrades to the in-memory window (the workers x rate_limit caveat applies); false (the default) surfaces the failure as Err so the caller can answer the reference 503 "Redis rate limiting unavailable".

Source

pub fn check_distributed( &self, ip: IpAddr, endpoint: Option<&str>, ) -> Result<RateLimitDecision, StoreError>

RateLimiter::check over the distributed store: Err means a failing backend with redis_fail_open = false.

§Errors

StoreError when the backend call fails and fail-open is off.

Source

pub fn check_tiers_distributed( &self, ip: IpAddr, url_path: Option<&str>, route: Option<&RouteRateLimits>, country_of_ip: Option<&dyn Fn(IpAddr) -> Option<String>>, ) -> Result<TierDecision, StoreError>

RateLimiter::check_tiers over the distributed store: every tier keys its window the way the reference does (endpoint tiers keyed with the hashed path segment, the global tier by IP alone).

§Errors

StoreError when a tier’s backend call fails and fail-open is off.

Source

pub fn with_clock(clock: Clock) -> Self

Source

pub const fn config(&self) -> &RateLimitConfig

The validated config the limiter decides under.

Source

pub fn check(&self, ip: IpAddr, endpoint: Option<&str>) -> RateLimitDecision

Record one request for ip and decide it.

endpoint selects the scope: None is the global per-IP window, Some(path) the per-endpoint window keyed by (ip, path). A disabled limiter allows without recording. The returned decision’s count includes the request just recorded, so a block reports the crossing count exactly as the references log it.

Source

pub fn check_tiers( &self, ip: IpAddr, url_path: Option<&str>, route: Option<&RouteRateLimits>, country_of_ip: Option<&dyn Fn(IpAddr) -> Option<String>>, ) -> TierDecision

Record one request for ip under every configured tier and decide it (the reference RateLimitCheck.check order, via the Go port’s tiersFor/runTier): the endpoint tier, the route tier, the geo tier, then the global tier. Every configured tier records one hit into its own window (each tier is an independent budget, exactly as the references count), and the first tier that crosses its limit decides the outcome with its own window for Retry-After.

url_path is the request path the endpoint tier matches (endpoint_rate_limits exact match) and the route/geo tiers key their windows by; without a path those tiers cannot key an isolated window and are skipped (the references always carry a request path here). route carries the decorator tiers (None skips both). country_of_ip resolves the geolocation the geo tier reads (the reference geo_handler.get_country); None or an unresolved country skips the geo tier unless a "*" entry covers it. A disabled limiter allows without recording anything.

Source

pub fn reset(&self)

Drop every window, every recorded request included (reset in the references; test harnesses use it to isolate cases).

Source

pub fn tracked_windows(&self) -> usize

How many windows the store currently tracks (test/observability seam for the LRU cap).

Trait Implementations§

Source§

impl Clone for RateLimiter

Source§

fn clone(&self) -> Self

A clone shares the window store and the clock, the references’ singleton semantics: requests recorded through any handle land in the same sliding windows.

1.0.0 (const: unstable) · Source§

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

Performs copy-assignment from source. Read more
Source§

impl Debug for RateLimiter

Source§

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

Formats the value using the given formatter. Read more

Auto Trait Implementations§

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> Same for T

Source§

type Output = T

Should always be Self
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 = !

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

fn try_from(value: U) -> Result<T, !>

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.