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() returns False immediately (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:

  1. First thread sets _half_open_pending = True and makes the test request

  2. Other threads see the flag and skip (fail-fast, try another proxy)

  3. 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…

failure_threshold

5

Using unreliable free proxies (avoid over-ejection)

Using premium proxies (eject fast on failure)

window_duration

60s

Transient failures are common

Failures are persistent

timeout_duration

30s

Recovery is slow (residential proxies)

Recovery is fast (datacenter proxies)

Further Reading