9.0 KiB
ADR-0151:SQLite/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-corecapability 从 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 的顺序固定为:
- 从 ADR-0149 source 读取一次 active generation;
- 以其
generationDigest精确查 revision; - 规范化并复验完整 identity;
- 由后续受审 publisher 执行原子业务发布;
- 发布前再次确认 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 绑定结果。
被否决方案
- 新增
plugin-package-revision-storeworkspace package:没有独立部署或依赖 生命周期,会继续细碎化packages/。 - 按 Task/Workflow/Prompt/Tool 拆多张表:当前 consumer 需要完整 revision, 多表会引入部分发布和额外恢复协议。
- 把 revision 塞进 active pointer/ConfigMap:会突破 pointer 预算并合并文件发布、 语义 authority 与业务激活。
- 只存 digest、每次从 staging/OCI 重建:无法给重启和多副本提供耐久 exact evidence,也把外部制品可用性带入执行读路径。
- 给 manager 或 runtime 读写表权限:会把公开 transport 或常驻控制面升级为 原始 Package 语义 authority。
- 允许 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; - PostgreSQL:SQL 生成、数据库时钟、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 门全部通过; - architecture:21-package 全量测试、edge import audit 和 cluster dependency audit 必须保持无 finding。
后续
- 设计 Package TaskDefinition 多资源单事务 reconciliation,以 generation 为批次, 防止一代内部分可见;
- 为全部 active generation 构造一次性 immutable Tool registry snapshot,Definition 与受信 handler/preview publisher 分离;
- 为 Workflow/Prompt 建立独立版本仓库和执行协议;
- 建立 Package Secret requirement 到 Project SecretRef 的审批、rotation 和审计 ceremony;
- 在 execution revision/Run 引用建立后,再设计引用感知 retention;当前禁止 GC durable materialized revision。
上述 Gate 完成前,durable revision 仍不可直接进入生产执行路径。