Spaces:
Sleeping
Sleeping
Claude
Add a circuit breaker to the multi-backend search fallback (item from review)
ba2278a unverified Download circuit_breaker.py from OrganizedProgrammers/SERPent: direct link, hf CLI and curl.
- Browser
- Download file 2.63 kB
-
https://huggingface.co/spaces/OrganizedProgrammers/SERPent/resolve/main/circuit_breaker.py
- Command line
-
hf download hf://spaces/OrganizedProgrammers/SERPent/circuit_breaker.py
-
curl -L -o circuit_breaker.py https://huggingface.co/spaces/OrganizedProgrammers/SERPent/resolve/main/circuit_breaker.py
2.63 kB
| """A minimal per-key circuit breaker for outbound scraping backends. | |
| When a backend (e.g. one search engine) is rate-limiting or blocking us, | |
| retrying it on every single query just makes that worse - it's the | |
| opposite of what you want when the goal is to stop looking suspicious to | |
| that backend. This tracks consecutive failures per key and, once a | |
| threshold is hit, short-circuits further calls to that key for a cooldown | |
| period instead of attempting (and paying the timeout cost of) a call that | |
| is very likely to fail anyway. After the cooldown it lets exactly one | |
| trial call through (half-open); success closes the circuit again, failure | |
| re-opens it for another full cooldown. | |
| Not thread-safe (no locking) - fine given every caller in this codebase | |
| only ever runs on a single asyncio event loop. | |
| """ | |
| import time | |
| from dataclasses import dataclass | |
| class CircuitOpenError(Exception): | |
| """Raised by `before_call` instead of letting a call through while a | |
| key's circuit is open.""" | |
| def __init__(self, key: str, retry_after: float): | |
| self.key = key | |
| self.retry_after = retry_after | |
| super().__init__(f"Circuit open for '{key}'; retry after {retry_after:.0f}s") | |
| class _BreakerState: | |
| failure_count: int = 0 | |
| opened_at: float = 0.0 | |
| class CircuitBreaker: | |
| def __init__(self, failure_threshold: int = 3, cooldown_seconds: float = 60.0): | |
| self.failure_threshold = failure_threshold | |
| self.cooldown_seconds = cooldown_seconds | |
| self._states: dict[str, _BreakerState] = {} | |
| def _state(self, key: str) -> _BreakerState: | |
| return self._states.setdefault(key, _BreakerState()) | |
| def before_call(self, key: str) -> None: | |
| """Raise CircuitOpenError if `key` is currently open and its | |
| cooldown hasn't elapsed. Call this before attempting the call. | |
| """ | |
| state = self._state(key) | |
| if state.failure_count < self.failure_threshold: | |
| return | |
| remaining = self.cooldown_seconds - (time.monotonic() - state.opened_at) | |
| if remaining > 0: | |
| raise CircuitOpenError(key, remaining) | |
| # Cooldown elapsed: half-open - let this one call through. A | |
| # renewed failure (record_failure) re-opens it for another cooldown; | |
| # a success (record_success) clears it entirely. | |
| def record_success(self, key: str) -> None: | |
| self._states.pop(key, None) | |
| def record_failure(self, key: str) -> None: | |
| state = self._state(key) | |
| state.failure_count += 1 | |
| if state.failure_count >= self.failure_threshold: | |
| state.opened_at = time.monotonic() | |