feat: add the {SCOPE}__CIRCUIT_OPEN admission policy key
Limiter rejections have always chosen between waiting and failing fast; breaker rejections had no such choice. The new key is the missing cell of that matrix, shaped exactly like QUOTA_FULL so there is nothing new to learn. It defaults to fail_fast: flipping the default would move every existing deployment's worst-case wall clock from milliseconds to the stall window, which is the wrong direction to impose on anyone. Single-source scopes are the ones that want wait, and they now have a way to say so. The two keys stay separate despite sharing a domain, because a full quota is "queue for your share" (your turn always comes) while an open circuit is "wait for the source to recover" (it might not). Policy validation collapses into SourceAdmission, the only consumer. The three client constructors used to each carry their own copy of the quota_full check; adding a second key there would have made eight copies of the same two lines. Rejection timing and message are unchanged -- admission is built inside those constructors. This commit only wires the key through; the control flow that reads it lands next.
This commit is contained in:
@@ -50,6 +50,9 @@ _SOURCE_FIELDS: dict[str, tuple[str, str]] = {
|
||||
_RESERVED_SEGMENTS = frozenset({"GLOBAL", "RETRY", "BREAKER", "BACKPRESSURE"})
|
||||
_SELECTORS = frozenset({"round_robin", "least_inflight", "health_aware"})
|
||||
_QUOTA_FULL = frozenset({"wait", "fail_fast"})
|
||||
# 熔断全拒时的处置(issue #14);值域与 _QUOTA_FULL 相同但语义不同——配额满是
|
||||
# "排队等自己的份额"(必然轮到),熔断开路是"等源恢复"(未必恢复),故分列两键
|
||||
_CIRCUIT_OPEN = frozenset({"wait", "fail_fast"})
|
||||
# 后端合法域: env 解析与构造期校验共用一份定义,避免两处分叉
|
||||
_LIMITER_BACKENDS = frozenset({"memory", "redis"})
|
||||
_BREAKER_BACKENDS = frozenset({"memory", "redis"})
|
||||
@@ -128,6 +131,9 @@ class GatewaySettings:
|
||||
backpressure: BackpressurePolicy
|
||||
selector: str
|
||||
quota_full: str
|
||||
# 熔断全拒时是当场判死还是等冷却过去(issue #14);缺省 fail_fast 保持
|
||||
# 存量下游的控制流不变,单源 scope 应显式配 wait
|
||||
circuit_open: str
|
||||
limiter_backend: str
|
||||
breaker_backend: str
|
||||
cache_backend: str
|
||||
@@ -203,6 +209,7 @@ class GatewaySettings:
|
||||
("telemetry_backend", _TELEMETRY_BACKENDS),
|
||||
("selector", _SELECTORS),
|
||||
("quota_full", _QUOTA_FULL),
|
||||
("circuit_open", _CIRCUIT_OPEN),
|
||||
):
|
||||
value = getattr(self, field)
|
||||
if value not in allowed:
|
||||
@@ -320,6 +327,9 @@ class GatewaySettings:
|
||||
backpressure=_load_backpressure(scope_u, env),
|
||||
selector=_load_choice(env, f"{scope_u}__SELECTOR", _SELECTORS, "health_aware"),
|
||||
quota_full=_load_choice(env, f"{scope_u}__QUOTA_FULL", _QUOTA_FULL, "wait"),
|
||||
circuit_open=_load_choice(
|
||||
env, f"{scope_u}__CIRCUIT_OPEN", _CIRCUIT_OPEN, "fail_fast"
|
||||
),
|
||||
**_load_pgw(env),
|
||||
)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user