|
|
""" |
|
|
Token bucket rate limiting service |
|
|
""" |
|
|
import time |
|
|
import threading |
|
|
from collections import deque |
|
|
from src.utils.logger import logger |
|
|
|
|
|
|
|
|
class RateLimiter: |
|
|
""" |
|
|
β±οΈ TOKEN BUCKET RATE LIMITER |
|
|
""" |
|
|
def __init__(self, max_requests: int = 50, window_seconds: int = 60): |
|
|
self.max_requests = max_requests |
|
|
self.window_seconds = window_seconds |
|
|
self.requests = deque() |
|
|
self.lock = threading.Lock() |
|
|
|
|
|
def acquire(self) -> bool: |
|
|
""" |
|
|
β
ACQUIRE RATE LIMIT TOKEN |
|
|
Returns True if request is allowed, False otherwise |
|
|
""" |
|
|
with self.lock: |
|
|
current_time = time.time() |
|
|
|
|
|
|
|
|
while self.requests and current_time - self.requests[0] > self.window_seconds: |
|
|
self.requests.popleft() |
|
|
|
|
|
|
|
|
if len(self.requests) < self.max_requests: |
|
|
self.requests.append(current_time) |
|
|
logger.debug(f"β
Rate limit check passed ({len(self.requests)}/{self.max_requests})") |
|
|
return True |
|
|
|
|
|
|
|
|
wait_time = self.window_seconds - (current_time - self.requests[0]) |
|
|
logger.warning(f"β³ Rate limit exceeded. Wait {wait_time:.1f}s") |
|
|
|
|
|
|
|
|
time.sleep(wait_time + 0.1) |
|
|
self.requests.popleft() |
|
|
self.requests.append(time.time()) |
|
|
return True |
|
|
|
|
|
def get_stats(self) -> dict: |
|
|
""" |
|
|
π GET RATE LIMITER STATISTICS |
|
|
""" |
|
|
with self.lock: |
|
|
current_time = time.time() |
|
|
|
|
|
|
|
|
while self.requests and current_time - self.requests[0] > self.window_seconds: |
|
|
self.requests.popleft() |
|
|
|
|
|
return { |
|
|
'current_requests': len(self.requests), |
|
|
'max_requests': self.max_requests, |
|
|
'window_seconds': self.window_seconds, |
|
|
'remaining': self.max_requests - len(self.requests) |
|
|
} |
|
|
|
|
|
def reset(self) -> None: |
|
|
""" |
|
|
π RESET RATE LIMITER |
|
|
""" |
|
|
with self.lock: |
|
|
self.requests.clear() |
|
|
logger.info("π Rate limiter reset") |
|
|
|