mirror of
https://github.com/whyour/qinglong.git
synced 2026-09-20 16:07:11 +08:00
139 lines
6.9 KiB
Markdown
139 lines
6.9 KiB
Markdown
# ADR-0186:Cluster PostgreSQL Plugin Package 隔离与能力撤出
|
||
|
||
- 状态:Accepted
|
||
- 日期:2026-07-28
|
||
- 关联:RFC D-137、D-149、D-152、D-154、D-158、D-174、D-175、D-176;
|
||
ADR-0125、ADR-0137、ADR-0152、ADR-0154、ADR-0158、ADR-0184、ADR-0185
|
||
|
||
## 背景
|
||
|
||
D-174 已为 Edge/Standalone 建立 SQLite quarantine overlay,但 Cluster 仍只有 Package
|
||
安装、物化、Task reconciliation 与 Tool snapshot 的 PostgreSQL事实。若 Cluster 只在
|
||
某个 control replica 内保存 denylist,重启、水平扩容或主备提升都会重新暴露受影响
|
||
能力;若只改变 install state,则已发布的 Task revision 和历史 Tool snapshot 仍能被
|
||
新的 Run/StepRun 引用。
|
||
|
||
隔离必须同时满足:
|
||
|
||
- event、Task disable、Tool source withdrawal 和 receipt 原子提交;
|
||
- 多副本、COMMIT 响应丢失、同步复制与主备提升后语义不变;
|
||
- runtime 只能获得启动判定,不能读取隔离证据;
|
||
- 已经 durable running 的工作保留 completion/recovery,不伪装成安全热杀;
|
||
- 不为这一个领域再拆 workspace package,也不把 Cluster 代码带入路由设备 Profile。
|
||
|
||
## 决策
|
||
|
||
### 1. control-core v36 使用永久 overlay
|
||
|
||
`pg-0037-plugin-package-quarantine` 将 capability 从 v35 推进到 v36,并新增:
|
||
|
||
- `plugin_package_quarantine_events`:绑定 revocation receipt、impact、精确 install
|
||
record、双人或 break-glass 主体、reason、mutation 和事件时间;
|
||
- `plugin_package_withdrawal_receipts`:绑定目标、前后 active vector、新 Tool
|
||
snapshot、Task 撤回数量和数据库提交时间;
|
||
- `plugin_package_withdrawal_tasks`:绑定每个 Task 的前一 revision 与 disabled
|
||
revision。
|
||
|
||
事实表只追加,不删除或改写安装历史、旧 Task revision、旧 Tool snapshot、Run、
|
||
StepRun、Artifact 与 Completion。
|
||
|
||
### 2. package-executor 持有唯一提交 authority
|
||
|
||
`commit_plugin_package_quarantine(jsonb,jsonb,jsonb,jsonb)` 是
|
||
`SECURITY DEFINER`,只授予 `ql3_package_executor`:
|
||
|
||
1. 锁定 Project 和 exact install;
|
||
2. 校验 event/receipt target、时间关系、active head 与 materialized revision;
|
||
3. 对 Package 当前拥有且仍 enabled 的 Task 追加 disabled revision;
|
||
4. 重新计算并校验排除目标 lock 后的完整 active source 集;
|
||
5. 发布 immutable Tool snapshot;
|
||
6. 写 event、receipt 和 Task withdrawal relation。
|
||
|
||
任一 identity、source、Task head、snapshot 或 authorization fence 漂移都回滚整个
|
||
事务。event digest 与 target 唯一;COMMIT 响应丢失后通过 durable read-after-write
|
||
收敛为 exact replay。
|
||
|
||
### 3. runtime 只获得同 Project 启动栅栏
|
||
|
||
`ql3_runtime` 对三张隔离表没有 SELECT/INSERT/UPDATE/DELETE,只能执行:
|
||
|
||
- `plugin_package_run_start_allowed(project,task,revision)`;
|
||
- `plugin_package_tool_start_allowed(project,definitionRef,digest)`。
|
||
|
||
guard 通过 Task reconciliation 或历史 Tool snapshot source 回溯 Package lock,并对
|
||
Project 取 `FOR SHARE`;quarantine commit 对同一 Project 取 `FOR UPDATE`。因此新
|
||
Run dispatch 与 Tool barrier 不可能越过并发隔离事务。同步副本缺失且
|
||
`synchronous_commit=remote_apply` 时,行锁 WAL 会使启动型 mutation fail closed;
|
||
不得为提高切换窗口可用性绕过该 fence。
|
||
|
||
Run 已经 durable 处于 dispatching/running 且 Task revision 未变化时,不重新解释为
|
||
新 start,允许既有 completion/recovery 收敛。Package install recovery、Task
|
||
reconciliation recovery 和 Tool snapshot recovery 都排除已有 quarantine event 的
|
||
target/source,避免重启复活。
|
||
|
||
### 4. 管理入口是短生命周期 subpath
|
||
|
||
`@qinglong/cluster-admin/plugin-package-quarantine` 组合 readiness、PostgreSQL
|
||
repository 和事务内授权复验:
|
||
|
||
- 每批 1–128 个 dense、event digest/target 唯一的 event;
|
||
- 每个 repository transaction 在写入前和 COMMIT 前调用授权复验;
|
||
- 使用独立 package-executor credential,完成后关闭数据库;
|
||
- 不进入 cluster-admin root,不新增常驻 HTTP route、listener、timer 或 workspace
|
||
package。
|
||
|
||
它接收的是已经绑定 `revocationReceiptDigest + impactDigest + exact lock` 的事件。
|
||
Cluster 当前安装事实没有保存 OCI publisher signature provenance,因此本 ADR 不允许
|
||
从 revoked key、package 名或 registry 路径猜测受影响 lock。自动影响分析与事件生产
|
||
必须先在后续 D-177 建立不可变 signer provenance。
|
||
|
||
## 不采用方案
|
||
|
||
### 新增 quarantine workspace package
|
||
|
||
该能力与现有 cluster-postgres storage adapter、cluster-admin 短 authority 同生命周期,
|
||
新增包没有独立交付或依赖隔离收益,并违反 22-package hard cap。
|
||
|
||
### 给 runtime 隔离表读取权限
|
||
|
||
runtime 只需要布尔启动判定。直接 SELECT 会暴露管理主体、reason、impact 与安装历史,
|
||
也会让各消费者自行实现不一致 join。
|
||
|
||
### 跨多个事务撤出 Task 和 Tool
|
||
|
||
会出现 Task 已禁用而 Tool 仍可启动、或 event 已存在但 receipt 不完整的可见窗口,
|
||
并使 recovery 无法判断 winner。
|
||
|
||
### 在没有 provenance 时自动按 key 隔离
|
||
|
||
无法证明某个 install lock 确由该 signer 发布;猜测影响集会产生漏隔离或误隔离,
|
||
比显式承认缺口更危险。
|
||
|
||
## 验收证据
|
||
|
||
- `@qinglong/cluster-postgres`:190 pass,1 条需要外部 live URL 的 integration 条件
|
||
skip;migration checksum、schema contract、Drizzle、readiness、恢复与 barrier
|
||
单元门通过;
|
||
- `@qinglong/cluster-admin`:101 pass,1 条真实 Kubernetes API 条件 skip;新增
|
||
batch service 覆盖双次授权、dense/limit/duplicate target 和显式 subpath;
|
||
- PostgreSQL 18.4 Debian arm64 physical-streaming HA Docker contract:
|
||
- 构造一个真实 Task+Tool Package,隔离前 Run/Tool guard 均为 true;
|
||
- COMMIT 响应丢失后 durable replay 收敛,event/receipt 各 1、Task withdrawal 2;
|
||
- 新 Tool snapshot retained source 为 0,runtime 隔离表访问返回 42501;
|
||
- 隔离后及 timeline 1→2 promotion、`pg_rewind`、同步 rejoin 后双 guard 均为 false;
|
||
- 分区提交未被确认、旧主先 fence、同步副本恢复后才重新开放 mutation;
|
||
- 最终 `pluginPackageQuarantine.*` 证据全为 true,`gates.passed=true`。
|
||
|
||
真库门发现并修复了 JSON 运算符优先级、active install/head join、JSON text 到 UUID
|
||
显式转换和 `ECONNRESET` durable retry 四类静态 mock 未覆盖的问题。
|
||
|
||
## 后续
|
||
|
||
1. D-177/ADR-0187 已完成 Cluster Package signer provenance、durable revocation
|
||
receipt/impact、receipt-first Run/Tool deny 与有界 quarantine producer;
|
||
2. 将 ADR-0187 producer 接入正式双人/break-glass Cluster 管理 transport;在该
|
||
ceremony 完成前保持 subpath 为内部短 authority,不发布“任意请求按 key 隔离”的
|
||
产品能力;
|
||
3. 为多 Project 批次补充显式并发/限流策略,但单事务仍只处理一个 Project target,
|
||
不扩大数据库锁范围。
|