ProjectArchitecture Decision Records
006: Cross-Proxy Failover Orchestration
Architecture decision record.
Status
Accepted
Context
Inner RetryExecutor retries against a single selected proxy. Users also need optional
outer rotation to a different proxy when inner retries are exhausted, without breaking
existing single-proxy behavior.
Decision
- Introduce
proxywhirl/orchestration.pywithFailoverPolicyandRequestOrchestration. FailoverPolicy.enabled=Falseby default preserves legacy inner-retry-only semantics.- When enabled, an outer loop selects the next proxy via
RetryExecutor.select_retry_proxy(or strategy fallback), excluding failed proxy IDs throughSelectionContext. - Sync and async rotators delegate
_make_requesttoRequestOrchestration; the FastAPI/api/requestendpoint callsrotator._make_requestviaasyncio.to_thread. - Proxies without circuit breaker entries remain selectable (
_should_use_circuit_breaker). - Rate limits are non-failover signals:
RateLimitExceededErrorpropagates without outer rotation; only connection-level exhaustion triggers proxy failover. - Queued sync requests pin the enqueued proxy via
pinned_proxyso dequeue does not re-select a different proxy when failover is enabled. - Default outer round cap uses the count of eligible proxies at request start (non-expired,
circuit-available), not raw pool size, unless
max_proxy_attemptsis set explicitly. - Async parity:
AsyncProxyWhirlacceptsproxy_rotation_callbacklike syncProxyWhirl.
Consequences
- Positive: Explicit opt-in failover; API and library share one code path.
- Negative: Additional orchestration layer; tests must cover enabled/disabled modes.
References
proxywhirl/orchestration.pyproxywhirl/rotator/sync.py,proxywhirl/rotator/async_.pyproxywhirl/api/routes/proxies.pytests/integration/test_true_failover.py,tests/integration/test_failover_legacy.pytests/unit/test_request_queue.py(test_queued_request_pins_proxy_with_failover_enabled)tests/unit/test_api_core_additional.py(test_request_endpoint_error_status_matrix)/api/requestmapsProxyAuthenticationErrorto HTTP 502 (aligned with global API error handler)