mirror of
https://github.com/whyour/qinglong.git
synced 2026-09-20 16:07:11 +08:00
108 lines
8.4 KiB
Markdown
108 lines
8.4 KiB
Markdown
# ADR-0057:PostgreSQL Worker Session 与 Run Lease Authority
|
||
|
||
- 状态:Proposed
|
||
- 日期:2026-07-19
|
||
- 关联 RFC:QL-RFC-0001 D-05、D-06、D-23、D-34 至 D-42、D-54 至 D-56
|
||
- 关联 ADR:ADR-0012、ADR-0013、ADR-0014、ADR-0021、ADR-0039、ADR-0043、ADR-0056
|
||
|
||
## 上下文
|
||
|
||
cluster-control 的 `run_attempts` 原先同时保存 Worker、executor、lease token 和 expiry。这个扁平快照可以支持 Run 聚合读取,却不能单独表达两个不同 authority:
|
||
|
||
- Worker Session Lease 证明某个经过认证的 Worker 进程、session 和 generation 当前仍可报告容量;
|
||
- Run Lease 证明该 session 对某个 Attempt 获得了一次带 generation/version 的执行授权。
|
||
|
||
如果直接把 Attempt 行上的 `worker_id + lease_expires_at_ms` 当作远程恢复证据,会产生三个错误:Worker 在线不等于某个执行仍在运行,lease 过期不等于执行已经停止,多副本 Dispatcher 也没有可以原子竞争和 fencing 的独立行。ADR-0056 的 Remote Worker provider 因此不能建立在该扁平快照上。
|
||
|
||
同时,QingLong 3.0 仍必须保证 edge/standalone 不因集群 Worker 能力引入 PostgreSQL、常驻连接或新 timer。
|
||
|
||
## 决策
|
||
|
||
### 1. Session 与 Run Lease 使用独立事实表
|
||
|
||
PostgreSQL capability v8 新增:
|
||
|
||
- `worker_sessions`:以 `worker_id` 为主键,保存 session UUIDv7、generation、status、version、canonical capabilities/hash、容量、heartbeat 和数据库时间 lease;
|
||
- `run_dispatch_leases`:以 `attempt_id` 为主键,保存 Run、Worker session/generation、lease generation/version、token digest、稳定 offer ID 和终态元数据。
|
||
|
||
`run_attempts` 只保留用于聚合读取和 recovery snapshot CAS 的精确 fence 投影:Worker session/generation、lease generation/version、token digest、offer ID 和 expiry。`run_dispatch_leases` 才是执行授权的事实源;Attempt 投影不能独立续租、释放或证明执行停止。
|
||
|
||
### 2. Bearer lease token 不得持久化
|
||
|
||
新 cluster Run Lease API 接收内存中的高熵 bearer token,但 PostgreSQL 和返回 DTO 只保存/暴露 SHA-256 digest。claim、renew 和 release 在进程内派生 digest 后比较;事件、日志、recovery target 和诊断输出都不得包含原 token。
|
||
|
||
旧 `run_attempts.lease_token` 暂时保留用于迁移兼容,但新的 PostgreSQL Run Lease repository 固定写入 `NULL`。删除该列需要独立 migration 和 2.x 回退窗口审计。
|
||
|
||
### 3. 所有 lease 时间由 PostgreSQL 在取得必要锁之后观察
|
||
|
||
客户端时钟不参与 Session/Run Lease expiry。Repository 使用 `statement_timestamp()`,并且只能在以下锁已取得后读取 observation:
|
||
|
||
- Worker registration:按 Worker ID 的 transaction advisory lock 和现有 Worker 行锁;
|
||
- heartbeat/transition:Worker 行锁;
|
||
- Run claim:Worker、Run/Attempt 与既有 Run Lease 行锁;
|
||
- renew:Worker 与 Run Lease 行锁;
|
||
- release:Worker、Run Lease、Run/Attempt 行锁。
|
||
|
||
这样锁等待时间不能被遗漏,过期判断和新 expiry 都基于真正进行裁决时的数据库时间。
|
||
|
||
### 4. Worker Session replacement 与 mutation 全量 fencing
|
||
|
||
首次注册建立 generation 1/version 0。相同 session 只有 capabilities、容量和仍存活的 lease 全部一致时才是幂等重放;冲突、offline 或已过期 session 均拒绝。新 session 在同一 Worker ID 锁下原子推进 generation/version,并返回 `replacedSession=true`。
|
||
|
||
heartbeat、drain 和 offline 必须同时匹配 Worker ID、session、generation 和 expected version。draining/offline 容量固定为零。Repository 不认证请求体;未来 Worker transport 必须先从 mTLS/短期 enrollment credential 建立 principal,不能把 `register()` 直接暴露为匿名网络入口。
|
||
|
||
### 5. Run claim 由 Worker 行锁串行化容量裁决
|
||
|
||
claim 固定执行:
|
||
|
||
1. 锁定目标 Worker;
|
||
2. 锁定 Run/Attempt 与已有 Run Lease;
|
||
3. 读取数据库 observation;
|
||
4. 验证当前 Worker session/generation、online、session lease、Run runtime ownership、取消意图和 claimed Attempt;
|
||
5. 在 Worker 行锁保护下计算该 session 的 active lease 数,并要求它同时小于 `maxConcurrentRuns` 和最近一次 heartbeat 上报的 `availableSlots` 上界;
|
||
6. 原子写入 Run Lease、Attempt fence、Run version/event sequence 与 dispatch Event。
|
||
|
||
相同 token digest、offer ID 和完整 fence 是幂等重放;不同调用者看到 live lease 只能得到 `leased`。续租同时 CAS Run Lease version 和 Attempt 投影;release 只处理尚未越过启动屏障的 claimed Attempt,并在同一事务清除投影和追加 Event。starting/running 的完成、失租与取消仍由后续专用事务处理,不能借 release 扩权。
|
||
|
||
### 6. 权限和 Profile 保持分离
|
||
|
||
常驻 runtime role 对 `worker_sessions`、`run_dispatch_leases` 只有 SELECT/INSERT/UPDATE,无 DELETE/DDL;cluster-admin 对两表零权限。migration role 仍是唯一 DDL authority。
|
||
|
||
未来认证 Worker attestation ingress 应使用独立入口和更窄数据库 capability;不能因为 runtime role 当前可管理调度 lease,就允许普通业务 handler 伪造 `not_running` evidence。
|
||
|
||
runtime-core 只包含无数据库 driver 的 contract/validator。PostgreSQL repository 只从 cluster package/runtime 子入口导出,edge/standalone importer 不引入 `pg`、表、连接或 timer。
|
||
|
||
## 被否决的替代方案
|
||
|
||
1. **继续只用 Attempt 行 lease**:无法区分 Session Lease、Run Lease 和恢复快照,也无法安全串行化跨 Attempt 的 Worker 容量。
|
||
2. **用控制面进程时间计算 expiry**:副本时钟漂移和锁等待会让 takeover/renew 裁决不一致。
|
||
3. **在 PostgreSQL 保存原 lease token**:数据库读权限、备份和诊断泄漏会直接获得执行 capability。
|
||
4. **只检查 availableSlots 是否大于零**:多个 Dispatcher 可在同一 heartbeat 之间重复消费相同槽位;必须在 Worker 行锁下要求 active leases 同时低于最大并发和上报槽位上界。这个保守模型优先避免超卖;未来若要把 `availableSlots` 精确解释为“扣除运行中任务后的瞬时空闲数”,必须引入独立 reservation epoch/counter,不能在没有 heartbeat 代际的情况下直接递减并与 Worker 上报相互覆盖。
|
||
5. **Session offline/lease expiry 直接作为 `not_running`**:这只能证明控制面 authority 失效,不能证明远端进程已经停止。
|
||
6. **把 PostgreSQL Worker adapter 放入根应用**:会重新污染 edge 安装图并让 cluster adapter 依赖 legacy Sequelize。
|
||
|
||
## 影响与未完成项
|
||
|
||
正向影响:
|
||
|
||
- Worker/Run 两层 lease 在多副本下有独立、可锁定、可审计的事实源;
|
||
- bearer token 不落库,recovery target 只携带 digest;
|
||
- Session replacement、capacity claim、renew/release 都有精确 fencing;
|
||
- Attempt recovery snapshot 可以绑定 session、lease generation/version 和 offer,而不把投影误当 authority;
|
||
- edge/standalone 依赖和空闲资源成本不变。
|
||
|
||
ADR-0058 已完成独立 Worker credential/ingress role、append-only attestation、Remote Worker provider 和 PostgreSQL 16.14 四角色验证。ADR-0108 又完成 PostgreSQL candidate/placement、认证 Worker Pull 与 digest-only offer recovery;ADR-0109 已完成 starting/running/start-failure 数据库权威 ACK,并用真实 PostgreSQL 16.10 runtime role 验证。该入口在 ExecutionSpec transport 与生产内部 port 门禁前仍默认不注入生产组合根。仍未完成:
|
||
|
||
- mTLS/enrollment transport、credential recovery ceremony 与 pepper rotation;
|
||
- completion、expiry/lost、cancellation 与 retry 的 PostgreSQL 原子协调;
|
||
- Worker 本地 journal 到网络 attestation 的生产装配与 Artifact/日志 transport;
|
||
- PostgreSQL 16/18 远端 CI 首次成功证据和多 Pool failover/lock-wait 基准。
|
||
|
||
## 验证
|
||
|
||
- runtime-core contract test 验证 bounded Worker Session、canonical snapshot hash、token digest 和 lease terminal shape;
|
||
- migration/Drizzle/schema/readiness lockstep test 冻结 `pg-0009` checksum、capability v8、表/列/index/CHECK/FK 和 runtime/admin 权限;
|
||
- 真实 PostgreSQL transaction test 验证注册/replacement、幂等 claim、容量耗尽、renew version fencing、release 清权、token 不返回和 Attempt 投影;
|
||
- 本机 PostgreSQL 13.3 仅用于 SQL/锁/约束验证,production readiness 仍按策略拒绝;发布证据必须来自 PostgreSQL 16/18 CI;
|
||
- edge dependency/import audit 必须继续证明该切片没有进入 edge/standalone 产物。
|