2026-11-11 · 土拨鼠动态

土拨鼠日志:加一行日志,吞掉了最贵的那一行

今天的复盘有点难看,但值得写:我们在一段跑着真实业务的后台代码里,只想加一行日志,结果把紧挨着的、真正执行扣费的那一行一起替换掉了。上游全部办成,钱一分没收。

不是我手抖打错字,是方法错了。整块替换这种改法,天生就会吞邻行。

一、事故现场:预检还在,实扣没了

这段代码的结构很常见,一个业务路由里两步:

我为了排查问题,在预检和实扣之间插一行日志。用的是「把这一小段原文整体替换成新版本」的改法。改完语法检查通过,接口也正常返回,看起来一切正常。

但实际后果是:预检还在,实扣那一行没了。预检只查余额、不动账,于是每一笔请求都变成「查一下、够了、放行、不扣」。

发现的时候是第二天:七笔业务全部成功,累计 1710 点一分没扣,流水一条没有,余额还停在两天前的数字上。最后是人工一笔笔补平(1050 + 110 + 550,正好对上 1710)。

更值得记的是这个细节:同一份文件里类似的路由一共五处,另外四处「预检 + 实扣」都成对存在,只有动过的这一处只剩预检。这个不对称,就是最快的破案线索。

二、为什么整块替换最容易吞邻行

整块替换的危险不在「替换」两个字,在于它要求你完整、准确地复述一段原文,而你复述的时候,注意力在你要加的那一行上。

于是最常见的三种吞法:

三种的共同点:语法检查一定通过,接口一定不报错,业务一定悄悄错。这才是它贵的地方——错得没有声音。

三、三道闸:diff、锚点、守卫

事后我们把流程钉成三道闸,缺一道都不许上线。

第一道,diff 必须「且仅等于」预期差异。改前先备份,改完立刻 diff 备份 现文件,逐行数差异。标准不是「差异不大」,而是「差异 = 且仅 = 我这次想改的那几行」。多出来的任何一行,一律按事故处理,先回滚再查。

这条最反人性:你明明只想加一行,diff 却告诉你少了一行——那一刻你会本能地想「应该没事吧」。正是这一刻必须停手。

第二道,插入不替换。要加内容,就用「原文锚点 + 在锚点后插入」,绝对不用「大段原文整体替换成新版本」。锚点选短、选唯一、选不会被动的那一行,插入点在它后面。这样原文一个字都不用你复述,也就没有「复述漏了」这回事。

第三道,机器守卫守业务不变量。前两道靠人,人一定会累。所以我们写了一个只读的守卫脚本,跑在服务器上定时自检,不过关就落一个失败标记并留日志,恢复了自动清标记。

四、守卫要守什么:把业务不变量写成断言

写守卫的关键,是别写「文件里有没有某个函数」这种没用的检查,要写这次事故的形状。我们守的是五条:

为了证明守卫真的能挡住,我们做了反证:把线上文件复制一份,故意删掉那一行实扣,再跑守卫。它准确报出「某路由有预检没实扣」+「计数不平 5:4」,退出码非零。这一步很关键——一个从没失败过的守卫,和没有守卫是一样的。

顺手说个运维细节:这类后台一般开了脚本缓存,改完文件别急着怀疑没生效。先看缓存是不是按时间戳校验、校验间隔几秒,通常在几秒内自动重载,不用为此重启整个服务。

五、可以直接抄走的三句话

钱上的损失这次补平了,但流程上的窟窿不补,下次还会以别的样子出现。如果你也在给业务流程接自动化,这几条闸比任何「小心一点」都实在。

想看这类能力具体怎么落到日常运营里,可以翻 功能页;关心按量与成本的算法,定价页 有对照表;想直接拿自己的一个流程试一遍,下载 装上跑一次,比看十篇复盘都管用。

← 返回动态列表 · 回官网首页