rate_limiter.py 1.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960
  1. import time
  2. from collections import defaultdict
  3. from threading import Lock
  4. class SimpleRateLimiter:
  5. """
  6. Simple in-memory rate limiter using sliding window.
  7. For production, consider using Redis-based rate limiting.
  8. """
  9. def __init__(self, max_requests=60, window_seconds=60):
  10. self.max_requests = int(max_requests)
  11. self.window_seconds = int(window_seconds)
  12. self._requests = defaultdict(list)
  13. self._lock = Lock()
  14. def is_allowed(self, key):
  15. """
  16. Check if the key is allowed to make a request.
  17. Args:
  18. key: Identifier (IP address, session_id, etc.)
  19. Returns:
  20. bool: True if allowed, False if rate limit exceeded
  21. """
  22. now = time.time()
  23. window_start = now - self.window_seconds
  24. with self._lock:
  25. # Clean old requests
  26. requests = self._requests[key]
  27. self._requests[key] = [ts for ts in requests if ts > window_start]
  28. # Check limit
  29. if len(self._requests[key]) >= self.max_requests:
  30. return False
  31. # Record this request
  32. self._requests[key].append(now)
  33. return True
  34. def cleanup(self, max_age_seconds=3600):
  35. """
  36. Remove old entries to prevent memory leak.
  37. Call this periodically in a background thread.
  38. """
  39. now = time.time()
  40. cutoff = now - max_age_seconds
  41. with self._lock:
  42. keys_to_delete = []
  43. for key, requests in self._requests.items():
  44. if not requests or requests[-1] < cutoff:
  45. keys_to_delete.append(key)
  46. for key in keys_to_delete:
  47. del self._requests[key]