Initial curated ITWorx_MCP_Hub source from 368fba691ea8d23403b4d56b53b5ec670d7c5020
This commit is contained in:
commit
cb5be898ea
663 files changed
+82674
No files matched your search
@@ -0,0 +1,45 @@
|
||||
"""In-process bounded sliding-window rate limiter with isolated compound keys."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import time
|
||||
from collections import defaultdict, deque
|
||||
from threading import Lock
|
||||
from uuid import UUID
|
||||
|
||||
|
||||
class RateLimitExceeded(RuntimeError):
|
||||
code = "RATE_LIMITED"
|
||||
|
||||
|
||||
class SlidingWindowRateLimiter:
|
||||
def __init__(
|
||||
self, *, limit: int = 60, window_seconds: float = 60.0, clock: object = time.monotonic
|
||||
) -> None:
|
||||
if limit < 1 or window_seconds <= 0:
|
||||
raise ValueError("rate limit must be positive")
|
||||
self.limit = limit
|
||||
self.window_seconds = window_seconds
|
||||
self.clock = clock
|
||||
self._entries: dict[tuple[str, str, str], deque[float]] = defaultdict(deque)
|
||||
self._lock = Lock()
|
||||
|
||||
def allow(self, *, client_id: UUID | str, project: str, tool: str) -> bool:
|
||||
key = (str(client_id), project, tool)
|
||||
now = self.clock() # type: ignore[operator]
|
||||
with self._lock:
|
||||
bucket = self._entries[key]
|
||||
while bucket and bucket[0] <= now - self.window_seconds:
|
||||
bucket.popleft()
|
||||
if len(bucket) >= self.limit:
|
||||
return False
|
||||
bucket.append(now)
|
||||
return True
|
||||
|
||||
def check(self, *, client_id: UUID | str, project: str, tool: str) -> None:
|
||||
if not self.allow(client_id=client_id, project=project, tool=tool):
|
||||
raise RateLimitExceeded("invocation rate limit exceeded")
|
||||
|
||||
def clear(self) -> None:
|
||||
with self._lock:
|
||||
self._entries.clear()
|
||||
Reference in new issue
Block a user