Skip to content

scheduler in-flight attempt 生命周期收成单一出口状态机(技术债) #1016

Description

@dashhuang

背景

PR #944 给 scheduler 引擎加了「in-flight attempt」这层记账:槽位占用、卡死判定、强制收口、迟到 settle 防护都挂在它上面(packages/maker-scheduler/src/engine/scheduler.tsInflightAttempt / 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 行、排期、通知认领、pendingReplansabandonedRuns、所有权标记)在那里集中执行
  • 非法转移直接抛错而不是静默容忍 —— 现在多数漏项的表现都是「静默少做一件事」
  • 用状态机把这些不变量写成断言:slotsInUse 永不超过 maxConcurrentRuns;进入 done 时 run 行必为终态或已登记僵尸清扫兜底;pendingReplans 里不存在已 done 且排期已恢复的条目

为什么不在 #944 里做

#944 已经 28 commit、合过一次 main,且缺陷发现率已收敛到每轮 1 条。在其中再做一次结构重构会让评审面重新扩大,收益(预防未来同类漏项)不如放到独立 PR 里一次立住不变量。真因(纯等待占死执行槽)与三层自愈已在 #944 内修好并有测试覆盖,此项属纯技术债,无用户可见行为变化。

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    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