Skip to content

Bug: Async rate limiting fails with 'async call made without an async bucket' #38

@dgunning

Description

@dgunning

Description

When using async_http_client() for async HTTP requests, the rate limiter fails with:

async call made without an async bucket

This occurs because create_rate_limiter() in ratelimiter.py creates an InMemoryBucket, which doesn't support async operations in pyrate-limiter 3.x.

Steps to Reproduce

from httpxthrottlecache import HttpxThrottleCache
import asyncio

mgr = HttpxThrottleCache(request_per_sec_limit=9)

async def test():
    async with mgr.async_http_client() as client:
        response = await client.get("https://httpbin.org/get")
        print(response.status_code)

asyncio.run(test())

Output:

async call made without an async bucket.

Root Cause

In ratelimiter.py:14-24:

def create_rate_limiter(requests_per_second: int, max_delay: Duration | int = Duration.DAY) -> Limiter:
    rate = Rate(requests_per_second, Duration.SECOND)
    rate_limits = [rate]
    base_bucket = InMemoryBucket(rate_limits)  # <-- sync-only bucket
    bucket = base_bucket
    limiter = Limiter(bucket, max_delay=max_delay, raise_when_fail=False, retry_until_max_delay=True)
    return limiter

InMemoryBucket from pyrate-limiter 3.x doesn't have async support. When AsyncRateLimitingTransport.handle_async_request() calls limiter.try_acquire_async(), it fails.

Suggested Fix

Use BucketAsyncWrapper to wrap the bucket for async support:

from pyrate_limiter import InMemoryBucket, BucketAsyncWrapper, Limiter, Rate, Duration

def create_rate_limiter(requests_per_second: int, max_delay: Duration | int = Duration.DAY) -> Limiter:
    rate = Rate(requests_per_second, Duration.SECOND)
    base_bucket = InMemoryBucket([rate])
    async_bucket = BucketAsyncWrapper(base_bucket)
    return Limiter(async_bucket, max_delay=max_delay, raise_when_fail=False, retry_until_max_delay=True)

Note: BucketAsyncWrapper requires a running event loop when created, so this may need to be lazily initialized or created differently.

Environment

  • httpxthrottlecache: 0.3.0
  • pyrate-limiter: 3.9.0
  • Python: 3.11

Impact

This bug prevents using httpxthrottlecache for any async HTTP operations, which is a significant limitation for libraries that need both sync and async support.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions