Files
PolyGateway/research-wiki/designs/governance-backend-error.md
T
iomgaa 3a104fcce4 docs: record human approval of the issue #7 design
All three open decisions were settled as proposed: a dedicated
SourceNotConfiguredError so a misconfigured source reaches the dead
letter queue instead of retrying forever, a 5 second retry_after_s so a
backlog does not stampede a backend that is already down, and public
export so callers can alarm on assembly defects specifically.
2026-08-06 03:56:41 -04:00

4.4 KiB

type, node_id, title, date
type node_id title date
design design:governance-backend-error 治理后端故障归位为 scope 级不可用(Issue #7) 2026-08-06

治理后端故障归位为 scope 级不可用(Issue #7)

全文见 2026-08-06-governance-backend-error-design.md。来源: Gitea Issue #7(下游 CHSAnalyzer3 按异常类型分流失败)。状态: 已批准(2026-08-06,人类逐条拍板 Q1/Q2/Q3),待 writing-plans

问题: 限流/熔断状态后端故障时库 fail-closed,一个请求都发不出去——语义上就是 scope 级不可用,但 GovernanceBackendErrorPolyGatewayError直接子类,只写 except GatewayUnavailableError 的调用方接不住,于是 Redis 抖一下,积压任务一批批消耗业务失败预算进死信,而那是运维重启就好的故障。

选定方案

决策 选定 关键理由
A 类型树 GovernanceBackendError 改继承 GatewayUnavailableError,SCOPE_REASONSgovernance_backend_down,reason 恒为该值 加父类是扩大不是破坏(既有 except GovernanceBackendError 照旧命中);库内仅 telemetry.py:210 一处捕父类且已并列写两者,零回归
B retry_after_s 模块常量 GOVERNANCE_BACKEND_RETRY_AFTER_S = 5.0,非环境配置项 后端恢复时间物理上不可知(不同于熔断冷却有确定到期时刻);取 0 会让积压任务零延迟批量重投,把一次故障放大成风暴
C scope 来源 后端层用 self._scope;QuotaGate/BreakerGate 构造函数注入,三处装配(retry.py/ocr.py/embedding.py)各传一行 两个包装器是后端异常的唯一入口,注入点收敛;三处装配本就持有 self._scope
D 未知源拆分 _cfg() 的 2 处改抛新增的 SourceNotConfiguredError,有意不放在 GatewayUnavailableError 之下 那是装配缺陷不是后端故障;随整类归入"可重投"会让配置写错的任务永远重投、永不进死信——本 issue 要修的 bug 的镜像
E message 保全 super().__init__() 后覆写 self.args = (message,) 父类会把 message 覆盖为 f"{scope} 网关暂时不可用: {reason}",而 22 处构造点的诊断串是排障主线索。机制已实跑验证

被否决的备选

备选 否决原因
B(issue 原议): 只补文档,类型树不动 正确性依赖每个下游都读到那句话;本 issue 本身就是"文档读不出来"引发的,同一失效模式不能用同一种药治
C: 在 RetryMW 边界包成 AllSourcesExhausted 比选定方案更具破坏性——下游现有 except GovernanceBackendError 直接失效
D: 后端层不再构造该异常,原始异常穿透由包装器统一翻译 初评时倾向。redis/limiter.py:133,151RedisPermit.release/settle 依赖 except GovernanceBackendError 实现释放侧降级,穿透后接不住会破坏该既有行为;改 except Exception 则违反 P5
retry_after_s 复用 BackpressureConfig.poll_interval_s 该值只有三个装配点持有,为此给后端加构造参数等于让状态存储层持有重投策略,违反 P7
新增配置项 PGW_GOVERNANCE_BACKEND_RETRY_AFTER_S YAGNI;无下游表达过需要,真需要时下游可忽略该字段用自有退避

对 issue 前提的四处修正

泄漏路径是三条不是两条(retry.py:216progress_age_s() 同样在 catch 之外);构造点 22 处;其中 2 处语义完全不同(未知源);retry_after_s=0 语义通但工程不通。

根因记录: ARCHITECTURE.md §6.1 错误分类表里 GovernanceBackendError 一次都没出现——它是 M2 引入分布式后端时新增的,当时未回补架构表,于是它在"调用方视角的分类学"中从来没有位置,README 的遗漏是这个遗漏的下游后果。

独立审查修正(2026-08-06, Codex)

4 条意见逐条核验: 两条"架构文档未同步"实质成立但性质是执行顺序 → 新增 §8.1 钉死"ARCHITECTURE.md §6.1 修订先于/同批于实现";"新错误类违反四分类铁律"部分成立——铁律论域被误读(GatewayUnavailableError 族本就合法处在四分类之外),但原表述确会引起疑虑 → §7 补写三论域划分论证,并把"复用 RequestRejectedError"增列为待人类权衡的备选;两条建议性意见(常量非配置项的说明、决策编号 DQ 防与架构 D1–D14 混淆)已采纳。

相关: m2-distributedm1-core-designm25-resilience