Circuit Breakers¶
ProxyWhirl uses the circuit breaker pattern to detect, isolate, and recover from proxy failures automatically. This page explains the theory – for configuration, see Retry & Failover Guide.
The Problem¶
Without intelligent failure handling, a proxy rotator would:
Repeatedly retry failed proxies, wasting time on known-bad proxies
Cascade failures as one slow proxy blocks all requests
Never detect recovery of temporarily failed proxies
Simple retry logic isn’t enough – it doesn’t prevent the same bad proxy from being selected across different requests.
Three-State Machine¶
Each proxy has its own CircuitBreaker with three states:
stateDiagram-v2
[*] --> CLOSED
CLOSED --> OPEN: failures >= threshold\n(within rolling window)
OPEN --> HALF_OPEN: timeout elapsed
HALF_OPEN --> CLOSED: test request succeeds
HALF_OPEN --> OPEN: test request fails\n(reset timeout)
- CLOSED (normal operation):
Proxy is available for all requests. Failures are tracked in a rolling time window (default: 60 seconds). When failures exceed the threshold (default: 5), the circuit opens.
- OPEN (proxy excluded):
Proxy is excluded from rotation –
should_attempt_request()returnsFalseimmediately (fail-fast). No requests are attempted. After a configurable timeout (default: 30 seconds), the circuit transitions to half-open.- HALF_OPEN (testing recovery):
A single test request is allowed through. If it succeeds, the circuit closes (proxy recovered). If it fails, the circuit goes back to open with the timeout reset. Only one test request is permitted at a time to prevent a thundering herd.
Why Not Just Blacklist?¶
Approach |
Detects Failure |
Automatic Recovery |
No Manual Intervention |
|---|---|---|---|
Manual blacklisting |
No |
No |
No |
Simple retry |
Per-request |
No |
Yes |
Circuit breaker |
Yes |
Yes |
Yes |
The circuit breaker’s key advantage is automatic recovery testing. Proxies that go down temporarily (network blip, rate limit, restart) come back into rotation automatically once the half-open test succeeds.
Rolling Time Window¶
Failures are tracked in a collections.deque of timestamps. Only failures within the window (default: 60 seconds) count toward the threshold. This prevents permanent exclusion from transient failure bursts – once the window passes, old failures are pruned.
Time: 0s 10s 20s 30s 40s 50s 60s 70s
Fails: X X X X X
Window: [============================]
Count: 3 (below threshold of 5)
Half-Open Gating¶
When the timeout expires, multiple threads might try to test the proxy simultaneously. ProxyWhirl uses a _half_open_pending flag to ensure only one test request proceeds:
First thread sets
_half_open_pending = Trueand makes the test requestOther threads see the flag and skip (fail-fast, try another proxy)
Test result determines the next state transition
This prevents a thundering herd where N threads all test a potentially-dead proxy at the same time.
State Persistence¶
Circuit breaker state can optionally persist to SQLite (persist_state=True). This prevents retry storms on restart – without persistence, all circuit breakers start in CLOSED state after an application restart, causing a flood of requests to previously-failed proxies.
Persistence is asynchronous (fire-and-forget) to avoid blocking the request path. State lost during a hard crash is acceptable – it only means the circuit might need to re-learn failures.
Configuration Tuning¶
Parameter |
Default |
Increase When… |
Decrease When… |
|---|---|---|---|
|
5 |
Using unreliable free proxies (avoid over-ejection) |
Using premium proxies (eject fast on failure) |
|
60s |
Transient failures are common |
Failures are persistent |
|
30s |
Recovery is slow (residential proxies) |
Recovery is fast (datacenter proxies) |
Further Reading¶
Retry & Failover Guide – configuration guide with code examples
Python API –
CircuitBreaker,AsyncCircuitBreakerAPIADR-002: Circuit Breaker Pattern – original decision record