mirror of
https://github.com/whyour/qinglong.git
synced 2026-09-21 18:08:20 +08:00
153 lines
7.9 KiB
Markdown
153 lines
7.9 KiB
Markdown
# ADR-0228:有界 Plugin Package Workflow 整体取消收敛
|
||
|
||
- 状态:Proposed
|
||
- 日期:2026-07-30
|
||
- 关联 RFC:QL-RFC-0001 D-18、D-19、D-71、D-117、D-207、D-212、D-213
|
||
- 关联 ADR:ADR-0072、ADR-0118、ADR-0121、ADR-0226、ADR-0227
|
||
|
||
## 背景
|
||
|
||
Workflow Task 已有 generation-bound admission、StepRun-aware
|
||
activation/completion 和专用 recovery,但普通 Run cancellation convergence
|
||
一次只处理一个 Run 与最新 Attempt。Workflow aggregate 则以同一个 Run 承载最多
|
||
128 个 StepRun 和多个 Task Attempt;直接复用普通逻辑会出现三类错误:
|
||
|
||
1. 父 Run 被提前写为 terminal,而在途 Worker 仍有 execution authority;
|
||
2. pending/ready StepRun 继续被 frontier 或 Task admission 推进;
|
||
3. 只终结“最新 Attempt”,其余 StepRun 永久不能收敛。
|
||
|
||
同时,不能为每个 Workflow 或 Step 增加 timer、watcher、child Run、额外 claim 表或
|
||
workspace package。Edge/Standalone 的 SQLite 单连接与 Cluster 的 PostgreSQL 多副本
|
||
必须共享同一个领域裁决,但采用各自合适的锁和分页策略。
|
||
|
||
## 决策
|
||
|
||
### 1. 取消意图只属于父 Workflow Run
|
||
|
||
`cancel_requested_at_ms` 与 `cancel_reason` 继续写在 aggregate Run。收到意图后:
|
||
|
||
- Workflow frontier 和 Task Attempt candidate 都必须排除该 Run;
|
||
- 不再产生新的 ready StepRun 或 Task Attempt;
|
||
- 已 durable 的 Task Attempt 仍由原 activation/completion/recovery authority 收敛,
|
||
cancellation reconciler 不能伪造 Worker 已停止。
|
||
|
||
### 2. 使用一个 profile-neutral 纯状态机
|
||
|
||
`runtime-core/plugin-package-workflow-cancellation-convergence` 接收 exact Run、
|
||
全部 StepRun、immutable admission-bound active Task Attempt、lease 状态和观察时间,
|
||
确定性输出:
|
||
|
||
- 可安全终结的 claimed、尚未执行 Attempt transition 与 Event;
|
||
- non-executing StepRun mutation/Event;
|
||
- 仍持有执行 authority 的 blocked Attempt/StepRun identity;
|
||
- 仅当 projected StepRun 全部 terminal 时才生成父 Workflow terminal transition。
|
||
|
||
状态机不读取数据库、不持有连接、不创建 timer,也不决定执行器 stop。输出 ID 由
|
||
Run、取消时间、target identity 和 durable epoch 生成,最长 36 字符,可在未知提交
|
||
结果后重算。
|
||
|
||
### 3. start barrier 不能被取消协调器越过
|
||
|
||
- claimed 且没有 active lease 的 Task Attempt 可以在 exact admitted ready epoch
|
||
直接收敛为 `cancelled|timed_out`;
|
||
- claimed + leased、starting、running 一律 blocked,等待 Worker completion 或可信
|
||
recovery;
|
||
- running StepRun 即使缺少当前 adapter 可见的 Task Attempt,也保持 blocked,不能写
|
||
假终态;
|
||
- pending/lost StepRun 在 aggregate timeout 下写 `cancelled`,因为它们没有实际
|
||
执行超时;ready/waiting_approval 可写 `timed_out`;
|
||
- 用户、Policy、shutdown、reconcile 取消统一映射为 `cancelled`;父 Run 的 timeout
|
||
映射为 `timed_out`。
|
||
|
||
### 4. 父 Run 最后终结
|
||
|
||
每个可安全 Attempt Event、StepRun mutation/Event 都先消耗连续 aggregate
|
||
version/event sequence。只有所有 StepRun projected terminal 且 blocked 集合为空时,
|
||
才追加 `workflow.cancelled|workflow.timed_out` 并终结父 Run。部分收敛后父 Run
|
||
保持 `running` 和原 cancel intent,后续 caller-driven cycle 继续处理。
|
||
|
||
一个 Workflow 可以在一页中结算多个 Attempt,因此共享 page result 的
|
||
`settledAttempts` 上界从 `settledRuns` 修正为
|
||
`scanned * MAX_STEP_RUNS_PER_RUN`;`settledRuns` 和 `blocked` 仍以 aggregate Run
|
||
计数。
|
||
|
||
### 5. PostgreSQL 使用既有 Attempt authority 锁顺序
|
||
|
||
Cluster adapter 留在 `@qinglong/cluster-postgres` 的显式 subpath/既有 cancellation
|
||
repository:
|
||
|
||
1. 普通非 Workflow Run 保留原 `SKIP LOCKED` 收敛;
|
||
2. Workflow candidate 不先锁 Run;
|
||
3. 先稳定排序 active Attempt ID,并取得 `pg_try_advisory_xact_lock`;
|
||
4. 再锁父 Run、active Attempt、lease 和全部 StepRun,重新读取 immutable admission;
|
||
5. 在一个短事务中 CAS Attempt/StepRun/Run,并追加全部 RunEvent 与
|
||
StepRunMutation。
|
||
|
||
这保持 activation/completion/recovery 的 Attempt authority → Run/Attempt/Step
|
||
固定锁序。锁竞争返回 blocked,不以扩大 timeout 或交换锁序处理。
|
||
|
||
### 6. SQLite 使用每 Workflow 一个短事务
|
||
|
||
Edge/Standalone adapter 留在既有 `@qinglong/local-sqlite` 包并复用共享
|
||
`LocalSqliteOperationAuthority`。一页先有界读取 candidate,但每个 Workflow 单独执行
|
||
一个 `BEGIN IMMEDIATE`:
|
||
|
||
- 页面增大不会把一个 SQLite write lock 放大到整页 DAG;
|
||
- 单 Workflow 最多 128 StepRun,内存和写放大有硬上限;
|
||
- 中途故障只会留下已完整提交的前序 Workflow,重跑按 durable terminal 状态继续;
|
||
- 不增加连接、后台线程、timer、表、migration 或依赖。
|
||
|
||
## 不采用的方案
|
||
|
||
### 每个 Step 创建 child Run
|
||
|
||
拒绝。会复制 Workflow/Step 聚合、取消 intent、版本序列和 recovery authority。
|
||
|
||
### cancellation reconciler 直接终结 leased/running Attempt
|
||
|
||
拒绝。stop_requested 不是执行已停止的证据,迟到 completion 可能来自仍合法的 Worker
|
||
authority。
|
||
|
||
### 为 Workflow 新建 cancellation package、表或 cadence
|
||
|
||
拒绝。该能力没有独立部署、权限或重依赖边界;复用现有 core/storage subpath 更符合
|
||
D-207,也避免增加路由器供应链和空闲唤醒成本。
|
||
|
||
## 当前验证
|
||
|
||
1. `runtime-core` 共享状态机覆盖全部 non-executing、unleased claim、leased/running
|
||
blocked、timeout 映射、determinism 与 stale admission,完整测试 419/419;
|
||
2. `local-sqlite` 在真实内存 SQLite 上完成 Attempt + 两 StepRun + 父 Run 原子取消、
|
||
admission crash 与 conclusive-stop/control-terminal crash 的 exact replay,
|
||
完整测试 189/189;
|
||
3. `cluster-postgres` 完整测试 238 项为 237 pass、1 条条件 skip、0 fail;
|
||
4. 全新 PostgreSQL 18.4 最小权限六角色实例已完成 admission→recovery→requeue→
|
||
second admission→whole cancellation,连续事件和所有 terminal fact 同事务提交;
|
||
5. `QL3_HA_SKIP_IMAGE_PULL=true` 的 PostgreSQL 18.4 arm64 物理 HA 门再次通过
|
||
`remote_apply`、timeline 1→2、旧主先 fencing、`pg_rewind` 只读同步重入、两个
|
||
fresh control replica 与 `gates.passed=true`;
|
||
6. Cluster production composition 已复用原有全局 cancellation lifecycle 和同一个
|
||
PostgreSQL repository,因此不增加第二 cadence。
|
||
7. ADR-0229 已把 Local cancellation、frontier、Task admission 与 dispatch 接入同一
|
||
scheduler cadence;启动前取消的 Workflow 原子终结两个 StepRun、零 Attempt,
|
||
普通 Run recovery 扫描为 0;
|
||
8. PostgreSQL physical HA 已新增独立 Workflow Task Attempt report,原子提交、
|
||
exact replay、晋升前复制、runtime-only ACL 与晋升后存活五项 gate 全为 true;
|
||
9. Local 两步 Workflow、每 Workflow 一个 SQLite write transaction,以及
|
||
Edge/Standalone 两组各 16 点 admission 与 conclusive-stop/control-terminal
|
||
crash/reopen/replay 已接入既有 128/256 MiB Linux resource gate。arm64 实测的
|
||
Workflow process peak RSS 分别为 `87449600`/`87949312` bytes,写锁 p95
|
||
分别为 `4.053`/`3.317` ms,cgroup memory pressure/OOM 增量为零;
|
||
workspace 仍为 20 包。
|
||
10. PostgreSQL 18.4 arm64 physical HA 新增真实 Remote Workflow cancellation
|
||
矩阵:正式 admission/Task Attempt、`stop_requested`、成功退出映射 cancelled、
|
||
completion 与父 Workflow convergence 两个 COMMIT response-loss 窗口、
|
||
exact replay、`remote_apply`、timeline 1→2 与 promotion 后再次 replay 均通过;
|
||
Run/Attempt/StepRun 为 cancelled、Lease 为 completed@v6,8 个 Event 与 3 个
|
||
StepRun mutation 无重复。
|
||
|
||
## 尚未关闭
|
||
|
||
1. 固定 Edge/Standalone 物理设备基线、闪存/FTL 写放大和受控突发断电门;CI
|
||
`SIGKILL` 证据不提升为物理 power-loss 结论。
|