mirror of
https://github.com/whyour/qinglong.git
synced 2026-09-21 00:17:47 +08:00
193 lines
9.0 KiB
Markdown
193 lines
9.0 KiB
Markdown
# 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-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;
|
||
- 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。
|
||
|
||
## 后续
|
||
|
||
1. 设计 Package TaskDefinition 多资源单事务 reconciliation,以 generation 为批次,
|
||
防止一代内部分可见;
|
||
2. 为全部 active generation 构造一次性 immutable Tool registry snapshot,Definition
|
||
与受信 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 仍不可直接进入生产执行路径。
|