mirror of
https://github.com/whyour/qinglong.git
synced 2026-09-20 16:07:11 +08:00
129 lines
7.1 KiB
Markdown
129 lines
7.1 KiB
Markdown
# ADR-0309:部署侧 Legacy Silence Commitment 与 adopted v3 启动门
|
||
|
||
- 状态:Accepted(Docker legacy-stop slice)
|
||
- 日期:2026-08-09
|
||
- 关联 RFC:QL-RFC-0001 D-05、D-06、D-17、D-63、D-64、D-65、D-85、D-87、D-259
|
||
- 关联 ADR:ADR-0064、ADR-0065、ADR-0066、ADR-0178、ADR-0194、ADR-0243、ADR-0308
|
||
|
||
> ADR-0310 已在同一 deployment product 中补齐 Docker target start/restart barrier、
|
||
> barrier 后 inspect-only 和 terminal `manual_required`。本 ADR 的 legacy-stop commitment
|
||
> 仍是每一代 target controller 的前驱事实。
|
||
|
||
## 背景
|
||
|
||
QingLong 3.0 已有旁路 SQLite adoption/activation、独立 Local Application、fresh deployment
|
||
与 systemd/OpenRC/Compose 描述符,但 adopted v1 仍可直接启动。source SQLite write fence 只能
|
||
证明一个数据库 writer 被阻塞,不能证明 2.x Scheduler、子进程、网络和其他外部副作用已经
|
||
静默。因此旧操作手册中的“先自行停止 2.x”不是可执行安全边界,D-64 也一直保持 Proposed。
|
||
|
||
ADR-0243 删除孤立的 `local-cutover` workspace package 是正确的:它没有 binary、部署消费者或
|
||
controller。现在 `@qinglong/local-owner-cli` 已经拥有真实 `ql3-local-deploy` 短生命周期产品、
|
||
私有 command-file 协议、Docker socket controller 和 durable file publication primitive,切换
|
||
能力应成为其 `src/deployment/cutover/` 内部 domain,而不是恢复第 20 个 package。
|
||
|
||
## 决策
|
||
|
||
### 1. 先关闭可证明的 Docker legacy-stop slice
|
||
|
||
新增 `local.deployment.cutover.legacy-stop` 与
|
||
`ql3-local-deploy cutover-legacy-stop`。命令精确绑定:
|
||
|
||
- deployment root、Docker executable 与 Unix socket;
|
||
- cutover ID、Profile、target instance ID;
|
||
- 私有 SQLite activation 文件及 reviewed activation digest;
|
||
- activation 对应的 canonical legacy source path 与预期容器内 database path;
|
||
- 完整 64-hex legacy container ID 和请求时间。
|
||
|
||
controller 不接受裸 PID、容器名、tag、调用方布尔值或自报“已停止”。它先验证 activation exact
|
||
shape/digest/Profile,再对精确 container ID 执行 `update --restart=no`、`stop --time 30` 和
|
||
`inspect`。只有 `Running=false`、`Restarting=false`、`Paused=false`、`Pid=0`、状态为
|
||
`exited|dead` 且 restart policy 为 `no`,并且唯一 bind mount 把 activation 的 source path
|
||
精确映射到预期容器内 database path 时,才承认 database writer 与该容器拥有的外部副作用
|
||
已静默。source path 必须先与 activation 的 `sourcePathDigest` 相等;container ID、
|
||
created/name/image、mount source/destination/RW 的低敏 identity digest 与 Docker endpoint digest
|
||
一并进入 commitment。
|
||
|
||
当前不接受 systemd/OpenRC legacy 或 Kubernetes/remote cluster evidence。无法证明的 controller
|
||
必须失败关闭,不能用人工 assertion 降级。
|
||
|
||
### 2. journal 有界、不可覆盖且可精确重放
|
||
|
||
每个 cutover 使用当前 UID 私有 `0700` 目录,最多保留 64 个 cutover:
|
||
|
||
```text
|
||
service/cutovers/<cutoverId>/
|
||
├── 0001-legacy-stop-requested.json
|
||
└── 0002-legacy-stopped.json
|
||
```
|
||
|
||
记录为 `0600`、最大 64 KiB,由现有 stage→fsync→hard-link no-replace→directory fsync primitive
|
||
发布。第一条记录在任何 Docker mutation 前完成;第二条绑定第一条 digest。崩溃发生在 stop 前或
|
||
期间时,原命令可幂等重做 legacy 收敛;第二条已存在时只验证 exact identity/digest 并返回
|
||
`existing`,不重新打开 socket,也不重复 stop。目录漂移、跨 cutover 重放、activation/endpoint/
|
||
container 漂移或 retention 超限全部失败关闭。
|
||
|
||
### 3. adopted application v3 强制消费 commitment
|
||
|
||
新增 `qinglong/local-application-process@v3`,只允许 `storage.mode=adopted`,并要求:
|
||
|
||
```text
|
||
cutover.cutoverId
|
||
cutover.commitmentPath
|
||
cutover.expectedCommitmentDigest
|
||
```
|
||
|
||
Application 自己只读取并验证 commitment,不导入 Owner CLI、Docker 或 cutover controller,也不
|
||
停止 2.x。commitment 必须精确绑定 config 的 cutover ID、Profile、instance ID 与 activation
|
||
digest,并通过 payload digest、私有文件身份和 authority-path 去重。验证发生在 AI 选择、Plugin
|
||
Package source、signal subscription、SQLite/Secret/Recovery 和 lifecycle 之前。
|
||
|
||
fresh v2 不需要 commitment,行为不变。adopted v1 保留离线解析兼容,但生产进程会在取得任何
|
||
runtime authority 前返回稳定的 `QL3_LOCAL_APPLICATION_CUTOVER_COMMITMENT_INVALID`,因此不能
|
||
成为绕过门。
|
||
|
||
### 4. 资源与 Profile 边界
|
||
|
||
本能力不新增 workspace package、依赖、daemon、timer、watcher、listener、数据库连接或历史扫描。
|
||
Edge 与 Standalone 只在一次人工 cutover 命令中支付三个 Docker 调用和两个小文件;常驻 Application
|
||
只增加一次 16 KiB 上限私有 JSON 读取和 SHA-256。它适用于低配 Docker 路由器/NAS,也不会让本机
|
||
文件/Docker authority 进入 Cluster、Worker 或 PostgreSQL 产物。
|
||
|
||
### 5. D-64 仍未全部关闭
|
||
|
||
本 ADR 关闭的是“没有 legacy silence evidence 仍可启动 adopted target”的产品旁路,以及
|
||
`legacy_stop_requested -> legacy_stopped` 的可恢复前半段。以下仍是独立 Gate:
|
||
|
||
- `manual_required` 的只读诊断、人工 resolution 与新 cutover ceremony;
|
||
- systemd/OpenRC legacy controller;
|
||
- systemd/OpenRC target controller 与 target 显式 stop;
|
||
- adopted Compose target 的受审 create/config 与真实 Docker live/crash Gate;
|
||
- target 产生新 3.0 事实后的数据对账式回退;
|
||
- cluster/Kubernetes 独立 cutover authority。
|
||
|
||
因此 Application active 仍不等于完整 deployment cutover completed,任何路径仍不得自动重启 2.x。
|
||
|
||
## 否决方案
|
||
|
||
1. **恢复 `@qinglong/local-cutover` package**:没有新增独立 artifact/dependency/权限或版本责任,只会
|
||
重新制造已删除 importer;拒绝。
|
||
2. **只依赖 SQLite `BEGIN IMMEDIATE`**:不能证明 Scheduler、spawn、网络或外部系统静默;拒绝。
|
||
3. **接受 operator boolean/PID/container name**:不可稳定绑定 owner,也无法阻止重放和 PID/name
|
||
复用;拒绝。
|
||
4. **由 Application 调 Docker 或停止 legacy**:形成稳态 runtime 与部署 authority 循环,并让低配
|
||
设备常驻 socket 权限;拒绝。
|
||
5. **一次 shell 串联 stop 后直接 start**:崩溃后无法判断副作用是否发生;拒绝。
|
||
6. **把本机 marker 用于 cluster**:本机文件和 Docker endpoint 不是 Kubernetes/PostgreSQL 的租约、
|
||
fencing 或 quorum evidence;拒绝。
|
||
|
||
## 验收
|
||
|
||
1. 精确 Docker stopped evidence 发布 commitment,完全相同命令返回 `existing` 且不再调用 Docker。
|
||
2. running/restarting/PID/restart-policy/container identity 任一不满足时只保留 intent,不发布
|
||
commitment。
|
||
3. v3 commitment 的 shape、digest、cutover/Profile/instance/activation 任一漂移,Application 在
|
||
signal/storage 前失败。
|
||
4. adopted v1 不能启动,fresh v2 行为不变。
|
||
5. package/dependency/boundary/artifact 审计证明 package 数仍为 19,Owner cutover 不进入 Application
|
||
或 Cluster production closure。
|
||
6. Edge/Standalone 常驻路径没有新增 timer、watcher、socket、数据库连接或历史扫描。
|