再试一次就好了
一句话总结
间歇性失败不是“偶尔发生的事”,而是尚未测量的概率,而且一旦测量,要跑多少次才能说已经修好,也就随之确定了。
为什么需要它
“我再试了一下,又好了。”这句话一出现,大多数调查就在这里停下。无法复现,就没法修复;没法修复,也就不会留下记录。下周同样的事情再次发生,又得从头开始。
但是这句话里其实已经包含了一条数据:一次失败,一次成功。也就是说,两次试验中有一次失败。当然,凭这个还不能说“失败率 50%”,因为试验只有两次。所以接下来要做的事是确定的——多跑几次。
间歇性失败之所以格外危险,还有另一个原因:它会让人用一次成功来判断是否修好。一个失败率为 10% 的问题,在没有修复的情况下跑一次就通过了,那么明明什么都没做,看起来却像是修好了。这种情况的概率是 90%。
工作原理
1. 写下试验次数。失败率永远是一个分数。“偶尔失败”这种说法,既没有分子也没有分母。跑 200 次失败 24 次,就是 0.12。
2. 给估计值加上区间。200 次中 24 次和 20 次中 2 次,都是 0.1,但可信程度不同。求比例区间的方法有好几种,在失败为 0 次或样本很小时也不会失效的,常用的是威尔逊得分区间(Wilson score interval)。重要的是,失败为 0 次时区间的宽度也不会变成 0——看到 0 次,并不意味着概率是 0。
3. 当场留下失败那次运行的证据。间歇性失败无法再次召唤出来。如果每次试验都把退出码、标准错误、耗时、与上一次运行的间隔写进文件,那么只在失败的那次运行中才成立的条件,就会在表格中自行浮现出来。没有这份记录,“当时显示的是什么来着”就没有可以询问的对象。
4. 计算需要跑多少次。对失败率为 p 的问题,在没有修复的情况下跑 n 次全部通过的概率是 (1-p) 的 n 次方。要把这个概率压到 1-c 以下,需要满足下式。
(1 - p)^n <= 1 - c 양변에 로그를 취하면
n >= log(1 - c) / log(1 - p)
p=0.25, c=0.95 -> n >= 10.4 -> 11번
p=0.10, c=0.95 -> n >= 28.4 -> 29번
p=0.02, c=0.95 -> n >= 148.3 -> 149번
该代码块中的韩文说明依次为:两边取对数之后得到 n >= log(1 - c) / log(1 - p);p=0.25、c=0.95 时 n >= 10.4,即 11 次;p=0.10、c=0.95 时 n >= 28.4,即 29 次;p=0.02、c=0.95 时 n >= 148.3,即 149 次。
这张表说明的事情很清楚:问题越罕见,说“已经修好”所需的试验次数就越多。对失败率为 2% 的问题只通过一次就说修好了,等于什么也没说。
在现场相遇的样子
第一,观测会改变症状。加上日志、挂上调试器、一步一步地运行,症状就消失了。依赖时序的问题,一旦变慢,条件就不成立了。这不是问题消失了,而是观测方法改变了条件,而症状消失这件事本身就是线索。如果放慢运行就消失,原因就在速度或顺序上。
第二,用重试来掩盖。重试并不是错误的应对。只是要确定重试次数,就需要失败率。如果失败率是 25%,允许重试三次,那么四次全部失败的概率是 0.4%。如果不做这个计算,只写“先试三次”,运维文档里就混入了一个没有依据的数字。
第三,不做隔离。一个不稳定的测试(不稳定测试,flaky test)如果每十次就阻塞一次整个部署流水线,人们最终会学会忽视失败的习惯。这种习惯同样会用在真正的失败上。所以在修好之前要把它隔离,并留下被隔离的清单和隔离日期。
第四,修好的依据只是一次成功。前面的计算就是为此而存在的。应当先确定修复后需要连续通过多少次,再把跑完这个次数的记录附在报告里。
第五,不写测量条件。同样的任务,连续运行和隔开一段时间运行时失败率不同,这本身就是指向原因的数据。但如果报告里只写“失败率 25%”,下一个人在自己的条件下看到的是 0%,就会以“无法复现”结案。必须连同试验次数一起写明是怎么跑的,这个数字才能被再次使用。
把以上内容压缩成一句话就是:处理间歇性失败,首先是在制造分数,然后才是找 bug。有了分子和分母,之后的决定——允许重试几次、是否隔离、能不能说已经修好——就全都变成了计算。没有分数,这些决定就全都成了个人喜好。
实际工作中真正重要的事
- 把“偶尔”变成分数。没有分母的失败率不是数据。
- 0 次不等于 0%。要把区间一并写下。
- 证据只能在失败的那一刻收集。每次试验都要记录。
- “已经修好”的主张,必须附有经过计算的试验次数。
下一项实验要做什么
拿到一个偶尔失败的任务,用试验次数和失败次数测出失败率和区间,并从试验记录中提取只在失败的那次运行中才成立的条件。拉开间隔重新测量,亲眼看到观测会让症状消失,再通过计算确定重试预算和隔离标准。最后把修复版按计算出的次数跑完,连同依据一起主张“已经修好”。