9.5 KiB
9.5 KiB
生产到 ARM 备份传输中断实施计划
Phase 0:规划与授权
- 创建 P1、
operationsTrellis task,记录来源事件和与恢复演练的阻塞关系。 - 固定只读调查、生产/ARM 写操作、手工补跑和测试告警为不同审批边界。
- 用户审阅并批准本规划及补充项后运行
task.py start;该批准只授权进入本地实施,不自动授权连接远端。
Phase 1:本地只读调查准备
- 从
scripts/run-automatic-backup.sh、receiver、dead-man 和 systemd installer 提取真实单位名、状态路径来源、阶段和退出码合同,禁止手写近似常量。 - 实现只输出 allowlist 的跟踪只读采集器:生产本地完整恢复点合同、本地成功/失败清理保留语义、partial 匿名字节比例、rsync 原始码、无法取得的传输时钟、两端容量;timer/service 继续复用既有只读 systemd 聚合合同,不在 Python 中另写一套。
- 为本地完整/无完整批次、成功清理/失败保留、相近/不同停止偏移、rsync
23/30/12/11/255、缺失持续时间、权限拒绝、畸形 manifest 和敏感输出补合成回归。 - 本地语法、合同、敏感扫描全部通过后,提交远端只读调查摘要与精确权限边界,单独申请执行批准。
本地准备结果(2026-08-28)
- 跟踪调查器:
scripts/qa/inspect-backup-transfer-incident.py;仅接受显式绝对路径参数,不回显参数值,未知异常也只输出稳定类别。 - 可复跑合同:
scripts/qa/run-backup-transfer-incident-contract.sh;初始 19 项合成检查通过;现场分类缺陷修复后扩展为 20 项,fixture 与 Python cache 零残留。权限拒绝不会被误写成“无有效备份”,其他阶段的旧日志不会冒充本轮 rsync 诊断;生产多个批次时只哈希最新元数据合格候选,最新哈希失败不回退扫描旧批次;停止偏移必须同时满足 5% 和 64 MiB 双阈值。 - 既有
run-automatic-backup-contract.sh74 项通过;生产 wrapper 未改。现状没有持久化传输专属开始/结束时刻,故持续时间必须为NOT_AVAILABLE。 - rsync 原始码只在安全摘要明确为
phase_offhost_transfer_failed时从 root 私有last-error.log固定正则提取,避免把 paired-backup/finalize 的旧诊断错误归因给传输。
Phase 2:获批的远端只读调查
- 在不修改生产/ARM 的前提下采集四类证据;每个临时入口在同轮精确清理并由外层独立复核零残留。
- 先判定“生产单点”或“无已证明可用备份”,再交叉判定主要根因、次要候选、清理/累积语义和容量预算;无法取得的字段标为
NOT_AVAILABLE/NOT_MEASURED。 - 记录过渡恢复点决策;只读证据无法收敛时停止扩读,提交一次受控观测性重传的独立审批方案,不直接执行。
- 更新本 task 证据,给出单一最小修复方案、回滚方案和生产写操作清单;停止并请求用户审阅。
远端只读调查结果(2026-08-28,追加时间线)
- 公网
/ready调查前后均 PASS,12 位构建前缀均为7b86ffb3c9ef;生产固定入口与 staging 经外层独立枚举均为 0,ARMarm_independent_residue=0。调查未改变在线 OA、正式归档、timer 或业务数据。 - 生产:
complete_local_recovery_point=yes、backup_risk_classification=production_single_point;完整哈希通过的最新批次 1 个、元数据完整批次 7 个,最新候选年龄 42885 秒、聚合 11400180 bytes;生产文件系统 82% 使用、10 GiB 可用。源码顺序证明传输失败保留完整本地批次、异机成功后 prune、生成临时对象在失败路径清理,故过渡动作是wait_natural。 - 生产最近 daily/watchdog service 均为
success/0、timer active;历史失败摘要未保留可绑定到本轮的 rsync 原始码或传输专属时钟,因此退出码和时长均为NOT_AVAILABLE,不得用 service exit、mtime 或对话时刻替代。 - ARM 原始输出:partial 2 个,聚合 2682168 bytes,样本总字节分别为 2682168 与 0;调查器当时输出
arm_partial_stop_offset_class=similar。该原始结果不回写。 - 分类缺陷修订:旧实现以
max(64 MiB, 5%)为接近上限,使 2.68 MB 与 0 字节被 64 MiB 阈值错误吞并。按固定阈值必须同时满足的合同,人工重算为different;一个约 32 日龄 partial 满足完整合同但未 finalize,另一个约 36 小时龄 partial 为零字节,不能据此声称两次都停在同一确定性偏移。 - ARM 正式原子归档 7 个、无无效归档,最新完整归档年龄 45756 秒;inbox/archive 文件系统 54% 使用、89 GiB 可用;dead-man service
success/0、timer active、最近状态PASS_OFFHOST_DEADMAN_FRESH/healthy。链路已自然恢复一个新鲜原子批次,可解除恢复演练的事件阻塞,但 P1 仍需第二个连续自然周期及容量验收后才能结案。 arm_deadman_alert_state_finding_count=invalid不表示远端状态损坏。仓库 helper 明确把 alert state 写成仅含schemaVersion/status/category的去抖状态;findingCount属于last-deadman.jsonsummary。私有探针错误复用了 summary 读取模板,后续证据不得把这个表示缺陷升级为告警链 finding。- 独立 finding:ARM health service 为
failed/1,但公网/ready实测前后一致且 PASS。这是健康监控执行/状态/通知链问题,不是 OA 当前不可用的证据;本次授权不覆盖进一步调查或修改。 - 根因裁决:历史单一 rsync 原始码和可靠传输时长不可得,两个 partial 的真实停止位置明显不同,现有证据不能收敛为单一根因。不得把自然恢复一次冒充“已实施修复”;后续若再失败,候选动作是另行批准的一次受控观测性重传。生产 10 GiB 余量仍需独立容量方案。
分类缺陷复盘
- 根因类别是“隐含假设 + 测试覆盖缺口”:实现用
max(absolute, relative)把两个独立上限误写成二选一宽松上限;旧正例夹具只证明“绝对差异较小”,没有证明相对差异也在 5% 内,因而为错误实现背书。 - 修复机制已闭环:实现改为
min(...);合同直接覆盖现场2682168/0、同时满足双阈值的正例、分别越过相对/绝对阈值的负例;稳定 spec 写明公式、错误写法与断言点。仓库搜索确认该分类公式只有这一个实现位置,不存在需要同步的第二套逻辑。 - 原始远端
similar保留,人工修订追加为different;以后任何现场分类缺陷都按同样的不可变时间线处理,不回写旧证据。
Phase 3:本地修复与变更审批
- 仅修改证据支持的脚本/installer/合同;不轮换已通过初始认证的密钥,不放宽 manifest/finalize 门禁。
- 容量方案优先扩容或精确处置已确认的自动备份自有对象,证明不会触碰唯一恢复点、数据库或附件源。
- 运行自动异机备份、receiver、dead-man、故障注入、回滚和敏感输出合同。
- 列出生产/ARM 的每个写目标、旧值指纹、候选 SHA、执行/回滚命令和停止条件,单独申请实际变更批准。
Phase 4:获批的实际修复
- 执行最小变更和容量处置;不做未批准的手工备份、timer 修改、partial 清理或测试告警。
- 若另获手工补跑批准,只运行一次并记录真实 rsync 码、时长、原子 finalize 和残留;失败不自动重试。
- 验证至少一个新原子批次、manifest/三文件/字节/SHA-256、三组 service exit
0和容量预算。
Phase 5:两个自然周期观察
- 自然周期 1:daily 新原子批次 PASS;watchdog/dead-man service success;记录生产/ARM 容量、ARM partial 数量/聚合字节趋势和适用的告警响应锚点。
- 自然周期 2:重复同一组断言;不以手工补跑或旧批次替代;ARM partial/容量趋势不得继续恶化。
- 两个周期均通过后标记事件
GO,否则保留失败并返回 Phase 2/3,不临时放宽阈值。
Phase 6:解除阻塞与收口
- 在
08-23-isolated-backup-restore-drill追加本任务结果交叉引用并解除事件阻塞;恢复演练仍需新批次身份探针和单独实际恢复审批。 - 运行 task validate、Trellis governance、process baseline、docs registry、敏感扫描和
git diff --check。 - 中文提交引用
08-28-production-arm-backup-transfer-incident;不自动 push,不归档未满足两个自然周期的任务。
停止条件
- 只读操作将输出路径、身份、凭据、原始日志/JSON 或业务内容。
- 需要新增 sudo、读取超出 allowlist 的 root 状态或改变生产/ARM 状态但尚未获独立批准。
- 清理对象、配置目标、旧值或回滚路径不唯一。
- 新原子批次不完整、service 非零、容量低于预算或任一自然周期失败。
- 告警发送/人工确认锚点缺失时不得补造响应延迟。
验证命令
bash -n/python3 -m py_compile覆盖新增或修改的运维脚本。- 运行自动备份、receiver、dead-man 和相关敏感输出合同。
python3 ./.trellis/scripts/task.py validate .trellis/tasks/08-28-production-arm-backup-transfer-incidentnode scripts/qa/check-trellis-governance.mjsnode scripts/qa/check-process-contract.mjs --baseline --jsonnode scripts/qa/audit-docs.mjs --check-registrynode scripts/qa/check-sensitive-content.mjsgit diff --check