feat(adapters): 实现 CircuitBreaker 内存级熔断器
注入 now 纯确定性,force_open 支持 401/403 直接熔断。 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,70 @@
|
||||
"""进程内熔断器:按 source_name 记连续失败数与冷却截止时刻。
|
||||
|
||||
内存级单实例,now 由调用方注入以保证纯确定性可测试。达阈值后开路冷却,
|
||||
冷却到期放行试探(half-open)。force_open 支持 401/403 一次即熔断。
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
|
||||
class CircuitBreaker:
|
||||
"""按 source_name 记失败数;达阈值后开路冷却,冷却到期放行试探(half-open)。
|
||||
|
||||
Args:
|
||||
fail_threshold: 连续失败次数阈值,达到后触发开路。
|
||||
cooldown_s: 开路冷却时长(秒),冷却到期自动进入半开状态。
|
||||
"""
|
||||
|
||||
def __init__(self, fail_threshold: int, cooldown_s: float) -> None:
|
||||
self._fail_threshold = fail_threshold
|
||||
self._cooldown_s = cooldown_s
|
||||
self._fails: dict[str, int] = {}
|
||||
self._open_until: dict[str, float] = {}
|
||||
|
||||
def is_open(self, source_name: str, now: float) -> bool:
|
||||
"""判断指定源是否处于开路状态。
|
||||
|
||||
冷却截止时刻之前为开路;到期返回 False(放行一个试探,即半开)。
|
||||
|
||||
Args:
|
||||
source_name: 被熔断的源标识。
|
||||
now: 当前时刻(秒级时间戳),由调用方注入。
|
||||
|
||||
Returns:
|
||||
True 表示开路(拒绝请求),False 表示关闭或半开(放行)。
|
||||
"""
|
||||
until = self._open_until.get(source_name)
|
||||
return until is not None and now < until
|
||||
|
||||
def record_failure(self, source_name: str, now: float) -> None:
|
||||
"""记录一次失败;累计达阈值则开路至 now + cooldown。
|
||||
|
||||
Args:
|
||||
source_name: 失败的源标识。
|
||||
now: 当前时刻(秒级时间戳)。
|
||||
"""
|
||||
count = self._fails.get(source_name, 0) + 1
|
||||
self._fails[source_name] = count
|
||||
if count >= self._fail_threshold:
|
||||
self._open_until[source_name] = now + self._cooldown_s
|
||||
|
||||
def force_open(self, source_name: str, now: float) -> None:
|
||||
"""强制开路(用于 401/403 等不可恢复错误),一次即熔断。
|
||||
|
||||
将失败计数预设为阈值,使半开探针仅需一次失败即可重新熔断。
|
||||
|
||||
Args:
|
||||
source_name: 需要强制熔断的源标识。
|
||||
now: 当前时刻(秒级时间戳)。
|
||||
"""
|
||||
self._fails[source_name] = self._fail_threshold
|
||||
self._open_until[source_name] = now + self._cooldown_s
|
||||
|
||||
def record_success(self, source_name: str) -> None:
|
||||
"""记录一次成功;清零失败计数与开路状态(关闭熔断器)。
|
||||
|
||||
Args:
|
||||
source_name: 成功的源标识。
|
||||
"""
|
||||
self._fails.pop(source_name, None)
|
||||
self._open_until.pop(source_name, None)
|
||||
Reference in New Issue
Block a user