mirror of
https://github.com/whyour/qinglong.git
synced 2026-09-21 01:32:44 +08:00
99 lines
7.7 KiB
Markdown
99 lines
7.7 KiB
Markdown
# ADR-0109:PostgreSQL Remote Run 原子启动 ACK
|
||
|
||
- 状态:Accepted
|
||
- 日期:2026-07-22
|
||
- 关联 RFC:QL-RFC-0001 D-23、D-57、D-107、D-108
|
||
- 关联 ADR:ADR-0013、ADR-0014、ADR-0057、ADR-0058、ADR-0108
|
||
|
||
## 背景
|
||
|
||
ADR-0108 已让认证 Worker 用稳定 `offerId` 和自己持有的高熵 lease capability 领取经过 Placement 的 Execution Offer,但 claim 只证明某个 Session 暂时拥有 Attempt。若 delivery、Worker 收包或 spawn 被直接解释成 running,控制面就无法区分“尚未接受”“正在建立执行器”和“已取得可恢复 handle”三个崩溃窗口。
|
||
|
||
QingLong 3.0 还必须同时服务低配路由设备和集群节点。ACK 协议不能要求 Worker 维持额外 stream、服务端 mailbox 或 per-run timer,也不能为了三个短命令再增加 workspace package。多副本控制面则必须让 Worker Session、Run Lease、Run、Attempt 和 Event 在同一个 PostgreSQL authority 下裁决。
|
||
|
||
## 决策
|
||
|
||
### 1. 三个 ACK 是独立、认证且有界的命令
|
||
|
||
Worker ingress 增加 `starting`、`running` 和 `start-failure` 三个显式操作。transport 先绑定 credential principal 与路径中的 `workerId/sessionId`,完成 durable security audit 后才调用注入的 activation port。请求体不能自报 Worker ID,响应不得回显 lease token 或 callback token digest。
|
||
|
||
每个命令必须携带 Run、Attempt、Session generation、offer ID、lease generation、lease token 和 expected lease version。`running` 还必须携带最多 512 字节的稳定 executor handle、可空的 log artifact ID、callback sequence 和 lowercase SHA-256 callback token digest。所有字符串都有固定上限;协议不建立常驻连接、队列、timer 或新增 package。
|
||
|
||
### 2. PostgreSQL 是唯一启动状态 authority
|
||
|
||
每个 ACK 使用一个短事务,并按 Attempt advisory fence、Worker Session、Run/Attempt、Run Lease 的固定顺序加锁。完成锁定后才读取 PostgreSQL statement time,并验证:
|
||
|
||
- Worker Session 仍是同一 session/generation,状态为 `online|draining` 且 lease 未过期;
|
||
- Run 为 runtime-owned,Attempt executor type 为 `remote_worker`;
|
||
- Run Lease 与 Attempt 投影中的 worker/session/generation/offer/generation/token digest/version 完全一致;
|
||
- 非终态 ACK 的 Lease 仍为 leased 且按数据库时间未过期。
|
||
|
||
Worker 的本机时钟不进入 Run/Attempt 时间事实。任何 fence、状态或重放元数据漂移都以低基数 conflict 失败,事务内任何 SQL 失败都回滚全部状态和 Event。
|
||
|
||
### 3. starting 与 running 具有不同提交边界
|
||
|
||
首次 `starting` 只把 Attempt 从 `claimed` 推进为 `starting`,Run 保持 `dispatching`,并追加一条 `attempt.starting` Event。完全相同 authority 的重放返回 `already_starting`;若已正确 running,迟到 starting 返回 `already_running`。
|
||
|
||
首次 `running` 要求 Attempt 已 starting,并在同一事务中:
|
||
|
||
- 把 Attempt 与 Run 推进为 running;
|
||
- 使用数据库时间写两者的 started time;
|
||
- 固化 executor handle、可选 log artifact、callback sequence 与 callback token digest;
|
||
- 连续追加 `attempt.running`、`run.running` 两条 Event。
|
||
|
||
running 不推进 Run Lease version;renewal 仍是 active Lease version 的唯一推进者。重放只有在 handle、artifact、callback sequence、callback digest 和完整 Lease authority 都一致时才返回 `already_running`。
|
||
|
||
### 4. start-failure 原子终结并关闭晚到回调
|
||
|
||
首次 `start-failure` 只接受 starting/dispatching,并在同一事务中:
|
||
|
||
- 按 cancellation fact 选择 `failed|cancelled|timed_out`;
|
||
- 写固定错误码与低敏摘要;
|
||
- 推进 callback sequence,关闭晚到 completion receipt;
|
||
- 终结 Attempt 与 Run,追加两条对应 Event;
|
||
- 将 Run Lease 标记 completed 并推进 version,同时更新 Attempt 的 lease version 投影。
|
||
|
||
普通失败使用 `EXECUTOR_START_FAILED`;已有普通取消使用 `EXECUTION_CANCELLED`;timeout 取消优先使用 `EXECUTION_TIMED_OUT`。丢失成功响应后的 retry 允许同一原始 expected version 精确命中 `completed version = expected + 1`,但仍必须证明同一 token、offer、Session、generation 和终态映射;否则拒绝,且绝不重复 Event。
|
||
|
||
### 5. 权限与包边界不扩大
|
||
|
||
contract 放在 `@qinglong/runtime-core/remote-activation`,PostgreSQL adapter 放在既有 `@qinglong/cluster-postgres/runtime`,应用服务放在 `@qinglong/cluster-control/remote-activation`。总包数保持 23。
|
||
|
||
独立 Worker-ingress application 只持有注入 port,且 `ql3_worker_ingress` role 继续没有 Run、Attempt、Run Lease mutation 权限。权威事务由受审 runtime role 执行;transport 与 runtime 的产品装配仍必须显式建立受保护的内部边界,不能让 HTTP handler 临时获取 runtime Pool。
|
||
|
||
## 被否决的替代方案
|
||
|
||
1. **claim 或 delivery 后直接标记 running**:无法证明执行器已建立,也无法安全恢复 handle。
|
||
2. **信任 Worker startedAt**:不同路由器和集群节点的时钟漂移会污染权威顺序。
|
||
3. **只校验 token,不校验 offer/session/version**:旧 Session、renewal 前请求或 ABA Lease 可以迟到覆盖。
|
||
4. **running 只保存 handle,不保存 callback digest**:后续 completion 与 recovery 无法证明同一回调 capability。
|
||
5. **给 worker-ingress role 表级写权限**:认证 transport 会获得绕过应用 fence 的宽 Run mutation authority。
|
||
6. **为 ACK 新增 package、队列或 stream**:没有新的发布/权限/平台边界,却会扩大低配安装闭包和常驻资源。
|
||
|
||
## 影响
|
||
|
||
正向影响:
|
||
|
||
- offer、starting、running 和 start-failure 成为可区分、可恢复的 durable facts;
|
||
- 多副本 ACK、renewal、Session replacement 和 cancellation 在同一数据库 authority 下竞争;
|
||
- 明文 lease/callback capability 均不落库、不进 Event、不出现在响应;
|
||
- 路由设备只需三个短请求,集群 Worker 可按自身并发扩展,服务端不保留 per-worker 内存状态;
|
||
- 不新增 migration、package 或 edge/standalone 依赖。
|
||
|
||
仍未完成:
|
||
|
||
- 认证 ExecutionSpec delivery 与 Worker durable admission 已由 ADR-0110 完成;仍缺 package inbox 到执行 Receiver 的单 journal 状态迁移,以及 Artifact/log/completion transport;
|
||
- PostgreSQL completion、lease expiry/lost、cancellation/retry 的对等远端生命周期;
|
||
- runtime ACK port 与独立 Worker listener 之间的生产内部调用边界、过载保护和 telemetry;
|
||
- 多 Pod/failover、PostgreSQL 18、真实路由设备网络抖动和断电证据。
|
||
|
||
因此 ACK 门禁已经完成,但 Pull/Remote Worker 仍不默认进入生产组合根。
|
||
|
||
## 验收证据
|
||
|
||
1. runtime contract 拒绝越界 handle、非法 callback digest、弱 lease capability 和不完整 fence。
|
||
2. cluster-control 测试证明服务端生成 Event ID、principal/session 绑定、三个认证 ingress operation、冲突映射以及响应不回显 capability/digest。
|
||
3. PostgreSQL 16.10 真实实例以独立 migration/runtime/worker-ingress role 验证 Pull offer、digest-only 丢响应恢复、starting/running 精确重放、启动失败与 timeout 优先终态;Run/Attempt/Lease/Event/callback fence 原子提交且 token 明文不落库。
|
||
4. `ql3_worker_ingress` 保持原最小权限;ACK repository 只从 runtime entrypoint 导出。
|
||
5. 23 个 package clean build/全量测试、backend 兼容回归、cluster/edge dependency audit、六档本地 Profile 制品与 GitNexus change detection 全部通过;制品最大 2,346,992 bytes/395 files、最大抽样 RSS delta 12,419,072 bytes,QL3 production importer 为 0 high/0 critical。GitNexus 对已跟踪 diff 为 LOW/0 affected process;当前 QL3 孵化树仍是未跟踪文件,因此该结果只作补充证据。
|