Skip to content

DbJobAdapter 把「handler 没抛错」记成 sys_job_run.status='success' —— 内部自行降级的 job(如 wait 唤醒打空)在作业审计面上仍显示成功 #5548

Description

@os-zhuang

发现于 #5529 的实测过程(PR 见该 issue),与该修复相邻但不同面 —— 这是 job 域的审计面问题,不是 automation 域的。

观察(实测,非推断)

DbJobAdapter.wrap()(packages/services/service-job/src/db-job-adapter.ts L161-176)只按 handler 是否抛错判定运行结果:

try { await handler(ctx); finishRun(runId, 'success'); bumpJob(name, 'success'); }
catch { finishRun(runId, 'failed', msg); bumpJob(name, 'failed', msg); throw err; }

对于「内部自行处理失败、不抛错」的 handler,这条路径把它记成成功。实测(临时 harness,已删除):一个 once job 的 handler 正常返回但什么也没完成,sys_job 行是

{"name":"flow-wait:run1:pause","active":true,"schedule_expression":"2026-08-05T16:45:20.837Z","last_status":"success","run_count":1}

sys_job_run 只有一行 status: 'success'

为什么可能值得看

sys_job / sys_job_run 是机器可读面(Studio 的作业界面读它)。一种读法是 sys_job_run.status 本就只表示「handler 跑完没抛异常」,那它没说谎;另一种读法是它作为作业审计面在这里给不出「这一枪没干成事」的信号。两种读法都成立,所以这里只做记录、不预判严重度。

需要说明的是 #5529 之后这个洞不是无声的:那条路径已经有一条 error 日志,并且 job 被刻意保留 active(#5529 就是这么修的),所以「run 卡住」本身是可见的 —— 缺的只是作业审计行里的那一格。因此按观察类归档(finding,不进 pm:queue),严重度交分诊判定。

可能的方向(未决,不在本 issue 范围内实施)

佐证


2026-08-08 裁决落地(services 座位 PM,会话 session_01USNUyHEr7uaU6MoEWXitei):维护者批复三轴分析,采纳 B-minimal(可加性第三态;A 与 #5529 裁决冲突被否,C 把审计静默合法化被否)。按 shared-contract 规则 contract-first 拆分:spec 半边见 #6617(JobHandler 可选 degraded-outcome 回报通道,API 形状归 spec 座位);本单收窄为 services 半边(DbJobAdapter 把回报映射为区别于 successsys_job_run.status + bumpJob 同步 + #5529 场景回归测试)。

2026-08-09 二次阻塞(round 18 dev 实测,详见 #7072 与本线程 13:4xZ 评论):#6617 只交付了生产者侧(JobRunOutcome + JobHandler 返回值);sys_job_run.status消费侧词表被三处 schema 钉死(spec 的 JobExecutionStatus enum + platform-objects 的两个 Field.select),全不在 services 车道 —— 适配器改动编译不过(TS2345),绕过类型则被 record-validatorinvalid_option 拒写且被 finishRun 的 try/catch 吞掉。词表拓宽 = #7072;它落地后本单退化为一次小的适配器接线。

Blocked-by: #7072

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions