Files
qinglong/docs/adr/ADR-0109-postgresql-remote-run-activation-ack.md

7.7 KiB
Raw Permalink Blame History

ADR-0109PostgreSQL Remote Run 原子启动 ACK

  • 状态:Accepted
  • 日期:2026-07-22
  • 关联 RFCQL-RFC-0001 D-23、D-57、D-107、D-108
  • 关联 ADRADR-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 增加 startingrunningstart-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-ownedAttempt 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 推进为 startingRun 保持 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.runningrun.running 两条 Event。

running 不推进 Run Lease versionrenewal 仍是 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_CANCELLEDtimeout 取消优先使用 EXECUTION_TIMED_OUT。丢失成功响应后的 retry 允许同一原始 expected version 精确命中 completed version = expected + 1,但仍必须证明同一 token、offer、Session、generation 和终态映射;否则拒绝,且绝不重复 Event。

5. 权限与包边界不扩大

contract 放在 @qinglong/runtime-core/remote-activationPostgreSQL 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 bytesQL3 production importer 为 0 high/0 critical。GitNexus 对已跟踪 diff 为 LOW/0 affected process;当前 QL3 孵化树仍是未跟踪文件,因此该结果只作补充证据。