一个账户,余额100。十个工人,每人取走10。每个工人都做了最明显的事:读余额、减掉10、写回去、记下发生了什么。最终余额停在90,九笔取款消失。

这部分是竞态条件,每个后端工程师都遇到过。我想谈的是审计日志。

打开网易新闻 查看精彩图片

十行记录,每一行都写着余额从100变成90。没有缺口,没有空值,没有孤儿外键。模式校验器、校验和、夜间对账任务都不会标记它。

事故中把这张表交给我,我会把它读成“一笔取款被重试了十次”。我会去查重试逻辑。重试逻辑没问题。我会在那里耗掉一个下午。

我们在受监管的支付领域。审计日志对我们来说不是调试用的便利工具。它是证据本身。审计员读它,争议处理拿它当依据,如果FCA问起一笔支付怎么变成这个状态,我们递上去的就是它。

所以我们要求它的标准不是“看起来是否合理”,而是“这是不是真实发生过的记录”。

而那次实验里的失败,通过了我所知道的针对日志能写的每一项检查。

想想你实际能自动检查什么:

这些问题每一个都在问日志是否自洽,没有一个在问日志是否真实。十个进程都以为自己是独自操作的,每个都忠实地写下了自己看到的世界。当十个诚实的叙述者都拿着同一份错误信息时,你得到的恰恰就是自洽

要发现它,你需要一行跟另一行不一致的记录。但没有。不一致才是信号,而这个bug把信号删掉了。

我周一发过一篇关于没人检查存活状态的护栏的文章。一条停止运行的lint规则,一个什么都匹配不到的策略检查,一个跳过一半语料却只报告跑完那半的评估任务。

两件事的形状是一样的。一个从不触发的检查和一个从不自相矛盾的日志,读出来的结论完全相同:一切正常。没有投诉被当成了健康的证据,而本该产生投诉的机制,恰恰是坏掉的那部分。

你没法靠更仔细地检查输出来修复这个问题。输出是干净的,这正是问题所在。