Changelog¶
All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog and this project adheres to Semantic Versioning.
[4.4.0]¶
Bug-fix, scalability, and internal-refactor release. No public API changes
(the new AbstractBucket.is_async attribute is additive).
Fixed¶
InMemoryBucket: guard the internal item list with a lock so the background
Leakerthread can no longer raceput/peek/leak. This was a data race in the default configuration (in-memory bucket + scheduled leak).MultiprocessBucketaliases this lock to its shared cross-process lock. (#302)PostgresClock: when the DB time query fails, fall back to local wall-clock epoch time instead of monotonic time. The monotonic fallback was ~5 orders of magnitude smaller than the stored epoch-ms timestamps and would corrupt every window comparison and leak bound. (#302)
Leaker: make the background sync-leak worker restartable. Re-registering a bucket after every bucket had been disposed previously raised
RuntimeError: threads can only be started once. (#302)Keep
Limiterpicklable after theInMemoryBucketlock addition. (#302)
Performance & Scalability¶
Limiter: release the limiter lock during the synchronous blocking wait, so a long wait on one key no longer serializes acquisitions for every other key sharing the limiter. (#304)
RedisBucket: batch weighted
ZADDs in bounded chunks inside the atomic Lua script, lowering latency for high-weight puts. (#284)
Internal / Refactor¶
Unify the limiter’s sync/async acquire plumbing into a single coroutine and share the delay-step decision across the sync and async branches. (#303)
Add a declarative
is_asyncbucket attribute so theLeakerno longer detects async by executing a side-effectingleak(0)probe.RedisBucketstill probes because it may wrap either a sync or an async client. (#305)Introduce an internal
Algorithm/Decisionseam (SlidingWindowLog) that the built-in buckets delegate their per-rate admit decision and leak bound to — the foundation for pluggable algorithms (e.g. GCRA, sliding-window-counter) in a future release. (#307)
Documentation¶
Document that
RedisBucketkeeps one sorted-set member per consumed unit, and that long-window / high-volume quotas may want a coarser counter-based backend for bounded memory. (#284)
CI¶
The release workflow now also creates a GitHub Release for the pushed tag and attaches the built
dist/*artifacts, in addition to publishing to PyPI.
[4.3.1]¶
Performance and maintenance release. No API or behavior changes.
Performance¶
PostgresBucket: insert all
weightunit-rows in a single statement (SELECT … FROM generate_series) instead of one INSERT per unit — ~3.4× faster weighted puts and a shorterEXCLUSIVElock hold. (#296)PostgresBucket: compute every rate’s windowed count in one query (
COUNT(*) FILTER) instead of one round trip per rate — ~2× faster multi-rate checks, fewer round trips under the lock. (#297)SingleBucketFactory.wrap_item: inline the sync fast path (no per-acquire closures) — ~23% faster item wrapping on the hot path. (#296)Deduplicate the sync/async deadline math in
_delay_waiterinto a single shared helper. (#294)
Documentation¶
Rewrite the README for accuracy, structure, and presentation, and replace the outdated architecture image with a component-level Mermaid diagram that renders on both GitHub and ReadTheDocs. (#293, #295)
CI¶
Move GitHub Actions off the deprecated Node-20 runtime to Node-24 (
checkout@v5,setup-python@v6,setup-uv@v7,upload-artifact@v6,download-artifact@v7). (#295)
[4.3.0]¶
Bug-fix and hardening release. It contains a few breaking changes that affect only edge or undocumented usage — see Breaking Changes below.
Breaking Changes¶
Ill-formed rate lists now raise
ValueErrorat bucket construction instead of being silently mis-enforced. A valid list is ordered by strictly increasing interval, with strictly increasing limits and non-increasing density (the “generous-before-tight” contract). (#239)binary_searchhas been removed from the public API. It was an undocumented internal helper, now replaced internally by the standard librarybisect. (#290)PgQueriesSQL templates now use bound%sparameters instead of the{offset}/{timestamp}format placeholders. (#233)
Security¶
SQLite and Postgres backends no longer build SQL through string interpolation. The user-supplied item name (SQLite) and the table name (Postgres) are now bound/quoted, closing a SQL-injection vector and fixing crashes on names containing quotes or other metacharacters. (#233, #244)
Fixed¶
Blocking
try_acquireno longer busy-spins (burning CPU until the next background leak) or spuriously times out whenbuffer_ms=0;waiting()now clears the inclusive window lower bound correctly. (#289)try_acquire_async(timeout=0)now succeeds when capacity is available instead of always returningFalse. (#289)SQLite
leak()no longer raisesAttributeErrorwhen invoked on a closed connection during teardown (fixes the unstabletest_sqlite_filelock_bucket). (#244)Rate lists are now consistently sorted by interval across all backends, fixing a latent
leak()bug when unsorted rates were passed to the Redis, SQLite, or Postgres buckets. (#239)
Changed¶
Replaced the custom
binary_searchwith the standard librarybisect. (#290)
Documentation¶
Fixed stale v4 examples in the README (removed the long-gone
clock=,raise_when_fail, andmax_delayparameters) and documented how to use a custom / distributed clock in v4. (#261)
[4.0.0]¶
Fourth major release with significant API simplification and breaking changes.
Breaking Changes¶
Exceptions Removed¶
BucketFullExceptionandLimiterDelayExceptionhave been removedRate limiting now uses blocking behavior instead of exception-based flow control
Limiter Constructor Simplified¶
Removed
clockparameter - each bucket now manages its own clock viabucket.now()Removed
raise_when_failparameter - no exceptions are raisedRemoved
max_delayparameter - blocking is now controlled per-callRemoved
retry_until_max_delayparameterAdded
buffer_msparameter (default 50ms) for clock drift tolerance
Clock Changes¶
TimeClockremoved - useMonotonicClockinsteadSQLiteClockremoved - buckets now manage their own timeTimeAsyncClockrenamed toMonotonicAsyncClockClock is no longer passed to Limiter; buckets have built-in
now()method
try_acquire API Changes¶
New signature:
try_acquire(name="pyrate", weight=1, blocking=True, timeout=-1)blocking=True(default): blocks until permit is acquiredblocking=False: returnsFalseimmediately if bucket is fullAdded
try_acquire_async()for proper async acquisition with asyncio.Lock
Decorator API Changes¶
Old:
limiter.as_decorator()(mapping_func)(target_func)where mapping returns(name, weight)New:
limiter.as_decorator(name="...", weight=...)(target_func)with direct parameters
BucketFactory Changes¶
schedule_leak(bucket, clock)nowschedule_leak(bucket)- no clock parametercreate(clock, bucket_class, ...)nowcreate(bucket_class, ...)- no clock parameter
New Features¶
Context manager support:
with Limiter(...) as limiter:andwith bucket:limiter.close()method for resource cleanuplimiter_factorymodule with convenience functions:create_sqlite_limiter()create_inmemory_limiter()init_global_limiter()
MultiprocessBucketfor multiprocessing environmentsWeb request helpers in
pyrate_limiter.extras:aiohttp_limiter.RateLimitedSessionhttpx_limiter.RateLimiterTransport/AsyncRateLimiterTransportrequests_limiter.RateLimitedRequestsSession
Time is now monotonic by default (
monotonic_ns())buffer_msconfigurable per-Limiter (default 50ms)
Improvements¶
Migrated from Poetry to UV for builds
Switched from Bitnami to official Redis/Postgres Docker images
Pre-commit switched to Ruff
Improved multiprocessing support with proper locking
Better asyncio support with thread-local async locks
[3.9.0]¶
Introduce MultiprocessBucket
Update documentation to include MultiprocessBucket
Add delay configure
Simplify lock interface for SQLFileLock & MultiprocessBucket
[3.8.1]¶
Keep Retrying until Max Delay Has Expired
Postgres performance turning
Fix cursor leaks on SQLiteBucket
[3.8.0]¶
Add FileLock option for SQLiteBucket
[3.7.1]¶
Update package metadata and local dev config to support python 3.13
[3.7.0]¶
Add method to remove bucket
[3.6.2]¶
Fix table creation for SQLiteBucket
[3.6.1]¶
Support creating/getting bucket asynchronously
[3.6.0]¶
Use psycopg3 for PostgresBucket
[3.5.1]¶
Fix dependencies for “all” package extra
[3.5.0]¶
Add PostgresBucket backend
[3.4.1]¶
Fix: unnecessary warning during async check
[3.4.0]¶
Improved in-memory-bucket performance
[3.3.0]¶
Fix background task for leaking
[3.2.1] - 2024-02-13¶
Fix Redis CROSSSLOT Keys following issue #126
[3.1.1] - 2024-01-02¶
Fix broken SqliteBucket following issue #132
[3.1.0] - 2023-08-28¶
Allow to pass rates directly to Limiter to use default ImMemoryBucket with Limiter
Allow to pass Duration to
max_delayargument of Limiter
[3.0.2] - 2023-08-28¶
Critical bug fix: importing redis fail crashing apps
[3.0.0] - 2023-08-28¶
Third major release with API breaking changes:
Drop python 3.7 (only python ^3.8)
Bucket must be initialized before passing to Limiter
Auto leaking (provided by BucketFactory)
Decorator API changes
Limiter workable with both async/sync out-of-the-box
Async RedisBucket built-in
Contextmanager not available yet
[2.10.0] - 2023-02-26¶
Updates¶
Add change log to sdist
Improve test coverage
Force check some bucket-keyword arguments
[2.9.1] - 2023-02-26¶
Fixed¶
Fix unit test to make test results stable
Fix remaining-time calculation using exact 3 decimals only
Increase test intesity to ensure correctness
[2.8.5] - TBD¶
Fixed¶
Fix SQLite OperationalError when getting more items than SQLite variable limit
[2.8.4] - 2022-11-23¶
Fixed¶
Build both
wheelandsdiston publish
[2.8.3] - 2022-10-17¶
Added¶
Add option to expire redis key when using RedisBucket
[2.8.2] - 2022-09-24¶
Removed¶
Python 3.6 support
[2.8.1] - 2022-04-11¶
Added¶
Add Sphinx config
Add documentation site: https://pyrate-limiter.readthedocs.io
Add some missing type hints
Add package metadata to indicate PEP-561 compliance
[2.8.0] - 2022-04-10¶
Added¶
Add
flush()method to all bucket classes
[2.7.0] - 2022-04-06¶
Added¶
Add
FileLockSQliteBucketfor a SQLite backend with file-based lockingAdd optional backend dependencies to package metadata
[2.6.3] - 2022-04-05¶
Fixed¶
Make SQLite bucket thread-safe and multiprocess-safe
[2.6.2] - 2022-03-30¶
Fixed¶
Remove development scripts from package published on PyPI
Added¶
Add
noxto run development scripts
[2.6.1] - 2022-03-30¶
Updated¶
Replace all formatting/linting tools with pre-commit
[2.6.0] - 2021-12-08¶
Added¶
Add
SQliteBucketto persist rate limit data in a SQLite database
[2.5.0] - 2021-12-08¶
Added¶
Custom time source
[2.4.6] - 2021-09-30¶
Add
RedisClusterBucketto support usingPyrateLimiterwithredis-py-clusterUpdate README, add Table of Content
[2.3.6] - 2021-09-23¶
Run CI tests for all supported python versions
Fix issue with deployments on Travis CI
[2.3.5] - 2021-09-22¶
Added¶
Use
time.monotonic()instead oftime.time()Support for floating point rate-limiting delays (more granular than 1 second)
[2.3.4] - 2021-06-01¶
Fixed¶
Bucket group initialization
[2.3.3] - 2021-05-08¶
Added¶
Support for python 3.6
[2.3.2] - 2021-05-06¶
Fixed¶
Incorrect type hint
[2.3.1] - 2021-04-26¶
Added¶
LICENSE file to be included in PyPI package
Fixed¶
Incorrect delay time when using using
Limiter.ratelimit()withdelay=True
[2.3.0] - 2021-03-01¶
Added¶
Support for using
Limiter.ratelimit()as a contextmanager or async contextmanagerSeparate
LimitContextDecoratorclass to handleLimiter.ratelimit()behaviorPackage published on conda-forge
[2.2.2] - 2021-03-03¶
Changed¶
Internal: Reduce cognitive complexity
[2.2.1] - 2021-03-02¶
Fixed¶
Incorrect check log against time-window
[2.2.0] - 2021-02-26¶
Added¶
Limiter.ratelimit()method, an async-compatible decorator that optionally adds rate-limiting delays
[2.1.0] - 2021-02-21¶
[2.0.3] - 2020-06-01¶
[2.0.2] - 2020-06-01¶
[2.0.1] - 2020-06-01¶
[2.0.0] - 2019-12-29¶
[1.1.0] - 2019-12-17¶
Removed¶
Code duplication
Added¶
Thread lock for Bucket’s state modification in case of Multi-threading
Html Cover Report
Fixed¶
LocalBucket’s default init value being mutated
Typos. A lot of friggin’ typos.