RateLimitMiddleware — Rate Limiting Middleware¶
Overview¶
RateLimitMiddleware is a sliding-window based rate limiting middleware. It controls
event publishing frequency during the before_publish phase, automatically discarding
events that exceed the limit (without calling next) and logging a warning.
Two rate limiting modes are supported:
- Global limiting (
per_event=False): All events share a single window. - Per-event limiting (
per_event=True): Each event name counts independently.
Pure in-memory implementation with no external dependencies.
Use Cases¶
- Anti-abuse protection: Limit high-frequency event publishing from clients.
- Resource protection: Prevent downstream handlers from being overwhelmed by traffic bursts.
- Degradation strategy: Automatically drop non-critical events under high system load.
- Test control: Precisely control event flow rates in test environments.
Function Signature¶
class RateLimitMiddleware(Middleware):
def __init__(
self,
max_requests: int = 100,
window_seconds: float = 1.0,
per_event: bool = False,
) -> None
| Parameter | Type | Description |
|---|---|---|
max_requests |
int |
Maximum allowed requests within the time window. Default 100. |
window_seconds |
float |
Sliding window size in seconds. Default 1.0. |
per_event |
bool |
If True, count independently per event name; otherwise share a single global window. Default False. |
Properties¶
| Property | Type | Description |
|---|---|---|
current_rate |
Dict[str, int] |
Returns a snapshot of current request counts per window. Key is the event name (or "__global__"), value is the count in the current window. |
Workflow¶
sequenceDiagram
participant P as Publisher
participant RL as RateLimitMiddleware
participant N as Downstream/Core
P->>RL: before_publish(name, data)
RL->>RL: Purge expired timestamps
RL->>RL: Check current window count
alt Under limit
RL->>RL: Record timestamp
RL->>N: next()
else Over limit
RL-->>RL: Log WARNING
Note over RL: return (discard event)
end
- On
before_publish, timestamps outside the window (earlier thannow - window_seconds) are purged first. - Check whether requests in the current window ≥
max_requests. - If under limit: record the current timestamp, call
nextto continue the publish flow. - If over limit: log a
WARNING, return directly (skipnext), event is discarded.
Usage Examples¶
Global Limiting¶
All events share a limit of 100 per second:
from infinity_bus.templates.middlewares import RateLimitMiddleware
from infinity_bus import MiddlewareChain
mw = RateLimitMiddleware(max_requests=100, window_seconds=1.0)
chain = MiddlewareChain()
chain.add(mw)
Per-Event Limiting¶
Each event name counts independently:
# mw.ping max 50/sec, user.login max 10/sec (each independently)
mw = RateLimitMiddleware(
max_requests=50,
window_seconds=1.0,
per_event=True,
)
Query Current Rate¶
# View current request counts per window
print(mw.current_rate)
# Example output: {'__global__': 42} or {'mw.ping': 5, 'user.login': 2}
Composing with Other Middlewares¶
Rate-limit first, then transform — ensuring transforms only apply to events that pass the rate limit:
from infinity_bus.templates.middlewares import (
RateLimitMiddleware,
EventTransformMiddleware,
make_field_inject_transform,
)
chain = MiddlewareChain()
chain.add(RateLimitMiddleware(max_requests=10, window_seconds=1.0))
chain.add(EventTransformMiddleware(
make_field_inject_transform(source="trusted")
))
# Execution order: rate limit → inject field → core publish
Notes¶
- Silent discard: Rate-limited events are silently dropped with no exception raised. To detect discards, monitor logs or implement a custom middleware.
- Window precision: Based on
time.monotonic(), unaffected by system clock adjustments. - Concurrency safety: Counting operations are protected by
asyncio.Lock. - Memory footprint: Each window retains at most
max_requeststimestamps; memory overhead is manageable. - No
on_publish_error: Rate limiting occurs before enqueueing and does not trigger the error hook.
Full Example¶
See tests/templates/middlewares/rate_limit_test.py
Contains test cases for global limiting, per-event limiting, and compose-with-transform after rate limiting scenarios.