mirror of
https://github.com/whyour/qinglong.git
synced 2026-09-20 16:07:11 +08:00
95 lines
7.3 KiB
Markdown
95 lines
7.3 KiB
Markdown
# ADR-0072:统一的本机执行完成、取消、超时与停机生命周期
|
||
|
||
- 状态:Proposed
|
||
- 日期:2026-07-20
|
||
- 关联:D-18、D-21、D-65、D-67 至 D-70、ADR-0005、ADR-0007、ADR-0066、ADR-0068 至 ADR-0071
|
||
|
||
> ADR-0087 现行增量:本文 control lifecycle 已迁入 `@qinglong/local-execution/control`;`/recovery` 可单向消费其 completion processor,其他 subpath 不得反向导入 control/recovery。
|
||
|
||
## 上下文
|
||
|
||
ADR-0068 至 ADR-0071 已分别建立启动恢复、持久化进程、原子启动协调和有界调度,但完成 Promise、启动恢复回执、取消、Attempt deadline、回执清理和应用停止仍可能由不同 lifecycle 推进。若每条路径各自实现终态映射或拥有 timer,会产生迟到成功覆盖取消、同一 Attempt 重复完成、停机后孤儿进程,以及低配路由器空闲唤醒和内存状态随任务数增长的问题。
|
||
|
||
## 决策
|
||
|
||
### 1. 新增独立 Profile-neutral 控制包
|
||
|
||
新增 `@qinglong/local-execution-control`。生产只依赖 `@qinglong/runtime-core` 与 `@qinglong/local-process`,不得导入 SQLite adapter、Profile importer、legacy、cluster、ORM 或 HTTP。依赖方向固定为:
|
||
|
||
```text
|
||
runtime-core <- local-process <- local-execution-control <- local-run-recovery
|
||
^
|
||
|
|
||
local-application
|
||
```
|
||
|
||
local-run-recovery 与在线 lifecycle 共享同一个 `LocalCompletionReceiptProcessor`;Run/Attempt 的成功、失败、取消和超时终态只能由该处理器或同包内的控制协调器提交,不再保留两套回执认证和终态事务实现。
|
||
|
||
### 2. 完成回执是唯一正常完成事实
|
||
|
||
dispatcher 不向 application stack 返回进程 completion Promise。它只在 Promise settle 后向组合根发送 Attempt ID;通知采用最多 64 个 identity 的进程内合并集合,不携带 token、exit code、命令或日志内容,也不是耐久事实。通知丢失时,周期性数据库候选与启动恢复仍能收敛。
|
||
|
||
处理器先从唯一 RunRepository 重读 Attempt/Run,再读取 exact receipt,并校验 Run ID、Attempt ID、callback sequence、token digest 和时间边界。可信回执在一个 Repository transaction 中按 Attempt Event、Run Event 顺序 CAS 终态;终态提交后才删除 receipt 并解除 journal。已终态 Attempt 的同一或下一 callback sequence 回执只做认证和清理,不得改写终态。非法回执必须先保存 quarantine intent,再移动文件。
|
||
|
||
### 3. deadline 先转为 durable cancellation intent
|
||
|
||
SQLite source 只使用已有 cancellation/deadline 索引,以 `(dueAtMs, kind, attemptId)` 稳定 keyset 返回最多 64 条候选。deadline 到期后,协调器先 CAS 写入 `cancelRequestedAtMs`、`cancelReason=timeout` 与 `run.cancel_requested`,随后才能停止进程;数据库失败不得 signal。
|
||
|
||
所有取消先尝试消费可信 completion。仍 active 时,只能把最新 runtime-owned local-process Attempt 的完整 durable handle 交给 `LocalProcessController`;禁止裸 PID signal。`claimed` 可直接终结,`starting/running` 只有 exact stop 返回 `stopped/already_exited` 或可信回执时才能终结。unknown、identity/provider 不可用和 stop timeout 保持待收敛,不能猜测退出。
|
||
|
||
### 4. application 只拥有一个周期 timer
|
||
|
||
`LocalExecutionControlLifecycle` 同时驱动完成通知、deadline/cancellation 单页扫描和低频 receipt cleanup。每个 application 只有一个 `unref` 周期 timer,没有每任务 timer、watcher、目录扫描或无界队列:
|
||
|
||
| Profile | 控制周期 | 控制页 | cleanup 周期 | cleanup 页 | drain 页预算 |
|
||
| --- | ---: | ---: | ---: | ---: | ---: |
|
||
| edge | 5 秒 | 4 | 5 分钟 | 8 | 2 |
|
||
| standalone | 1 秒 | 32 | 1 分钟 | 32 | 8 |
|
||
|
||
每次工作串行且不重叠;页、通知、停止等待和 drain 页数均有硬上限。cluster-control/worker 不装配该本机 lifecycle,其远端 Session/Lease/Attestation 继续使用独立 authority。
|
||
|
||
### 5. 停机顺序包含 execution drain
|
||
|
||
application 停止顺序固定为 admission close/drain → local execution stop/drain → injected stack stop → storage close。execution drain 只读取有界 active keyset,为尚无取消意图的 Attempt 先提交 `shutdown` cancellation,再按 exact handle 停止并终态化。已有 user/policy/timeout 意图必须保留,不能被 shutdown 覆盖。生命周期 stop 自身幂等;页预算、stop 证据或时间预算未收敛时返回 `timed_out`,但仍继续关闭后续 owner。
|
||
|
||
## 替代方案
|
||
|
||
1. **每个任务一个 timeout timer**:任务数直接决定 timer/closure 数和停机竞态,拒绝。
|
||
2. **Promise settle 直接写终态**:内存事实无法跨重启,且绕过 receipt capability,拒绝。
|
||
3. **取消先 signal 后写数据库**:数据库失败后无法证明意图与审计,拒绝。
|
||
4. **扫描 receipt 目录发现完成**:空闲和大目录成本不可控,也无法证明 active authority,拒绝。
|
||
5. **复用 cluster CancellationDispatch/Lease**:让路由设备携带多副本 authority 和驱动,拒绝。
|
||
6. **停机直接关闭 SQLite**:仍运行的进程失去控制面与终态收敛路径,拒绝。
|
||
|
||
## 影响与未完成项
|
||
|
||
已完成:
|
||
|
||
- 共享完成处理器、回执认证、原子 Attempt/Run 双终态和迟到回执幂等清理;
|
||
- SQLite cancellation/deadline/active stable keyset source,无新 migration 或第二连接;
|
||
- deadline→durable timeout intent、user/policy/shutdown cancellation 与 exact process stop;
|
||
- dispatcher 内部完成通知、单 timer Profile lifecycle 和 application admission-first shutdown drain;
|
||
- 启动恢复复用同一个完成处理器;
|
||
- 真实 SQLite completion、deadline、user cancellation、shutdown drain、通知合并和幂等停止测试。
|
||
|
||
仍未完成:
|
||
|
||
- retry policy 的产品入口与安全重试协调;
|
||
- 加密 Secret provider、Artifact retention/read API 与可观测指标/告警;
|
||
- target executable、systemd/Docker/s6 controller 和 2.x cutover 产品流程;
|
||
- Linux x64/arm64、PID namespace、强杀/断电/ENOSPC 与固定物理路由器实机门禁;
|
||
- PostgreSQL/Remote Worker completion、expiry、cancellation 和 retry 对等生命周期。
|
||
|
||
因此本 ADR 关闭本机单次执行的 completion/cancellation/timeout/shutdown 纵向缺口,但不表示整个 3.0 target 已可默认接管存量任务。
|
||
|
||
## 验证
|
||
|
||
1. completion receipt 认证后原子写入 Attempt/Run 双终态和顺序事件,清理发生在提交之后。
|
||
2. cancellation 与 deadline 在 signal 前已有 durable intent;数据库失败、无 exact handle 或 stop unknown 时不写假终态。
|
||
3. due source 使用既有索引、稳定 keyset、最多 64 条;edge/standalone 只读取 4/32 条。
|
||
4. shutdown drain 先写 `shutdown`,保留已有 reason,并在 storage close 前停止 exact active execution。
|
||
5. dispatcher 不暴露 completion Promise,重复完成通知只消费一次 identity。
|
||
6. startup recovery 与在线 completion 使用同一处理器,迟到回执不能覆盖 cancelled/timed_out。
|
||
7. lifecycle 只拥有一个周期 timer、无任务级 timer/watcher,stop 调用幂等且有硬超时。
|
||
8. 本 ADR 落地时 dependency/source/lock、Profile vulnerability 和 application artifact 门禁包含第二十个 importer;ADR-0073 已将 local-secret 扩展为第二十一个。
|