文件
hyb-oa/.trellis/tasks/08-28-production-arm-backup-transfer-incident/implement.md
T

9.5 KiB
原始文件 Blame 文件历史

生产到 ARM 备份传输中断实施计划

Phase 0:规划与授权

  • 创建 P1、operations Trellis 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.sh 74 项通过;生产 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,ARM arm_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.json summary。私有探针错误复用了 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-incident
  • node scripts/qa/check-trellis-governance.mjs
  • node scripts/qa/check-process-contract.mjs --baseline --json
  • node scripts/qa/audit-docs.mjs --check-registry
  • node scripts/qa/check-sensitive-content.mjs
  • git diff --check