-
Notifications
You must be signed in to change notification settings - Fork 322
feat(core): add RPC rate limiting and retry handling #154
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
bbad970
39110fa
1fb080d
2ce7fe3
92bd274
021065a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,50 @@ | ||
| """Token bucket rate limiter for Solana RPC requests.""" | ||
|
|
||
| import asyncio | ||
| import time | ||
|
|
||
| from utils.logger import get_logger | ||
|
|
||
| logger = get_logger(__name__) | ||
|
|
||
|
|
||
| class TokenBucketRateLimiter: | ||
| """Token bucket rate limiter for controlling RPC request rate. | ||
|
|
||
| Implements a token bucket algorithm that replenishes tokens at a | ||
| fixed rate. Each RPC call consumes one token. When the bucket is | ||
| empty, callers wait until a token becomes available. | ||
|
|
||
| Args: | ||
| max_rps: Maximum requests per second (bucket refill rate). | ||
| burst_size: Maximum burst size (bucket capacity). Defaults to max_rps. | ||
| """ | ||
|
|
||
| def __init__(self, max_rps: float, burst_size: int | None = None) -> None: | ||
| self._max_rps = max_rps | ||
| self._burst_size = burst_size if burst_size is not None else int(max_rps) | ||
| self._tokens = float(self._burst_size) | ||
| self._last_refill = time.monotonic() | ||
| self._lock = asyncio.Lock() | ||
|
Comment on lines
+24
to
+37
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Line 28 exceeds the 88-character limit; also validate explicit Two items in
Proposed fix def __init__(self, max_rps: float, burst_size: int | None = None) -> None:
if max_rps <= 0:
- raise ValueError(f"max_rps must be positive, got {max_rps}")
+ msg = f"max_rps must be positive, got {max_rps}"
+ raise ValueError(msg)
self._max_rps = max_rps
- self._burst_size = burst_size if burst_size is not None else max(1, math.ceil(max_rps))
+ self._burst_size = (
+ burst_size if burst_size is not None
+ else max(1, math.ceil(max_rps))
+ )
+ if self._burst_size <= 0:
+ msg = f"burst_size must be positive, got {burst_size}"
+ raise ValueError(msg)
self._tokens = float(self._burst_size)
self._last_refill = time.monotonic()
self._lock = asyncio.Lock()As per coding guidelines: "Limit lines to 88 characters", "Perform comprehensive input validation for externally sourced data", and "Comply with Ruff exception handling rules (BLE, TRY)". 🧰 Tools🪛 Ruff (0.14.14)[warning] 26-26: Avoid specifying long messages outside the exception class (TRY003) 🤖 Prompt for AI Agents |
||
|
|
||
| async def acquire(self) -> None: | ||
| """Acquire a token, waiting if the bucket is empty.""" | ||
| while True: | ||
| async with self._lock: | ||
| self._refill() | ||
| if self._tokens >= 1.0: | ||
| self._tokens -= 1.0 | ||
| return | ||
| wait_time = (1.0 - self._tokens) / self._max_rps | ||
|
|
||
| await asyncio.sleep(wait_time) | ||
|
|
||
| def _refill(self) -> None: | ||
| """Refill tokens based on elapsed time since last refill.""" | ||
| now = time.monotonic() | ||
| elapsed = now - self._last_refill | ||
| self._tokens = min( | ||
| self._burst_size, | ||
| self._tokens + elapsed * self._max_rps, | ||
| ) | ||
| self._last_refill = now | ||
Uh oh!
There was an error while loading. Please reload this page.