Files
qinglong/docs/adr/ADR-0314-docker-target-stop-and-reconciliation-evidence.md
T

149 lines
8.1 KiB
Markdown
Raw 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-0314Docker Target Stop 与写后 Reconciliation 证据
- 状态:Accepted
- 日期:2026-08-09
- 关联 RFCQL-RFC-0001 D-05、D-17、D-63、D-64、D-65、D-259
- 关联 ADRADR-0064、ADR-0065、ADR-0309、ADR-0310、ADR-0313
## 背景
ADR-0310/0313 已实现 target start/restart、`manual_required` 和新 ceremony 授权,但没有受审
target stop。更重要的是,现有 start evidence 只证明 config、legacy commitment、activation 和
legacy source 的 bind mapping,没有证明真正可写的 3.0 target SQLite、recovery 和 adoption
manifest 映射。若不先关闭这个身份缺口,停止后比较 target 内容没有可信数据对象。
ADR-0064 已明确:只有 target 仍等于 activation 的初始 SHA-256,且 source 仍等于 recovery
快照时,才可能进入未写回退;target 一旦写入,系统不得自动重启 2.x 或声称无损 rollback。
编辑前 GitNexus 显示 target normalizer/Application binding 各为 LOW、1 个直接运行入口;target
container evidence 为 LOW、2 个直接调用、0 processinstance head advance/manual evidence 与 CLI
入口也都是 LOW。没有 HIGH/CRITICAL 风险。
## 决策
### 1. Start/restart 先绑定完整 adopted 数据面
target start/restart command 增加三个宿主机 authority path
```text
targetDatabasePath
recoveryPath
manifestPath
```
它们与 legacy source、activation、Application config 必须互不相同且都是 supervisor-safe canonical
absolute path。Application v3 config 必须提供对应的 container-side `storage.targetPath`
`storage.recoveryPath``storage.manifestPath`。Docker inspect 必须证明每项均存在唯一 read-write
bind mapping;这些 mount 与 config/commitment/source/activation 一起进入
`targetApplicationBindingDigest`。缺失或漂移在 start barrier 前失败关闭。
此处选择 read-write 是当前 adopted Application 的实际 authoritytarget SQLite 必须写,activation
acquisition 会读取 recovery/manifest。未来若 runtime 将后二者收窄为 read-only 独立 mount,需要新
contract version,不得静默放宽或改变 v1 digest。
### 2. Target stop 是显式一次性 deployment 命令
新增:
```text
ql3-local-deploy cutover-target-stop
```
命令只接受当前实例 lineage head 的 exact active generation。它先重读并验证该 generation 的
start/restart request 与 `target_active`,再 inspect 当前 target,要求 container identity、Application
binding 和 active journal 完全一致。随后固定追加:
```text
4g+1 target_stop_requested | manual_required
4g+2 target_stopped | manual_required
```
这两个 sequence 与下一代 restart recheck 使用同一位置,因此 stop 和 restart 不能并发各自成功。
stop barrier 后执行 exact container 的 `update --restart=no``stop --time 30` 和 stopped inspect。
stop 是安全收敛副作用,barrier 后崩溃可以幂等重做 stop-and-verify;响应丢失时以 inspect 为准。
无法证明 stopped、identity/binding 不变或 restart=no 时进入 terminal `manual_required`
成功后实例 CAS head 从 `target_active` 变为 `target_stopped`。原 start/restart command 不得从 stopped
head 恢复,stop 终态原样重放不再打开 Docker socket。
### 3. Stopped 后生成有界只读数据证据
只有 exact stopped container 已证明后,deployment owner 才以固定 64 KiB buffer 流式读取 target 和
legacy source 主文件;不打开 SQLite connection、不执行 checkpoint/DDL、不创建副本。两文件必须是
当前 UID、private、单 link、canonical regular file。读取固定为
`lstat -> O_NOFOLLOW open -> fstat(before) -> 同一 descriptor hash -> fstat(after)`,前后
device/inode/mode/link/uid/size/mtime/ctime 任一变化都放弃分类。target path digest/device/inode 还必须与
activation 一致。
为了不把未 checkpoint 的事实误判为“未写”,任一 `-wal``-shm``-journal` sidecar 都视为
非空数据风险。journal 只保存布尔比较结果、文件 identity digest 和总 evidence digest,不保存路径、
表内容或原始错误。
### 4. 三类 disposition 不等于 rollback authority
```text
rollback_candidate
target main SHA-256 == activation.targetSha256
target sidecars absent
source main SHA-256 == activation.recoverySha256
source sidecars absent
reconciliation_required
target main digest 已变化,或 target 存在 SQLite sidecar
manual_review
文件/activation/稳定身份无法证明,或 target 未写但 source 不再等于 recovery
```
`rollback_candidate` 只表示“数据证据允许进入后续回退 ceremony”,不是自动重启 legacy 的授权。
本命令永远不启动 2.x、不覆盖 target、不把 target 数据回灌 source。`reconciliation_required` 必须由
后续按数据域导出/冲突清单/明确选择的流程处理;`manual_review` 不允许自动推断。
### 5. 资源与源码边界
实现继续位于现有 `@qinglong/local-owner-cli/deployment/cutover`,新增 stop contract/coordinator 和
数据 evidence 内部模块。没有新增 package、production dependency、数据库连接、timer、watcher、
daemon 或常驻制品模块。每次 stop 最多新增两条小 journal record,单 cutover 仍受 64 文件上限;
文件 hashing 使用固定 buffer,适合低内存路由器,集群节点不加载该本机 authority。
## 被否决方案
1. **只验证 container running/stopped**:不能证明正在停止的是绑定正确 target 数据的实例,拒绝。
2. **只比较 target 主文件而忽略 WAL**:可能把已提交但未 checkpoint 的 3.0 事实误判为未写,拒绝。
3. **stop 成功后直接启动 legacy**:target 可能已有新事实,违反 ADR-0064,拒绝。
4. **把 target 覆盖回 source**:方向错误且会破坏两份恢复资产,拒绝。
5. **为比较数据打开 writable SQLite/checkpoint**:诊断本身会改变证据,拒绝。
6. **新建 workspace package**:没有独立 deployable、依赖闭包或 consumer,拒绝。
## 验收证据
- cutover 专项 16/16,覆盖 start/restart/manual lineage、脱离 target writable mount 的失败关闭,以及正常
stop、写后 classification、SQLite sidecar、source drift、barrier crash recovery 和 stop unknown terminal。
- `@qinglong/local-owner-cli` 完整回归 126/12616-package clean build/test 退出 0。
- package boundary schema v5 为 16/16、787 source、25 root、762 nested
`singleSourcePackages=[]``shallowSourcePackages=[]``findings=[]`。新增源码全部位于既有
`deployment/cutover/`,没有新增 package 或 production dependency。
- Edge import、Cluster dependency、Cluster deployment 均为 `compatible=true`。十档真实
pack/install/import/RSS audit 全部 compatible:最小 Edge/Standalone 为
3,623,093/3,623,129 bytes、331 files、49 modules;最大 Edge/Standalone Application AI 为
6,108,149/6,108,281 bytes、492 files、109 modules;全部 RSS delta 在各自预算内。
- GitNexus 刷新为 43,805 nodes/99,719 edges/1,722 clusters/274 flows。stop coordinator、数据 evidence、
stop/run normalizer、container evidence 与 instance head advance 均为 LOW,最多 3 个直接调用、14 个
impacted symbol、0 affected process。`detect_changes` all/compare `develop` 为 12/31 与 14/34,均为
low/0 process;当前 QL3 孵化树仍未完整进入 Git baseline,因此 change detection 只作补充证据,不替代
上述完整 package、边界和制品门。
- 本批不改 SQL、migration、PostgreSQL/Cluster runtime 或部署资源,因此不重复生成 PostgreSQL HA
物理晋升证据。
## 未完成
- `rollback_candidate` 的双阶段 legacy restart ceremony
- `reconciliation_required` 的数据域清单、export、冲突裁决和受审回灌;
- Keyv、日志、配置、Secret keyring 等多资产 backup/reconciliation manifest
- systemd/OpenRC target stop controller
- adopted Compose live create/config 与真实 Docker crash gate
- Cluster/Kubernetes 独立 cutover authority。
本 ADR 关闭 Docker target 显式停止、完整 adopted mount identity 和保守写后分类,但不宣称无损自动
rollback 或 QingLong 3.0 整体完成。