| Invalid Date
字数 0阅读时长≈ 1 分钟
type
Post
status
Published
date
Oct 9, 2026
slug
summary
迁移清点若只按文件类型扫,会漏掉「领域配置里的相对路径」这一类。
tags
Write
路径迁移
静默故障
排障方法
DSH 排障现场
category
DSH
icon
password
url

一、一份本该废弃的目录,自己回来了

前一阵我刚做完 Vault 的顶层重构。编号层、同步镜像层、配置层分清楚,原先堆在顶层的内容整体下沉到内容总层里,所有引用路径做了一次全量迁移。跑完之后我抽查了几处,写入正常,目录结构干净,就当作这件事结束了。
第二天晚上做例行兜底核对时,我发现一个不该存在的目录又出现了:那个重构前用的旧顶层路径,重建时间正好是迁移当天的深夜,之后还在被持续写入。
更刺眼的是里面那份运动计划文件,只有 3 KB。而正位——也就是重构后应该写入的那份——是 10 KB。
3 KB 意味着什么,我打开一看就明白了:手写的那几段全没了。当前状态、当前目标、复出分级、目标重估、力量与预防、配速区、变更记录,一整块一整块地不见,只剩脚本自动生成的那部分骨架。
第一反应是「手写内容被覆盖了」。这个判断是错的,后面会说为什么。

二、先别急着恢复,先问一句「谁在写」

遇到「文件被改坏」,本能是去翻备份。但备份只能救回内容,救不回原因——只要写它的那条路径还在,恢复完第二天还会再坏一次。
所以我先做的是定位写入方。判断链很短:旧目录不是被人手动建的,它的重建时间和某个定时任务对得上;那个任务每次跑完都会刷新运动档案;刷新用的路径是「一个配置根变量 + 一个相对路径」拼出来的。
顺着这条线去看那份领域配置,问题就在一行上:里面的运动目录键,写的还是重构前的旧相对路径,缺了内容总层这一层前缀。
于是每次任务跑完,脚本都老老实实把档案写到旧位置去。它没有报错,没有告警,甚至回执都是成功的。

三、迁移扫的是文件,漏的是「键」

这次漏迁的根因,值得单独说。
我做全量路径迁移时,清点方式是按存放位置扫的:插件配置扫一遍,profile 补丁扫一遍。这两类都覆盖到了,也确实都改干净了。
但还有第三类:领域配置里的相对路径。它既不在插件配置里,也不在补丁里,而是某个业务模块自己的配置文件中的一项。这类键的特点是——它不指向某个具体文件,而是指向一个目录,由脚本在运行时拼接。按文件类型去 grep,它长得不像路径,很容易被跳过。
换句话说,我当时是按「文件长什么样」在扫,而不是按「这个键被谁读、拼成什么」在扫。前者只能覆盖你想到的形态,后者才能覆盖所有会被消费的路径。
漏掉任何一类存放位置,后果都不是报错,而是静默回写:新位置照常工作,旧位置悄悄复活,两边同时存在,谁都不觉得自己有问题。

四、手写内容其实没丢,只是被写到了两个地方

回到那个「被覆盖」的误判。
脚本写档案时的逻辑是:目标文件存在就更新其中的自动块,不存在就按模板新建一份。旧位置那份是脚本自己刚造出来的,模板里本来就没有手写正文——所以它看起来「空」,看起来像手写段被抹掉了。
而手写原文一直在正位那份 10 KB 的文件里,一个字都没少。
真正的问题不是丢失,是分裂:同一份档案被劈成两个目录,正位有前几天的推送,旧位有当天晚间的推送,两边各写各的。如果没有及时发现,越往后越难判断哪份才是真的。
这也解释了我当时为什么先看到 3 KB 就慌了——体积异常是最先跳出来的信号,但它指向的原因,和直觉往往不是一回事。

五、修复顺序:先断源,再合流,最后验证

定位清楚之后,处理是三步。
第一步改配置,把那个键补回完整前缀,改之前先备份一份。这一步是断源——不改它,后面做什么都白搭。
第二步合流。旧位置那几份推送里,有正位缺的晚间段,把它们并回正位同名文件;整份缺的直接复制过去。这一步要小心的是别反向覆盖:正位那份才是内容更全的,合并方向只能是旧位补进正位。
第三步重跑同步脚本,看回执。回执显示只更新了课表块和状态块,记录数和课表数都对得上——说明脚本认出了文件已存在,走的是更新分支而不是新建分支,手写段因此完好无损。
到这里才算闭环:源断了、内容合了、脚本行为符合预期了。

六、我后来固定下来的两条做法

这件事之后,我调整了两个习惯。
一是迁移清点按「键的存放位置」分类,不按文件类型扫。 至少要覆盖三类:插件配置、领域配置、profile 补丁。前两类通常会被想到,第三类最容易漏,因为它藏在业务模块自己的配置里,形态上还不像路径。
二是用「正位 vs 旧位的体积与段落完整性」当低成本回归探针。 不需要写复杂的校验,只要在例行核对时看一眼:正位文件是不是明显偏小、手写段是不是成块消失、旧位置是不是又出现了。体积异常和段落缺失这两个信号,比日志更早暴露问题——因为静默回写根本不会写进日志。
几条可以带走的判据:
  1. 迁移完成后,抽查「写入是否正常」是不够的,要专门查「有没有旧位置被重新创建」。
  1. 路径迁移的覆盖面取决于清点维度。按文件形态扫只能覆盖想到的形态,按消费方扫才能覆盖全部。
  1. 文件体积骤降通常不是内容丢失,而是写到了另一份上——先找第二个位置,再谈恢复。
  1. 修复顺序永远是断源、合流、验证,跳过第一步的恢复都是临时的。
  1. 静默失败比报错危险,因为它伪装成成功。给关键写入配一个「旧位置是否复活」的检查,成本极低。
我当时的判断错在把「体积变小」直接等同于「内容被覆盖」,跳过了「谁在写、写到哪」这一步。现在回头看,那 3 KB 不是损失,是提示。
Loading...
目录