Files
qinglong/docs/adr/ADR-0151-sqlite-postgresql-immutable-plugin-package-materialized-revision-repository.md

193 lines
9.0 KiB
Markdown
Raw Permalink 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-0151SQLite/PostgreSQL 不可变 Plugin Package 物化修订仓库
- 状态:Accepted(双方言仓库、迁移、readiness、共享合同与真实 PostgreSQL 门已实现;
Task 批量发布、全局 Tool snapshot 和 consumer activation 尚未实现)
- 日期:2026-07-25
- 关联:ADR-0136、ADR-0137、ADR-0149、ADR-0150、QL-RFC-0001 D-144/D-145
## 背景
ADR-0150 已把同一 active Package generation 的 Task、Workflow、Prompt 和 Tool 文件
物化为自包含、可规范化的
`qinglong/plugin-package-materialized-revision@v1`。此前只有纯领域 repository port
没有耐久 adapter
- edge/standalone 重启后必须重新打开 staging 才能取得语义;
- Cluster 多副本无法围绕同一 `generationDigest` 收敛;
- consumer 无法在读取 active generation 后取得一份可审计的 exact revision
- 如果分别保存 source、Task、Workflow、Prompt 和 Tool,多行提交会产生同一 generation
的部分可见状态。
耐久化不能成为第二个 active pointer,也不能让 Package manager、常驻 runtime 或
Worker 获得原始 Package 语义写权限。
## 决策
### 1. 不新增 workspace package
repository port 继续位于
`@qinglong/runtime-core/plugin-package-resource-materialization`SQLite adapter 放入
既有 `@qinglong/local-sqlite/plugin-package-materialized-revision`PostgreSQL
adapter 放入
`@qinglong/cluster-postgres/plugin-package-materialized-revision`,并只从 Cluster
`package-executor` 权限入口导出。
两个 adapter 都是显式 subpath,不从 Profile-neutral root 聚合导出。不新增第三方
依赖、连接池、timer、watcher、socket 或常驻缓存;workspace importer 保持 21 个。
### 2. 一代一行,自包含保存
双方言分别新增:
- SQLite `QingLong3PluginPackageMaterializedRevisions`
- PostgreSQL `ql3.plugin_package_materialized_revisions`
每行保存:
- `generation_digest` 主键;
- `project_id``package_name``generation``lock_digest`
- `manifest_digest``revision_digest`
- 完整 canonical `revision_json`
- 数据库观察的创建时间。
`generationDigest` 是 repository identity`project + package + generation` 另有唯一
约束,防止同一代出现不同语义。数据库 CHECK 从 JSON 中复验上述重复 identity
避免索引列与 payload 漂移。只外键到 Project,不外键到可变 install head 或 lock
history:语义修订需要在安装记录回收后仍可作为执行和审计事实存在。
完整 revision 单行保存是刻意选择。repository port 返回一份完整 revision,单行
`INSERT` 可以保证 publish 原子性;若拆为资源明细表,必须再引入 batch commit 标记与
复杂的 incomplete-generation 恢复协议。
### 3. create 或 exact replay
仓库只提供:
- `create(revision)`
- `findByGenerationDigest(generationDigest)`
`create` 先通过 runtime-core normalizer 复验完整 revision。首次写入成功;同一
`generationDigest` 重放时,只有 durable row 规范化后的完整值和摘要均 exact 相同才
返回既有记录。任何 identity、lock、Manifest、source 或语义差异都作为 conflict
失败关闭,不允许 update、delete、upsert overwrite 或“最后写入获胜”。
SQLite 在既有单 operation authority queue 内执行一个
`INSERT ... ON CONFLICT DO NOTHING` 与 exact read,不打开第二连接。PostgreSQL 使用
`INSERT ... ON CONFLICT DO NOTHING RETURNING`,冲突后 exact read;因为一行 append
本身即是原子发布,不要求连接 pinning 或跨网络长事务。
损坏的 durable JSON、数据库不可用和非预期 SQL 错误都映射为 unavailable;受审唯一/
CHECK/FK 约束映射为 conflict。调用方不得把不确定写入透明重放成不同 revision。
### 4. 明确存储与内存上限
ADR-0150 仍限制每个 resource 1 MiB、业务 resource source bytes 总计 8 MiB。
materialized revision 还包含 normalized semantic values、Manifest、lock、generation
和 JSON 编码开销,因此 durable `revision_json` 另设 24 MiB 硬上限,并在:
- runtime-core normalizer
- SQLite CHECK
- PostgreSQL CHECK
三处保持一致。
24 MiB 是一次显式安装/物化操作的最坏输入上限,不是常驻 RSS 预算。repository 不做
目录扫描、后台预热或全表加载;consumer 必须按 active `generationDigest` 精确读取。
### 5. 双方言 schema 与能力版本
SQLite
- `0045-plugin-package-materialized-revisions` 建表和索引;
- `0046-capability-v23` 把 local contract 推进至 v23
- capability 增加 `plugin_package_materialized_revision:1`
PostgreSQL
- `pg-0025-plugin-package-materialized-revisions` 建表、索引和角色授权;
- `control-core` capability 从 v23 推进至 v24
- migration stream 为 25 条,catalog 为 38 张表;
- capability 增加 `plugin_package_materialized_revision:1`
SQLite readiness 同时要求 migration、capability、表和两个业务索引。PostgreSQL
readiness 同时要求 migration、capability、表、索引、CHECK/FK 与精确 ACL。
### 6. PostgreSQL authority 只属于 Package executor
`pg-0025` 撤销 PUBLIC、runtime、admin、package-manager 和 worker-ingress 对新表的
全部权限,只授予 `ql3_package_executor`
- `SELECT`
- `INSERT`
executor 没有 UPDATE/DELETE/TRUNCATE authority。Package manager 继续只负责公开管理
准入;常驻 runtime、Worker ingress 和 admin 不能读取或改写原始物化语义。迁移仍由
短生命周期 `ql3_migration` authority 执行。
### 7. durable revision 仍不是 active pointer 或执行发布
consumer 的顺序固定为:
1. 从 ADR-0149 source 读取一次 active generation
2. 以其 `generationDigest` 精确查 revision
3. 规范化并复验完整 identity;
4. 由后续受审 publisher 执行原子业务发布;
5. 发布前再次确认 active generation 未漂移。
repository 不提供 `current` 查询,不更新 active pointer,也不逐项创建 TaskDefinition、
注册 Tool handler、执行 Workflow/Prompt 或绑定 Secret。仓库完成只关闭“耐久语义事实”
缺口,不关闭“生产执行可见性”缺口。
## Profile 影响
- edge/standalone:复用现有单 SQLite operation authority;无新连接、线程或后台任务。
只有显式 Package 物化/读取操作承担最多 24 MiB JSON 的短生命周期成本。
- cluster:多 executor 可以竞争同一 append;数据库唯一约束和 exact replay 使结果
收敛。RPO-0/切换语义继续由既有 PostgreSQL HA 约束承担。
- worker:不导入 repository,不取得表权限,只消费后续 execution revision 绑定结果。
## 被否决方案
1. **新增 `plugin-package-revision-store` workspace package**:没有独立部署或依赖
生命周期,会继续细碎化 `packages/`
2. **按 Task/Workflow/Prompt/Tool 拆多张表**:当前 consumer 需要完整 revision
多表会引入部分发布和额外恢复协议。
3. **把 revision 塞进 active pointer/ConfigMap**:会突破 pointer 预算并合并文件发布、
语义 authority 与业务激活。
4. **只存 digest、每次从 staging/OCI 重建**:无法给重启和多副本提供耐久 exact
evidence,也把外部制品可用性带入执行读路径。
5. **给 manager 或 runtime 读写表权限**:会把公开 transport 或常驻控制面升级为
原始 Package 语义 authority。
6. **允许 update/delete 以便升级或 GC**:升级本来就应产生新 generation;在引用感知
retention 设计完成前删除会破坏执行与审计事实。
## 验证
- 共享 repository contract:首次 create、exact replay、按 generation digest 查询、
absence
- SQLite:真实 Node 24 `DatabaseSync`、单 operation authority、损坏 JSON
fail-closed、root/subpath 隔离、typed schema/catalog/readiness lockstep
- PostgreSQLSQL 生成、数据库时钟、SQLSTATE conflict、损坏 JSON fail-closed、
package-executor/root entrypoint 隔离;
- PostgreSQL 18.4 arm64 真库:25 条 migration 后完成 revision create、exact replay
与 find
- PostgreSQL 18.4 physical HA`pg-0025`/v24/38 表和角色 readiness 在
timeline 1→2 promotion 前后通过,旧主 fencing、`pg_rewind` 只读同步重入及既有
24 项领域/HA 门全部通过;
- architecture21-package 全量测试、edge import audit 和 cluster dependency audit
必须保持无 finding。
## 后续
1. 设计 Package TaskDefinition 多资源单事务 reconciliation,以 generation 为批次,
防止一代内部分可见;
2. 为全部 active generation 构造一次性 immutable Tool registry snapshotDefinition
与受信 handler/preview publisher 分离;
3. 为 Workflow/Prompt 建立独立版本仓库和执行协议;
4. 建立 Package Secret requirement 到 Project SecretRef 的审批、rotation 和审计
ceremony
5. 在 execution revision/Run 引用建立后,再设计引用感知 retention;当前禁止 GC
durable materialized revision。
上述 Gate 完成前,durable revision 仍不可直接进入生产执行路径。