mirror of
https://github.com/whyour/qinglong.git
synced 2026-09-20 16:07:11 +08:00
89 lines
4.7 KiB
Markdown
89 lines
4.7 KiB
Markdown
# ADR-0225:PostgreSQL Serializable Plugin Package Workflow 准入
|
||
|
||
- 状态:Proposed
|
||
- 日期:2026-07-30
|
||
- 关联 RFC:QL-RFC-0001 D-03、D-12、D-144、D-207、D-212
|
||
- 关联 ADR:ADR-0156、ADR-0158、ADR-0222、ADR-0223、ADR-0224
|
||
|
||
## 背景
|
||
|
||
SQLite v41 已证明 generation-bound Workflow plan 可以在一个 `BEGIN IMMEDIATE`
|
||
事务中原子展开。Cluster 不能简单把这个实现翻译成若干 PostgreSQL autocommit:
|
||
automation start guard 中的 publisher signer advisory lock、publication/install/
|
||
lifecycle 行锁、quarantine/revocation absence 判断,以及 Run/StepRun 写入必须属于同一
|
||
transaction。否则安全事实仍可能穿越检查与提交。
|
||
|
||
跨方言审计还发现 runtime admission 原先生成约 64 字节 RunEvent ID,而 PostgreSQL
|
||
`run_events.id` 为 `varchar(36)`;plan 的 Run ID 也曾允许 128 字节。公共契约现已
|
||
收紧为最多 36 字节 Run ID,并生成最多 36 字节 admission/Step RunEvent ID;
|
||
StepRun/mutation identity 继续允许 128 字节。这样无需扩大既有 Run 外键闭包。
|
||
|
||
## 决策
|
||
|
||
### 1. 使用同一 SERIALIZABLE transaction
|
||
|
||
Cluster adapter 必须在一个专用 connection 上执行:
|
||
|
||
1. `BEGIN ISOLATION LEVEL SERIALIZABLE`;
|
||
2. durable exact replay lookup;
|
||
3. 调用既有 `plugin_package_automation_start_allowed`;
|
||
4. 在事务内复验 exact publication Workflow 与 materialized Task source digest;
|
||
5. 原子写 Run、全部 StepRun、RunEvent、StepRunMutation、admission/step ledger;
|
||
6. `COMMIT`;serialization failure 作为可安全重试的 unavailable,不在内部无界重试。
|
||
|
||
现有 start guard 取得的 `pg_advisory_xact_lock` 与 `FOR SHARE` 锁会一直保持到该
|
||
transaction 结束。`SERIALIZABLE` 的 predicate conflict 负责拒绝 guard 后插入的
|
||
quarantine/revocation 等 phantom;不能降为默认 Read Committed。
|
||
|
||
### 2. runtime 只获得窄表权限
|
||
|
||
`pg-0045` 只向 `ql3_runtime` 授予 admission 两表的 `SELECT, INSERT`,以及既有
|
||
Run/StepRun execution 写权限;不向 runtime 开放 quarantine、publisher receipt 或
|
||
lifecycle mutation。manager、package-manager、package-executor、worker-ingress 不得
|
||
写 admission ledger。
|
||
|
||
### 3. exact replay 先于 current guard
|
||
|
||
与 SQLite 相同,已提交 exact plan 必须返回原 receipt,即使 Package 后来 withdrawn
|
||
或 quarantined。新的 plan 才检查 current guard。相同 planId/runId/receipt/StepRun/
|
||
event/mutation identity 绑定不同内容时 conflict;COMMIT response loss 只能 inspect
|
||
durable winner。
|
||
|
||
admission ledger 对 runtime 是 append-only(仅 `SELECT, INSERT`);重放读取不得使用
|
||
`FOR UPDATE/FOR SHARE`,否则 PostgreSQL 会要求 `UPDATE` 权限。并发竞争由
|
||
`SERIALIZABLE`、唯一约束和事务级 start guard fence 收敛,不为不可变行扩大权限。
|
||
|
||
### 4. 不在事务内执行外部副作用
|
||
|
||
admission 只展开 durable DAG,不创建 Attempt、不 claim Worker、不调用 Executor。
|
||
后续 caller-driven frontier 在新事务中推进 ready Step,避免长事务、连接占用和锁
|
||
等待随 Task 执行时间增长。
|
||
|
||
## 接受门
|
||
|
||
1. `pg-0045` schema、capability、checksum、权限与 readiness;
|
||
2. mocked repository contract 与真实 PostgreSQL exact replay/collision/guard/
|
||
serialization rollback;
|
||
3. runtime 角色可准入但不能直接读取安全 receipt 或调用管理 mutation;
|
||
4. PostgreSQL 18 physical streaming HA 下 admission ledger、Run/StepRun 和 receipt
|
||
经 `remote_apply`、promotion、旧主 fencing、`pg_rewind`、fresh replicas 保持;
|
||
5. 无 image pull 的 HA 重跑,且不新增 package、依赖、daemon/timer/watcher。
|
||
|
||
## 当前验证
|
||
|
||
1. `pg-0045` 已把 control-core 推进至 v44,并增加两张 admission ledger、受审
|
||
snapshot function、显式约束/索引、Drizzle schema、manifest/checksum 与六角色
|
||
readiness;
|
||
2. PostgreSQL repository 使用同一 connection 的 `SERIALIZABLE` transaction,完整
|
||
durable evidence exact replay 优先于 mutable guard;mock 与 package 全量为
|
||
217 pass、1 条无测试 URL 条件 skip;
|
||
3. runtime 对新表只有 `SELECT, INSERT`,真实故障注入进一步证明 replay 读取不能使用
|
||
`FOR SHARE`;manager/admin/package-executor/worker-ingress 无该 authority;
|
||
4. PostgreSQL 18.4 arm64 physical HA 在
|
||
`QL3_HA_SKIP_IMAGE_PULL=true` 下完整通过:admission、2 个 StepRun、Event/mutation
|
||
与 receipt 经 `remote_apply`、timeline 1→2、旧主 fencing、promotion、
|
||
`pg_rewind` 和 fresh replicas 保持;
|
||
5. Workflow admission 的 atomic、exact replay、publisher revocation fence、
|
||
runtime-only 与 survives-promotion 五项 gate 以及总 `passed` 均为 true;
|
||
6. 后续 frontier 边界见 ADR-0226。
|