Files
qinglong/docs/adr/ADR-0203-physical-edge-compose-storage-recovery-evidence.md
T

118 lines
6.0 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-0203:物理 Edge Compose 存储恢复候选证据
- 状态:Accepted
- 日期:2026-07-29
- 关联 RFCQL-RFC-0001 D-86、D-87、D-175、D-192、D-193
- 关联 ADRADR-0088、ADR-0200、ADR-0201、ADR-0202
## 背景
ADR-0202 已证明 Compose recovery evidence collection 的逻辑状态机、崩溃重放和
保留底线,但开发机与容器测试不能回答两个物理 Edge 问题:
1. collection 在真实路由设备分区上会产生多少可观测写入;
2. durable rename stage 跨 Linux 重启后,正式 commit/replay 是否仍能收敛并回收
snapshot 占用。
这两个问题也不能直接等同于“突发断电已通过”。Linux block statistics 可能在重启
后重置,partition sectors 不是 eMMC/NAND FTL 内部写入量;普通 reboot 也不能证明
电源在某条 fsync 指令之后被物理切断。
## 决策
### 1. 记录器是两阶段、短生命周期工具
根命令 `evidence:physical-edge-compose-storage` 提供 `prepare``resume`。它是
repository script,不新增 workspace package、生产依赖、daemon、timer、watcher、
端口或后台扫描。
prepare 在 operator 提供的专用数据挂载内创建一个可丢弃 Edge scratch deployment
通过正式 local deployment API
1. 准备 Compose Edge deployment 并扩展 SQLite allocated pages
2. 推进 generation 2、3、4,创建 3 份真实 rollout backup 与 terminal receipt
3. 对最老 backup 执行 production evidence collection prepare
4. 把该 backup rename 为生产协议的确定性 `.ql3-collection-stage` 并 fsync 目录;
5. 发布 `0600`、no-replace、SHA-256 绑定的 session,然后停止并要求 operator
外部重启。
工具永不调用 reboot、poweroff 或设备管理接口。
### 2. 专用真实块设备和路径边界
data path 必须是 canonical、当前 UID 拥有、`0700`、读写的 Linux mountpoint
mount source 必须直接解析为 `/dev/*` block deviceroot filesystem、overlay、
容器 bind path、共享生产 deployment 和 symlink 均不接受。
session 只接受 exact shape,并绑定:
- device manifest digest、UID、boot ID、架构、文件系统、mount 与 block device
- 固定的 `.ql3-compose-storage-<session UUID>` scratch deployment
- generation 4、collection UUID、最老 rollout UUID 和 snapshot 完整事实;
- production prepare/commit command 的 exact shape、相同 options 和单调时间;
- target/stage 的确定性绝对路径及 prepare barrier tree。
resume 会再次 realpath 并检查 data root、deployment root、session 和 output 均未逃逸
专用挂载。即使当前用户重算 session digest,也不能把 production commit 指向任意
其他 deployment。
### 3. 跨启动只形成两个独立写入上界
prepare 在同一 boot 内记录操作前后 partition `sectors written` 差值;session 的
最终发布发生在 barrier 之后,因此不包含在 prepare 数值内。resume 在新 boot 内
重新取起始计数,并只对 production commit、exact replay、SQLite inspect 和结果
检查后的差值计量;它不包含 boot 自身写入。
两个差值分别除以本阶段逻辑 snapshot bytes,并向上取整为 permille 上界。不得跨
boot 相减,也不得把该数值命名为 whole-device 或 FTL write amplification。manifest
显式声明两个独立阈值,超出任一阈值即候选失败。
### 4. Resume 必须使用正式恢复路径
resume 只接受不同 boot ID、相同 UID/device/filesystem/architecture。它执行 session
内绑定的 production collection commit,再原样重放同一 commit,并验证:
- 首次结果为 `collected`,重放为 `existing`
- 主 SQLite 仍通过 v35 snapshot inspection
- deterministic stage 已消失,content-bound tombstone 已存在;
- 3 份 rollout backup 收集最老一份后正好保留 2 份;
- deployment tree allocated bytes 至少回收一页,且算术与 target snapshot
bytes 一致。
报告以 `0600`、no-replace、SHA-256 发布,永久为 `supported:false`
### 5. 聚合器不得扩大结论
统一 physical Edge aggregator 只导入同 device/Profile/architecture/filesystem 和
当前 resume boot 的私有 digest-bound 报告。成功时新增
`compose_sqlite_collection_reboot_and_partition_write_upper_bound`,但仍保留:
- `power_loss_restart`
- `whole_device_cpu_wakeups_and_flash_write_amplification`
- release signature 与其他尚未完成的物理证据。
报告明确不证明真实突发断电来源、生产主 deployment、FTL/NAND 写放大、长期耐久、
MTD/UBI/UBIFS 设备写放大、Compose restore replacement 的断电窗口或
Standalone/Cluster。MTD/UBI 需要独立计量适配器,不能套用 block-sector 阈值。
## 验收证据
- 新 recorder manifest/CLI/block-stat/session/report 契约测试 6/6
- 聚合器路径、私有文件、不同 boot/device binding 与不扩大断电声明测试 12/12;
- 合计专项 18/18
- session 的 deployment/path/production command 漂移即使重算 SHA-256 仍失败关闭;
- 没有新增 workspace package 和生产依赖。
本地 macOS 开发机没有执行物理阶段。Accepted 表示协议与门禁已实现,不表示任何具体
路由设备已经获得支持;真实报告必须在固定 arm/arm64 设备、专用可丢弃分区上采集。
## 被拒绝的替代方案
- **在 Docker Desktop 或 loop file 上采数字**:不能代表设备 block/FTL 路径。
- **把 MTD/UBI 当作 `/dev` block device**:观测与磨损语义不同,必须独立设计。
- **跨 boot 直接相减 `/sys/class/block/*/stat`**:计数可能重置,结果无效。
- **脚本自动 reboot/poweroff**:扩大破坏权限,仍不能证明突发断电时点。
- **复用生产 deployment**:会污染用户恢复历史,并把证据工具变成生产 GC authority。
- **从 `process.io` 或 SQLite 文件大小推导闪存写放大**:观测层级错误。
- **新增 storage-evidence package/daemon**:没有独立交付边界,并增加 Edge 常驻成本。