pub struct RedisRateLimiter { /* private fields */ }Expand description
A distributed, fixed-window rate limiter backed by a Redis server.
RateLimiter only protects a single process: two rws instances behind
a load balancer each track their own in-memory counters, so the effective
limit for a client doubles (or worse) as replicas scale out. RedisRateLimiter
keys the counter on a shared Redis server instead, so every instance enforces
the same budget.
Unlike RateLimiter’s sliding window (a deque of timestamps), this uses a
fixed-window counter: key’s count lives under one Redis key with a TTL equal
to the window length, incremented atomically via INCR. This is the standard
distributed rate-limiting pattern — Redis guarantees INCR is atomic across
concurrent callers, so counters stay correct even under heavy concurrent load
from multiple rws processes. The tradeoff versus a sliding window: a client
can burst up to 2x max_requests across a window boundary (once near the end
of one window, once at the start of the next).
Cloning is cheap — all clones share the same underlying TCP connection (one
persistent connection per RedisRateLimiter instance).
§Example
use rust_web_server::rate_limit::RedisRateLimiter;
let limiter = RedisRateLimiter::new("127.0.0.1:6379", None, 100, 60); // 100 req / 60s
match limiter.check("192.168.1.1") {
Ok(true) => { /* process request */ }
Ok(false) => { /* return 429 Too Many Requests */ }
Err(e) => { /* Redis unreachable — decide fail-open vs fail-closed */ }
}Implementations§
Source§impl RedisRateLimiter
impl RedisRateLimiter
Sourcepub fn new(
addr: impl Into<String>,
password: Option<String>,
max_requests: u32,
window_secs: u64,
) -> Self
pub fn new( addr: impl Into<String>, password: Option<String>, max_requests: u32, window_secs: u64, ) -> Self
Create a limiter that connects to addr (e.g. "127.0.0.1:6379"),
allowing max_requests per window_secs-second window.
password is passed to Redis AUTH if Some.
Sourcepub fn from_env() -> Self
pub fn from_env() -> Self
Build a limiter from environment variables:
RWS_REDIS_HOST(default127.0.0.1)RWS_REDIS_PORT(default6379)RWS_REDIS_PASSWORD(optional)RWS_CONFIG_RATE_LIMIT_MAX_REQUESTS(default1000)RWS_CONFIG_RATE_LIMIT_WINDOW_SECS(default60)
Sourcepub fn set_limits(&self, max_requests: u32, window_secs: u64)
pub fn set_limits(&self, max_requests: u32, window_secs: u64)
Update the limits on a live limiter without restarting.
Sourcepub fn retry_after(&self, _key: &str) -> u64
pub fn retry_after(&self, _key: &str) -> u64
Seconds a rejected key should wait before trying again: the window
length, a safe upper bound.
Sourcepub fn check(&self, key: &str) -> Result<bool>
pub fn check(&self, key: &str) -> Result<bool>
Returns Ok(true) if key (typically a client IP) is within the rate
limit, Ok(false) if the limit has been exceeded, or Err if the
Redis server could not be reached.
A call always increments the shared counter, whether permitted or not,
so callers must decide for themselves whether to fail open (allow the
request) or fail closed (deny it) on Err — this limiter does not
silently pick one.
Trait Implementations§
Auto Trait Implementations§
impl !Freeze for RedisRateLimiter
impl RefUnwindSafe for RedisRateLimiter
impl Send for RedisRateLimiter
impl Sync for RedisRateLimiter
impl Unpin for RedisRateLimiter
impl UnsafeUnpin for RedisRateLimiter
impl UnwindSafe for RedisRateLimiter
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
Source§fn in_current_span(self) -> Instrumented<Self> ⓘ
fn in_current_span(self) -> Instrumented<Self> ⓘ
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more