pyrate_limiter.abstracts.algorithm module¶
Rate-limiting algorithm abstraction.
Separates the policy (which rates admit an item, how long a rejected one waits, how far back items may be leaked) from the storage that counts and persists them. Internal in v4; v5 promotes it to a public extension point.
Retry-after rides on Decision so one check under one lock yields both the
verdict and the wait. Deriving it afterwards costs a second round trip and
reads state that may have moved - and is impossible for algorithms whose state
is not a log (token bucket, GCRA), which compute the wait in closed form.
- pyrate_limiter.abstracts.algorithm.ADMITTED = Decision(failing_rate=None, retry_after_ms=None)¶
Reused on every admit;
Decisionis immutable, so the hot path allocates nothing.
- class pyrate_limiter.abstracts.algorithm.Algorithm¶
Bases:
ABCA rate-limiting policy, independent of any storage backend.
Implementations must be stateless so one instance can be shared across buckets and threads. The two sub-interfaces differ in what they need remembered per key:
LogAlgorithman entry per consumed unit,StateAlgorithma fixed handful of numbers.- max_weight(rate)¶
Largest weight this policy can ever admit under
rate.- Return type:
int
- class pyrate_limiter.abstracts.algorithm.Decision(failing_rate=None, retry_after_ms=None)¶
Bases:
objectOutcome of an admit check.
retry_after_msis measured from the checked item’s own timestamp.Nonemeans “unknown, askAbstractBucket.waiting()” - either the weight can never fit, or the backend does not compute a wait. It does not mean “no wait”.- property allowed¶
- failing_rate = None¶
- retry_after_ms = None¶
- class pyrate_limiter.abstracts.algorithm.FixedWindow¶
Bases:
LogAlgorithmCounts within a wall-clock-aligned window that resets every
interval.Cheaper and coarser than the rolling window: up to
2 * limitcan pass across a window boundary. Use it to mirror an upstream API that genuinely resets on the hour rather than rolling.- admit(rates, counts, weight)¶
Whether
weightmore units fit, givencountsaligned torates.- Return type:
- retry_after(rate, now, blocking_timestamp)¶
Milliseconds until room exists under
rate.blocking_timestampis the entry named byblocking_offset(), orNonewhen there is none - or when the policy never asks for one.- Return type:
int
- window_start(rate, now)¶
Inclusive lower bound of
rate’s counting window atnow.- Return type:
int
- class pyrate_limiter.abstracts.algorithm.GCRA¶
Bases:
StateAlgorithmGeneric Cell Rate Algorithm - a leaky bucket kept as one timestamp.
Tracks a theoretical arrival time (TAT) per rate: the moment the bucket would next be empty. Admitting
weightpushes the TAT forward byweight * emission_interval; the request is allowed while that stays withinburstunits ofnow.Sustains
limitperintervalwhile tolerating a burst ofrate.burst, using one number per rate instead of an entry per unit.State is integer microseconds, not fractional milliseconds. An absolute TAT in epoch ms is ~1.7e12, and accumulating a fractional emission interval onto it loses the low bits - enough that the accumulated sum of burst emissions no longer equals burst * emission, and the last unit of a full burst gets rejected by a rounding error. Integers make it exact, and stay well inside the 2**53 a Lua double holds.
- consumed(rates, state, now)¶
Units currently owed - the closest analogue to a log’s length.
- Return type:
int
- decode(values)¶
Parse persisted strings back into state.
- Return type:
Tuple[float,...]
- initial(rates)¶
State for a key that has never been used.
- Return type:
Tuple[float,...]
- max_weight(rate)¶
Largest weight this policy can ever admit under
rate.- Return type:
int
- redis_args(rates)¶
Arguments
redis_script()needs, after the standard header.The store passes these through without inspecting them, so a policy’s script and its arguments stay a matched pair that only the policy knows the shape of. The header the store supplies first is
now, weight, ttl_ms, len(rates).- Return type:
List[int|float]
- redis_script()¶
Lua implementing
step()atomically, if this policy has one.- Return type:
str|None
- class pyrate_limiter.abstracts.algorithm.LogAlgorithm¶
Bases:
AlgorithmPolicy over storage holding one timestamped entry per consumed unit.
- abstractmethod admit(rates, counts, weight)¶
Whether
weightmore units fit, givencountsaligned torates.- Return type:
- blocking_offset(rate, weight)¶
Offset from the newest stored entry (0-based) whose expiry makes room for
weight, orNoneif the wait does not depend on an entry.- Return type:
int|None
- decide(rates, counts, weight, now, peek_timestamp)¶
admit(), resolving the retry-after in the same step on denial.peek_timestamp(offset)is only called when the policy asks for an entry and the item was rejected, so backends pay for the lookup only when it is needed.- Return type:
- leak_bound(rates, now)¶
Timestamp below which an entry is outside every rate’s window.
- Return type:
int
- abstractmethod retry_after(rate, now, blocking_timestamp)¶
Milliseconds until room exists under
rate.blocking_timestampis the entry named byblocking_offset(), orNonewhen there is none - or when the policy never asks for one.- Return type:
int
- abstractmethod window_start(rate, now)¶
Inclusive lower bound of
rate’s counting window atnow.- Return type:
int
- class pyrate_limiter.abstracts.algorithm.SlidingWindowLog¶
Bases:
LogAlgorithmPrecise rolling window: admit while each rate’s last
intervalstays under its limit.The default. Exact, at the cost of one stored entry per consumed unit.
- admit(rates, counts, weight)¶
Whether
weightmore units fit, givencountsaligned torates.- Return type:
- blocking_offset(rate, weight)¶
Offset from the newest stored entry (0-based) whose expiry makes room for
weight, orNoneif the wait does not depend on an entry.- Return type:
int|None
- retry_after(rate, now, blocking_timestamp)¶
Milliseconds until room exists under
rate.blocking_timestampis the entry named byblocking_offset(), orNonewhen there is none - or when the policy never asks for one.- Return type:
int
- window_start(rate, now)¶
Inclusive lower bound of
rate’s counting window atnow.- Return type:
int
- pyrate_limiter.abstracts.algorithm.State¶
Constant-state policies keep a small tuple of floats per key, opaque to the store that persists it. GCRA uses one theoretical-arrival-time per rate.
alias of
Tuple[float, …]
- class pyrate_limiter.abstracts.algorithm.StateAlgorithm¶
Bases:
AlgorithmPolicy whose state is a fixed-size tuple of numbers, not a log.
Storage keeps one small value per key however much traffic passes, and the wait comes out in closed form. In exchange the check is destructive - it spends what it admits - so
step()must evaluate every rate before committing any of them.- consumed(rates, state, now)¶
Units currently owed - the closest analogue to a log’s length.
- Return type:
int
- decode(values)¶
Parse persisted strings back into state.
- Return type:
Tuple[float,...]
- abstractmethod initial(rates)¶
State for a key that has never been used.
- Return type:
Tuple[float,...]
- redis_args(rates)¶
Arguments
redis_script()needs, after the standard header.The store passes these through without inspecting them, so a policy’s script and its arguments stay a matched pair that only the policy knows the shape of. The header the store supplies first is
now, weight, ttl_ms, len(rates).- Return type:
List[int|float]
- redis_script()¶
Lua implementing
step()atomically, if this policy has one.- Return type:
str|None
- class pyrate_limiter.abstracts.algorithm.TokenBucket¶
Bases:
GCRAToken bucket, which is GCRA under a more familiar name.
A bucket of
rate.bursttokens refilling atrate.limit / rate.intervaladmits exactly what GCRA does with an emission interval ofinterval / limit. Same implementation, one float of state rather than a token count plus a refill timestamp.