Files
Random-image-api/backend/app/core/failover.py
T
2026-02-22 18:58:55 +08:00

77 lines
1.9 KiB
Python

from __future__ import annotations
from enum import Enum
import httpx
class OutboundErrorKind(str, Enum):
PROXY_CONNECT = "proxy_connect"
PROXY_AUTH = "proxy_auth"
PIXIV_RATE_LIMIT = "pixiv_rate_limit"
def classify_httpx_exception(exc: BaseException) -> OutboundErrorKind | None:
if isinstance(exc, httpx.ProxyError):
msg = str(exc).lower()
if "407" in msg or "proxy authentication" in msg:
return OutboundErrorKind.PROXY_AUTH
return OutboundErrorKind.PROXY_CONNECT
return None
def classify_pixiv_rate_limit(*, status_code: int, body_text: str | None) -> OutboundErrorKind | None:
if int(status_code) != 403:
return None
text = (body_text or "").strip().lower()
if "rate limit" in text:
return OutboundErrorKind.PIXIV_RATE_LIMIT
return None
def should_override_proxy(kind: OutboundErrorKind) -> bool:
return kind in {OutboundErrorKind.PROXY_CONNECT, OutboundErrorKind.PROXY_AUTH}
def should_backoff_token(kind: OutboundErrorKind) -> bool:
return kind == OutboundErrorKind.PIXIV_RATE_LIMIT
def proxy_override_ttl_seconds(*, attempt: int) -> int:
attempt_i = int(attempt)
if attempt_i <= 0:
return 0
schedule = {
1: 20 * 60,
2: 60 * 60,
3: 6 * 60 * 60,
}
if attempt_i in schedule:
return schedule[attempt_i]
base = 6 * 60 * 60
seconds = base * (2 ** (attempt_i - 3))
return min(seconds, 24 * 60 * 60)
def pixiv_rate_limit_backoff_seconds(*, attempt: int) -> int:
attempt_i = int(attempt)
if attempt_i <= 0:
return 0
schedule = {
1: 60,
2: 5 * 60,
3: 15 * 60,
4: 60 * 60,
5: 6 * 60 * 60,
}
if attempt_i in schedule:
return schedule[attempt_i]
base = 6 * 60 * 60
seconds = base * (2 ** (attempt_i - 5))
return min(seconds, 24 * 60 * 60)