fix: let assembly defects pierce the gate wrappers
Independent verification caught that the split shipped in the previous commit did not actually hold on the only path production uses. The gate wrappers re-raise GovernanceBackendError but nothing else, so SourceNotConfiguredError fell into the following `except Exception` and came back out as a governance_backend_down failure with retry_after_s=5.0. A misconfigured source name would still retry forever and never surface. The existing tests missed it because both of them call the private _cfg() directly, one layer below the wrapper the governance loops actually go through. The regression test goes through QuotaGate. telemetry.py has to widen its terminal catch in the same commit: once the wrapper stops relabeling the error, it is no longer a GovernanceBackendError, and it is raised before any attempt exists, so the path would have recorded no telemetry at all. Also corrects the leak path count from three to five. QuotaGate.stats and BreakerGate.retry_after_s are not wrapped by _record_quietly either.
This commit is contained in:
@@ -18,6 +18,7 @@ from polygateway.errors import (
|
||||
SourceNotConfiguredError,
|
||||
TransientError,
|
||||
)
|
||||
from polygateway.middleware.ratelimit import QuotaGate
|
||||
from polygateway.middleware.retry import RetryMW, backoff_delay
|
||||
from polygateway.sources import RoundRobinSelector, SourceCooldownMemo
|
||||
from polygateway.types import (
|
||||
@@ -280,6 +281,24 @@ class TestUnknownSourceIsAssemblyDefect:
|
||||
# 关键: 若归入 scope 级家族,配置写错的任务会永远延期重投、永不进死信
|
||||
assert not isinstance(ei.value, GatewayUnavailableError)
|
||||
|
||||
@pytest.mark.parametrize("method", ["try_acquire", "stats"])
|
||||
async def test_survives_the_quota_gate_wrapper(self, method):
|
||||
"""必须穿透 QuotaGate,否则整个拆分在生产路径上等于没做。
|
||||
|
||||
上面两条(以及 redis 版)打的都是私有 `_cfg`,绕过了包装器。而治理循环
|
||||
只经 QuotaGate 访问后端,包装器的 `except Exception` 会把装配缺陷重新
|
||||
包成 `GovernanceBackendError`——下游又拿到可重投异常,永远重投不告警。
|
||||
"""
|
||||
src = make_source("s1")
|
||||
# 限流后端的源名单与治理循环拿到的源对不上 = 装配缺陷
|
||||
limiter = InMemoryLimiter(
|
||||
scope="llm", sources={"other": src}, global_limits=_NO_GLOBAL
|
||||
)
|
||||
gate = QuotaGate(limiter, scope="llm")
|
||||
with pytest.raises(SourceNotConfiguredError) as ei:
|
||||
await getattr(gate, method)(src)
|
||||
assert not isinstance(ei.value, GatewayUnavailableError)
|
||||
|
||||
|
||||
class TestGateFailuresReachCallersAsScopeLevel:
|
||||
"""三条闸门泄漏路径必须以 scope 级不可用的形态到达调用方(issue #7)。
|
||||
|
||||
Reference in New Issue
Block a user