mirror of
https://github.com/whyour/qinglong.git
synced 2026-09-20 16:07:11 +08:00
feat(ql3): reconcile local lost run retries
This commit is contained in:
@@ -11,6 +11,16 @@
|
||||
|
||||
最新增量证据(2026-08-12):
|
||||
|
||||
- D-292/ADR-0380(已接受)
|
||||
Local Edge/Standalone 已补齐 admission-safe lost Run retry 纵向闭环,并修正“共享纯策略却暴露 Cluster-only 名称”的边界。
|
||||
Runtime Core 在既有 Run 域发布 canonical `RunLostRetry*`/`run-lost-retry` subpath,同时保留早期 Cluster 名称兼容;SQLite
|
||||
adapter 复用唯一 operation authority 与 `BEGIN IMMEDIATE` Run aggregate transaction,有界选择 runtime-owned、非 Workflow、
|
||||
无 cancellation intent 且 latest Attempt 为 lost 的候选,原子 CAS Run/RetryPolicy、插入新 Attempt 与 Events。Local application
|
||||
把一页 retry 接入现有 execution-control 周期,顺序为 completion→control→lost retry→慢速 cleanup;启动时在 recovery 后、Scheduler
|
||||
前执行首轮。Edge 为 2 条/5 秒,Standalone 为 16 条/1 秒,不新增 package、migration、表、索引、timer、连接、listener 或 sidecar。
|
||||
无 policy/禁用、unknown safety、attempt 耗尽均终态失败关闭;只有 admission 时已证明 `idempotent|deduplicated` 的策略才创建全新
|
||||
Attempt。workspace 仍为 18 package/1055 source/1037 nested,`singleSourcePackages=[]`、`shallowSourcePackages=[]`。人工
|
||||
`run.retry` API/CLI/UI、强认证、审计与 rate limit 仍未包含,不能把自动 lost retry 误报为人工恢复完成。
|
||||
- D-291/ADR-0379(已接受)
|
||||
Cluster Run Attempt 日志 retention 已完成多副本纵向闭环,且没有新增 package:共享 claim contract 位于 Runtime Core 既有 Run
|
||||
log-retention 目录;PostgreSQL v54 提供 durable control、immutable tombstone、terminal remote Worker candidate index 与最小权限
|
||||
@@ -8485,7 +8495,7 @@ flowchart LR
|
||||
|
||||
> ADR-0058 至 ADR-0092 的以下段落是历史快照,其 PostgreSQL 数字与“下一切片”已由 ADR-0104/0105 及后续 Accepted ADR 取代;资源、Package 和物理证据边界仍保留作为演进记录:PostgreSQL 当时 baseline 为 11 条 reviewed migration、capability v10、19 张表和 migration/runtime/admin/worker-ingress 四角色;本机 SQLite 为二十八条 reviewed migration、capability v14、二十六张 owned table,并新增正式 TaskDefinition head/immutable revision Repository。当前有二十七个受审 3.0 importer:Owner maintenance 已合并为两个权限隔离 subpath,execution/control/recovery/dispatch 已合并为 `@qinglong/local-execution` 四个单向 subpath,bootstrap/credential-recovery 已合并为 `@qinglong/local-owner-ceremony` 两个互不依赖 subpath;三个 package 都不提供聚合根入口,hard cap 已同步降为 27。一次拓扑 build/test 会先清理已登记 QL3 package 的 stale dist;27 包全量测试、dependency/source boundary、联网 vulnerability audit 与六种 Profile 制品门禁均通过。ADR-0090 让 production packlist 只发布 JS、`.d.ts` 和受审 assets/drizzle,不再携带开发 map;ADR-0092 后当前最大 application 为 1,691,009 bytes、267 files、61 loaded modules,最大抽样 RSS delta 11,780,096 bytes,低于 4 MiB/512 files/16 MiB。所有 package build 已变为 self-only,全量从约 198 次编译降至精确 27 次,clean 状态单包测试仍按依赖闭包工作。资源门禁已拆为原生 Linux x64/arm64 的 128 MiB router stress、256 MiB Edge release guard 与 512 MiB Cluster control guard,并从容器内部验证 cgroup v2、零 swap/OOM、非 root、只读挂载、seccomp 和 `NoNewPrivs`;本轮本地原生 arm64 三档已通过,x64 等待远端 CI,所有档位均明确不是物理设备或生产容量承诺。物理 Edge candidate recorder 已绑定设备 manifest、实际 Linux/存储环境、Edge/SQLite 基准与 no-replace SHA-256 报告,并会拒绝容器/VM;同设备同 boot 的 idle sampler、不主动填盘的专用文件系统 fault probe,以及通过正式 Repository 写入/扫描 100/1000/10000 个 `qinglong/command@v1` TaskDefinition 的规模记录协议均已具备契约门禁,但尚未取得固定实机报告或 signature。ADR-0091 已冻结 1–32 个 exact descriptor 的不可变 TaskSpec semantic registry、内建 command v1 和本机写前门禁;历史 revision 在 provider 缺失时仍可读。ADR-0092 已实现绑定 source revision/content digest 的 Profile-neutral command plan 与确定性本机 context/execution 映射,且保持 subpath-only。
|
||||
|
||||
> 下表 PR-1 的旧“未完成”累计文字中,`completion/cancellation/timeout` 已由 ADR-0072 取代并闭环,Artifact range read 已由 ADR-0377 闭环;当前本机剩余项是人工 recovery、retry 产品策略、Artifact retention/tombstone、HTTP/CLI/UI 与部署 controller。远端 Worker completion 与 Workflow cancellation 的核心数据库链和 HA 重放已闭环;仍缺 production ingress/internal runtime port、expiry/retry lifecycle、部署启动装配与真实 Kubernetes 故障证据,不能与本机结论混用。
|
||||
> 下表 PR-1 的旧“未完成”累计文字中,`completion/cancellation/timeout` 已由 ADR-0072 取代并闭环,Artifact range read 与 Local/Cluster retention 已由 ADR-0377/0378/0379 闭环,Local admission-safe lost retry lifecycle 已由 ADR-0380 闭环;当前本机剩余项是人工 recovery、手工 retry 产品策略/API、HTTP/CLI/UI、部署 controller 与固定路由设备实机门。远端 Worker completion、Workflow cancellation 和 Cluster expiry/retry 的核心数据库链、HA 重放与启动装配也已闭环;仍缺真实 Kubernetes 多节点故障证据,不能与本机结论混用。
|
||||
>
|
||||
> 下表 PR-0 的累积长文本仍含“四角色、21 条 migration、capability v20、21 项 HA”历史短语;当前权威基线应读取为六角色、23 条 migration、capability v22/36 表和 23 项 physical HA gate。ADR-0145/0146 又增加默认关闭的 manager-only TLS 1.3 management process、可选双副本 operation 与 durable distributed quota;它仍缺全副本重启 keyset anti-rollback 和真实 IdP/live ingress,所以“受认证管理入口”保持“已孵化但生产失败关闭”。
|
||||
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
# ADR-0380:Local lost Run retry 复用单一 execution-control cadence
|
||||
|
||||
- 状态:Accepted
|
||||
- 日期:2026-08-12
|
||||
- 关联 RFC:QL-RFC-0001 D-292
|
||||
- 前置决策:ADR-0021、ADR-0066、ADR-0067、ADR-0232、ADR-0366
|
||||
|
||||
## 上下文
|
||||
|
||||
Cluster 已消费 admission-time `RunRetryPolicy`,但 Edge/Standalone 的启动恢复只能把确定不再运行的本地 Attempt 与 Run 标记为 `lost`。此后没有生产 consumer 处理该状态:安全且显式启用的重试不会创建新 Attempt,禁用、unsafe 或耗尽的 Run 也不会终态关闭。
|
||||
|
||||
直接复制一套 Local retry policy、建立独立 lifecycle package,或为 retry 新增 timer/SQLite 连接都会形成不合理架构。纯 transition 本来就是 profile-neutral,只因最初由 Cluster 接入而使用 `Cluster*` 名称;本地运行时也已经拥有单 SQLite operation authority、`BEGIN IMMEDIATE` Run aggregate transaction 和唯一 execution-control timer。
|
||||
|
||||
## 决策
|
||||
|
||||
### 1. 通用契约留在现有 Runtime Core Run 域
|
||||
|
||||
不新增 workspace package,也不复制状态机。`runtime-core` 对既有纯 transition、page contract、coordinator 与错误发布 canonical `RunLostRetry*` 名称和 `run-lost-retry` subpath;早期 `ClusterRunLostRetry*` 名称继续兼容。新代码只能依赖 profile-neutral 名称。
|
||||
|
||||
该兼容层不创建第二份实现。后续在具备 call-graph-aware rename 工具和独立废弃周期后,才允许移动物理文件或删除旧名称;本切片不以文本替换破坏已有 Cluster consumer。
|
||||
|
||||
### 2. SQLite adapter 复用唯一 operation authority
|
||||
|
||||
`LocalSqliteRunLostRetryRepository` 位于 `ql3-local-sqlite/src/run`,不形成单文件微包。它:
|
||||
|
||||
- 只选择 runtime-owned、非 Workflow、无 cancellation intent、latest Attempt 为 `lost` 的 Run;
|
||||
- `lost` 优先,`retry_wait` 仅在 durable `next_attempt_at_ms` 到期后进入候选;
|
||||
- 候选发现是一次有界 authority 操作,每个候选在同一既有 `LocalSqliteRunRepository.transaction()` 中重读并原子 CAS Run、RetryPolicy,插入新 Attempt 与 Events;
|
||||
- 已变化的候选返回 `raced`,响应丢失后的重放由当前 durable aggregate 自然收敛;
|
||||
- 不拥有连接、cursor、timer、listener、watcher 或常驻内存队列。
|
||||
|
||||
没有 policy、未启用或 `maxAttempts<=1` 时以 `RUN_LOST_RETRY_DISABLED` 失败关闭;`safety=unknown` 以 `RUN_LOST_RETRY_UNSAFE` 失败关闭;耗尽以 `RUN_LOST_RETRY_EXHAUSTED` 失败关闭。只有 admission 时已证明 `idempotent|deduplicated` 的 policy 才能先进入 `retry_wait`,到期后创建一个全新、无执行 handle/lease 的 claimed Attempt。
|
||||
|
||||
### 3. Local application 复用现有控制周期
|
||||
|
||||
lost retry 注入 `LocalExecutionControlLifecycle`,每轮顺序固定为:
|
||||
|
||||
1. 消费内存中的 completion 通知;
|
||||
2. 扫描 cancellation/deadline control;
|
||||
3. 执行一页 lost retry;
|
||||
4. 到慢周期时清理 completion receipt 与 Run log Artifact。
|
||||
|
||||
启动顺序仍为 startup recovery → 首次 execution-control cycle → scheduler。因而启动恢复刚写入的 `lost` 会在 Scheduler 首轮前进入安全 retry/终态。停机的 control drain 不再创建 retry work。
|
||||
|
||||
Edge 每轮最多 2 条,沿用 5 秒 control cadence;Standalone 每轮最多 16 条,沿用 1 秒 cadence。两者均复用已有 `unref` timer 和 SQLite authority,没有增加低配路由设备的空闲连接、常驻 sidecar 或独立唤醒源。
|
||||
|
||||
### 4. 明确不包含人工重试产品入口
|
||||
|
||||
本 ADR 只消费 admission 时冻结的自动 retry policy,不授予用户新的 mutation authority。`run.retry` Policy permission、人工 recovery API/CLI/UI、强认证、rate limit、审计与“对失败 Run 手工重跑”的产品语义仍需独立 ADR;不得把自动 lost retry 误报为人工恢复完成。
|
||||
|
||||
## 验收
|
||||
|
||||
- Runtime Core 验证通用名称与旧 Cluster 名称指向同一实现;
|
||||
- SQLite 真库验证安全 lost → retry_wait → 新 Attempt 的原子闭环、禁用 policy 失败关闭、页上限、`hasMore` 与重放不重复;
|
||||
- Local lifecycle 验证 control → lost retry → cleanup 顺序及重叠 `runOnce` 合并;
|
||||
- Local application 真启动验证 startup recovery 后、Scheduler 前完成首次 lost retry;
|
||||
- 受影响 package build/test、完整 QL3 package/backend 回归、dependency/package/Edge import audit 必须通过后才允许阶段性提交;
|
||||
- package 数、migration 数、表/索引数、timer 数和 SQLite connection 数不得增加。
|
||||
|
||||
### 验收证据(2026-08-12)
|
||||
|
||||
- `pnpm run test:packages:ql3`:18 个 QL3 package 全量清理、构建与测试通过,退出码 0;
|
||||
- backend 回归:1165 项中 1163 通过、2 项按环境条件跳过、0 失败;
|
||||
- package boundary audit:18 个 package、1055 个 source file、1037 个 nested source file,`singleSourcePackages=[]`、`shallowSourcePackages=[]`;
|
||||
- cluster dependency audit:`findings=[]`;Edge import audit:121 个 imported module,root dependency 与 import 违规均为空;
|
||||
- 本切片未新增 package、migration、表、索引、timer 或 SQLite connection。
|
||||
|
||||
## 被否决的替代方案
|
||||
|
||||
1. **新增 `ql3-local-retry` package**:只有一个 SQLite adapter,继续放大 package 碎片化,拒绝。
|
||||
2. **复制 Cluster transition 为 Local 版本**:会让安全与终态规则漂移,拒绝。
|
||||
3. **独立 retry timer**:增加路由设备唤醒、竞态和 shutdown 故障域,拒绝。
|
||||
4. **启动时递归清空全部 lost backlog**:无法约束启动延迟与写放大,拒绝。
|
||||
5. **`safety=unknown` 也自动重试**:可能复制不可逆外部副作用,拒绝。
|
||||
6. **把人工 retry API 一并塞入本切片**:认证、Policy、审计与新 mutation 语义未冻结,拒绝。
|
||||
@@ -383,6 +383,7 @@
|
||||
| [ADR-0377](./ADR-0377-profile-aware-run-attempt-log-range-read.md) | Profile-aware Run Attempt 日志 Range 读取 | Accepted |
|
||||
| [ADR-0378](./ADR-0378-local-run-attempt-log-retention-and-tombstones.md) | Local Run Attempt 日志有界保留与 durable tombstone | Accepted |
|
||||
| [ADR-0379](./ADR-0379-cluster-run-attempt-log-retention.md) | Cluster Run Attempt 日志多副本保留与条件删除 | Accepted |
|
||||
| [ADR-0380](./ADR-0380-local-lost-run-retry-single-control-cadence.md) | Local lost Run retry 复用单一 execution-control cadence | Accepted |
|
||||
|
||||
## 规则
|
||||
|
||||
|
||||
Reference in New Issue
Block a user