Skip to content

[approvals] 存量僵尸请求无人认领:已终态但 run 悬空的请求落在 releaseDeadRunRequests 的盲区里 #4469

Description

@os-zhuang

#4420 的后续。那个 PR(#4460)修的是不再产生新的僵尸;已经卡住的请求仍然没有任何机制发现或释放。

问题

#4420 的故障形态是:请求行已翻成 approved(或 rejected),但它的 flow_run_id 指向的 run 已经不存在——决策落库了,流程永远不动。凡是在装有 17.0.0-rc.1 且撞上装配漏洞的部署上、于重启前后跨越的审批,都可能留下这种行。

现有的死信清扫 ApprovalService.releaseDeadRunRequests()(packages/plugins/plugin-approvals/src/approval-service.ts)扫不到它们,原因很直接:

  • 它只扫 status: 'pending' 的请求;
  • 而僵尸的状态恰恰已经不是 pending——它是被那次"成功"的决策翻成终态的。

也就是说,把请求推入僵尸态的那一步,同时把它推出了唯一一个会去检查它的清扫器的视野。这也是这类故障能长期无声的原因之一:没有任何一层在看它。

另外 releaseDeadRunRequests 判定"死"的依据是 getRun(runId) 返回终态状态,而 getRun 读的是执行日志环形缓冲——重启后对一个活着的挂起 run 同样返回 null。它把 null 当作"还活着"来处理(保守,正确),但这也意味着它本来就没有能力回答"这个 run 是不是真的没了"。

现在有了可用的原语

#4460 引入了 AutomationEngine.hasSuspendedRun(runId):直接问挂起存储,而不是问执行日志;库读不到时抛错而不是答 false(避免把一次抖动误判成死 run)。它已经通过可选的 ApprovalResumeSurface.hasSuspendedRun 暴露给审批侧,可以直接复用。

期望

一个管理端清扫/巡检,能找出终态请求中 run 已不可恢复的行:

  • 扫描条件大致是:flow_run_id 非空 且 请求已终态 且 hasSuspendedRun(runId) === false 且 该 run 也没有终态 run 记录(sys_automation_runrun_ 前缀的历史行)——即"既不在挂起,也没跑完",这才是真悬空;
  • hasSuspendedRun 抛错的情况必须跳过而非判死(存储不可读 ≠ run 不存在);
  • 输出面向人工:哪些记录卡在哪一级、镜像字段停在什么状态。不建议自动改写状态——决策是真实发生过的,自动回滚会让审计与事实不符;先做成可见的报表/告警,由管理员决定是重跑后续动作还是重新发起。

参考

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions