Files
qinglong/docs/adr/ADR-0117-streamed-remote-worker-artifact-and-atomic-completion.md
T

133 lines
9.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# ADR-0117:流式 Remote Worker Artifact 与原子 Completion
- 状态:AcceptedS3-compatible 共享对象存储已由 ADR-0120 实现,生产执行面装配已由 ADR-0121 实现;retention 与完整 Worker 产品生命周期仍默认关闭)
- 日期:2026-07-23
- 关联 RFCQL-RFC-0001 D-14、D-17、D-24、D-26、D-68、D-85、D-108、D-111、D-114、D-115、D-116
- 关联 ADRADR-0024、ADR-0058、ADR-0109、ADR-0112、ADR-0115、ADR-0116
## 背景
ADR-0116 固定了 Worker 的 upload-before-completion 顺序,但中央侧仍只有抽象 port。若通过普通
JSON/base64 发送 64 MiB 日志,Worker 和 controller 都会产生大对象复制;低配路由设备无法承受,
高并发节点也会放大 GC。若 controller 先把日志写入本地盘,多副本 completion 可能看不到上传
结果;若只信任 Worker 上报的 digest,终态可引用不存在或已漂移的内容。
completion 还必须关闭两个崩溃窗口:running ACK 已完成后的正常终结,以及进程已经完成、但
Worker 在 running ACK 前崩溃后从 durable `starting` 证据恢复。网络 upload 不得跨 PostgreSQL
事务持锁,终态又必须一次完成 Lease、Attempt、Run 和 Event,不能由多个 API 逐步拼接。
## 决策
### 1. 不新增 package,使用两个 exact versioned contract
wire contract 放入 `@qinglong/runtime-core/remote-worker-completion`Worker adapter 放入现有
`@qinglong/worker-runtime/completion-transport`Cluster service 与 PostgreSQL repository 分别
留在现有 cluster-control、cluster-postgres。它们与既有部署、依赖、权限和版本责任相同,不满足
D-85 的拆包条件。
Artifact schema 为 `qinglong/remote-worker-artifact-upload@v1`。request 使用 4-byte big-endian
JSON header lengthheader 最多 4 KiB,随后是最多 64 MiB 原始内容。header 只携带 execution
fence、Artifact identity、内容长度和 truncationWorker/Session identity 由 path 绑定,不在 body
重复。media type 固定为 `application/vnd.qinglong.worker-artifact`,必须 identity encoding 和精确
Content-Length,不允许 chunked、压缩或 multipart 产生第二套解析语义。
completion schema 为 `qinglong/remote-worker-completion@v1`request/response 分别最多 16/4 KiB
字段 exact,未知字段拒绝。普通 Worker ingress JSON 的 4 KiB 默认与 64 KiB hard cap 不因 Artifact
stream 放宽;只有 Artifact route 使用独立 64 MiB + 4 KiB + 4 bytes hard cap。
### 2. 复用单一 mTLS client 和有界 backpressure
Worker Artifact uploader 与 completion client 复用 ADR-0112 的 `WorkerIngressHttpsClient`、TLS 1.3
mTLS credential provider、canonical `ql3w` authorization 和单 keep-alive Agent。stream writer 按
已知长度逐 chunk 写入,Node socket 返回 backpressure 时等待 `drain`;短读、超读、错误类型、
错误 route、响应越界或 authority 漂移全部 fail closed。
Artifact preamble、TLS credential material、JSON request/response 临时 buffer 在消费后尽力清零。
完成协调器继续只从 durable inbox 装配 lease capability;通用 Artifact source/store 不接收 Worker
credential,也不记录 capability。
### 3. 认证和授权先于内容,store 必须跨副本共享
Cluster HTTP surface 必须先完成 route、mTLS/credential、Worker/Session 和 admission prepare,再读取
stream body。Artifact service 读取有界 header 后,在共享 Attempt advisory fence 下通过 PostgreSQL
repository 精确复验 Worker generation、offer、Lease generation/token digest/version/expiry、Run、
Attempt 和状态;授权成功后才把原始内容交给 store。
`ClusterRemoteWorkerArtifactStore` 是 immutable put-if-absent portkey 至少绑定 Project、Run、
Attempt、log Artifact ID,返回实际消费的 length、SHA-256 和 truncation。实现必须跨所有
cluster-control replica 可见,并对相同 identity + digest 提供 exact replay;同 identity 不同内容
拒绝。controller 本地目录、Pod ephemeral volume 或仅 process-local Map 只能用于测试,不能声明为
生产 cluster store。
store 不接收 Worker/Session identity、raw lease token 或 callback capability。上传事务不跨 store
I/O 持有数据库锁;因此 completion 必须重新调用 `inspect`,精确匹配 immutable Artifact 的 ID、
length、SHA-256、truncation 和 Project/Run/Attempt authority 后才能进入终态事务。
### 4. completion 是一个 PostgreSQL 权威事务
repository 使用既有 Attempt advisory fence,并按 Worker Session、Run/Attempt、Run Lease 的固定
顺序 `FOR UPDATE`,随后读取一次数据库时间。它复验 path principal、Session/generation、offer、
Lease generation、raw token 的 SHA-256、expected version、callback sequence/digest 和 Artifact
receipt。raw token 只在应用内比较摘要,不进入 SQL、Event 或错误。
允许的 live 起点只有:
- `starting + dispatching`:覆盖 spawn 后/running ACK 前的 authenticated receipt 恢复;
- `running + running`:正常 completion。
Worker finished time 不能晚于数据库时间加五分钟。最终状态首先服从 durable cancellation/timeout
intent,再解释 exit code;不得让迟到成功覆盖取消或 timeout。单一 transaction 必须:完成 Lease
并把 version 加一、终结 Attempt、终结 Run、写 Attempt 与 Run 两个 Event。Event ID 由服务端生成,
terminal timestamp 取数据库 observation、Worker evidence 和既有 start/create facts 的安全上界。
### 5. exact replay 由终态和 Event 共同认证
只有 Lease 已 `completed`、Lease/Attempt version 精确为 request expected version + 1、Run/Attempt
终态和 callback/Artifact/exit/error facts 完全一致,并且已有 Attempt completion Event 具有 exact key
set 与 exact payload,才返回 `already_completed`。Event 的 `from_status` 只允许 `starting|running`
任何字段漂移为 `replay_mismatch`;其他已终态 aggregate 返回 `already_terminal`,不能被当前 Worker
改写,也不能重复追加 Event。
### 6. 生产默认关闭条件
ADR-0120 已选择一个 S3-compatible 共享 immutable adapter;任何 production composition 都必须显式
注入它,缺失时 Artifact/completion routes 不注册或 admission fail closed。不得用 controller-local
fallback 自动降级。ADR-0121 已提供默认关闭的 Worker execution composition,但 transport 类存在仍
不得自动启动 timer、Pull 或 completion loop;完整 Session/credential 产品生命周期继续由外层门禁。
本 ADR 不完成 Artifact range read/retention、spool deletion、对象存储 credential/临时前缀 lifecycle
的产品配置,也不声明多 Pod/failover 实机支持。durable timeout/cancellation/lease-loss stop 已由
ADR-0118 完成;S3 SSE/KMS adapter 与单节点 MinIO 真实验证由 ADR-0120 完成;默认关闭的生产执行面
装配与 drain-before-release 由 ADR-0121 完成。
## 被否决的替代方案
1. **JSON/base64 Artifact**:峰值内存和复制与日志大小成正比,拒绝。
2. **multipart/form-data**:引入第二套复杂边界解析且没有当前单 Artifact 请求的收益,拒绝。
3. **controller 本地文件作为 cluster store**:多副本不可见、Pod 重建丢失,拒绝。
4. **先写 store、后信任 Worker 自报 digest**:不能证明中央内容与终态一致,拒绝。
5. **跨 upload 网络调用持 PostgreSQL transaction**:长锁和连接占用放大 store 尾延迟,拒绝。
6. **completion 分别更新 Lease、Attempt、Run**:中途崩溃产生不可解释部分终态,拒绝。
7. **只按终态行做幂等**:无法证明历史 completion payload,拒绝。
8. **为 upload/client/repository 各建 package**:没有独立依赖/权限/发布责任,违反 D-85,拒绝。
## 验收证据
1. runtime contract 拒绝未知字段、越界 header/content、错误 authority 和结果不一致。
2. HTTP surface 在读取 stream 前完成 admission,拒绝错误 media type、encoding、Content-Length 和
未完整消费;普通 JSON cap 保持不变。
3. Worker client 使用 fixed length、backpressure 和共享 Agent;短读、超读、route confusion 拒绝。
4. 真实 TLS 1.3 mTLS 测试完成分帧 Artifact upload 与 completion,并验证 path/body identity 分离。
5. Artifact service 先授权再 store,验证实际消费 length/digestcompletion 前重新 inspect。
6. PostgreSQL repository 从 `starting``running` 收敛,在单 transaction 完成 Lease/Attempt/Run/
双 Eventcancellation/timeout 优先。
7. exact replay 同时验证终态、version 和 Event payload;非法 `from_status`、digest 漂移拒绝。
8. raw lease token 不进入 SQL/EventEvent identity 由服务端生成。
9. Worker、runtime-core、cluster-control、cluster-postgres 完整测试通过,未新增 workspace package、
migration、schema、timer、queue 或常驻连接。
10. ADR-0125 的本机 arm64 PostgreSQL 18.4 physical-promotion 门在 runtime-role completion
transaction 的 driver-confirmed `COMMIT` 后终止 backend,使首次调用得到 unavailable;同一
fence/receipt/Event identity 重放为 `already_completed`。standby 在 promotion 前、promoted
primary 在 timeline 2 上均保持 succeeded Run/Attempt、completed Lease version 5、2 条 Event、
2 个 dedupe key 和 0 duplicate;故障范围是 PostgresClient 边界,不是 raw-wire packet-loss。