Files

38 lines
1.1 KiB
Python

from __future__ import annotations
import asyncio
import time
from dataclasses import dataclass
@dataclass
class Bucket:
tokens: float
updated: float
class RateLimited(RuntimeError):
def __init__(self, retry_after: int = 1) -> None:
self.retry_after = retry_after
class ConservativeRateLimiter:
"""Process-local fallback. Redis may accelerate this, never own correctness."""
def __init__(self, text_rps: int, file_rps: int) -> None:
self.rates = {"text": text_rps, "file": file_rps}
now = time.monotonic()
self.buckets = {kind: Bucket(float(rate), now) for kind, rate in self.rates.items()}
self.lock = asyncio.Lock()
async def acquire(self, kind: str) -> None:
async with self.lock:
now = time.monotonic()
bucket = self.buckets[kind]
rate = self.rates[kind]
bucket.tokens = min(float(rate), bucket.tokens + (now - bucket.updated) * rate)
bucket.updated = now
if bucket.tokens < 1:
raise RateLimited
bucket.tokens -= 1