背景
PR #944 给 scheduler 引擎加了「in-flight attempt」这层记账:槽位占用、卡死判定、强制收口、迟到 settle 防护都挂在它上面(packages/maker-scheduler/src/engine/scheduler.ts 的 InflightAttempt / beginInflightAttempt / finishInflightAttempt / forceReleaseStalledRun)。
该 PR 的 review 过程里,同一个形状的缺陷反复出现了四次:某条出口分支没有走它该走的收口,于是 attempt 的所有权或终态在某个路径上悄悄丢掉。
- runner 迟到 settle 把强制收口中的 attempt 删掉 → 用
forceReleaseOwnsCleanup 显式表达所有权
- 强制收口自己的落库失败吞掉排期恢复 → 外层 catch 保排期
- 补发通知依赖终态落库成功 →
try/finally 解耦
- 补排重试凭据只在 try 尾部摘除,每个早退分支都绕过 → 拆成
applyStalledClaimReplan + 外层单一出口
每一条单独看都是小修,但它们是同一件事:attempt 的生命周期有多个隐式出口,每个出口都要手工记得做全套收口动作(摘槽位 / 落终态 / 恢复排期 / 认领通知 / 摘重试凭据 / 让出所有权)。靠注释和逐点补条件维持,已经证明会漏。
(同一 PR 里 coordinator 侧的 active-turn recovery 是同类问题,已在 PR 内收口成单一出口 setter setActiveTurnRecovery —— 可作为这条的参照做法。)
建议
把 attempt 生命周期显式建成状态机,单一出口:
- 状态:
loading → claiming → persisting → running ⇄ queued / cancelling → finalizing → done(现有 ScheduleRunPhase 已经是这套状态的雏形)
- 每个终态转移只有一个出口函数,收口动作(槽位、run 行、排期、通知认领、
pendingReplans、abandonedRuns、所有权标记)在那里集中执行
- 非法转移直接抛错而不是静默容忍 —— 现在多数漏项的表现都是「静默少做一件事」
- 用状态机把这些不变量写成断言:
slotsInUse 永不超过 maxConcurrentRuns;进入 done 时 run 行必为终态或已登记僵尸清扫兜底;pendingReplans 里不存在已 done 且排期已恢复的条目
为什么不在 #944 里做
#944 已经 28 commit、合过一次 main,且缺陷发现率已收敛到每轮 1 条。在其中再做一次结构重构会让评审面重新扩大,收益(预防未来同类漏项)不如放到独立 PR 里一次立住不变量。真因(纯等待占死执行槽)与三层自愈已在 #944 内修好并有测试覆盖,此项属纯技术债,无用户可见行为变化。
背景
PR #944 给 scheduler 引擎加了「in-flight attempt」这层记账:槽位占用、卡死判定、强制收口、迟到 settle 防护都挂在它上面(
packages/maker-scheduler/src/engine/scheduler.ts的InflightAttempt/beginInflightAttempt/finishInflightAttempt/forceReleaseStalledRun)。该 PR 的 review 过程里,同一个形状的缺陷反复出现了四次:某条出口分支没有走它该走的收口,于是 attempt 的所有权或终态在某个路径上悄悄丢掉。
forceReleaseOwnsCleanup显式表达所有权try/finally解耦applyStalledClaimReplan+ 外层单一出口每一条单独看都是小修,但它们是同一件事:attempt 的生命周期有多个隐式出口,每个出口都要手工记得做全套收口动作(摘槽位 / 落终态 / 恢复排期 / 认领通知 / 摘重试凭据 / 让出所有权)。靠注释和逐点补条件维持,已经证明会漏。
(同一 PR 里 coordinator 侧的
active-turn recovery是同类问题,已在 PR 内收口成单一出口 settersetActiveTurnRecovery—— 可作为这条的参照做法。)建议
把 attempt 生命周期显式建成状态机,单一出口:
loading → claiming → persisting → running ⇄ queued / cancelling → finalizing → done(现有ScheduleRunPhase已经是这套状态的雏形)pendingReplans、abandonedRuns、所有权标记)在那里集中执行slotsInUse永不超过maxConcurrentRuns;进入done时 run 行必为终态或已登记僵尸清扫兜底;pendingReplans里不存在已done且排期已恢复的条目为什么不在 #944 里做
#944 已经 28 commit、合过一次
main,且缺陷发现率已收敛到每轮 1 条。在其中再做一次结构重构会让评审面重新扩大,收益(预防未来同类漏项)不如放到独立 PR 里一次立住不变量。真因(纯等待占死执行槽)与三层自愈已在 #944 内修好并有测试覆盖,此项属纯技术债,无用户可见行为变化。