重试就能过的问题 — 要跑多少次才能说已修好
目标
通过试验测出间歇性失败的发生率,并给估计值加上区间。从试验记录中提取只在失败的那次运行中才成立的条件,拉开间隔重新测量,确认观测会让症状消失。最后计算要说已经修好需要跑多少次,并实际跑完这个次数来证明。
为什么重要
“我再试了一下,又好了”听起来像是结束调查的一句话,其实是两条数据:一次失败,一次成功。接下来要做的是多跑几次,做出分数。 间歇性失败之所以危险,是因为它会让人用一次成功来判断是否修好。一个失败率为 10% 的问题,在没有修复的情况下跑一次就通过的概率是 90%。所以必须先计算“需要连续通过多少次”。 本实验中难的不是循环,而是判定的依据。如果只写比例而没有试验次数,就分不清 20 次中的 2 次和 200 次中的 20 次;如果不当场留下失败那次运行的证据,之后就没有可以询问的对象。 评分器不会被随机性左右。评分器会让你的工具去运行它做出的确定性目标(恰好每五次失败一次的命令)并核对数字,而你留下的记录和计算,评分器会用同样的公式重新计算后进行比对。
步骤
- 创建并运行 /root/flaky/gen_flaky.py,生成 /root/flaky/job.py。
- 用 /root/flaky/measure.py 把 job.py 运行 200 次以上,并在 /root/flaky/rate.json 中写下失败率和威尔逊区间。
- 用 /root/flaky/plan.py 和 /root/flaky/runs_needed.json 求出各个置信水平所需的连续通过次数。
- 用 /root/flaky/collect.py 在每次试验中留下证据,生成 /root/flaky/facts.jsonl,并把只在失败时才成立的内容写入 /root/flaky/only_when_fail.json。
- 拉开试验之间的间隔重新测量,把两次测量并排写入 /root/flaky/observer.json。
- 通过计算,在 /root/flaky/policy.json 中确定重试预算、隔离标准,以及判定为绿色所需的连续通过次数。
- 把修复版按计算出的次数运行,并在 /root/flaky/proof.json 中留下证据。
- 用 /root/flaky/summary.json 和 /root/flaky/flaky_report.md 分四节进行报告。
参考
- 任务约定:
python3 /root/flaky/job.py [--fixed] [--state <디렉터리>] [--seed <정수>](占位符依次为目录、整数)成功时输出一行 JSON 并以退出码 0 结束,失败时输出一行标准错误并以退出码 3 结束。 - 测量约定:
python3 /root/flaky/measure.py --cmd "<명령>" --trials <횟수> [--gap-ms <밀리초>] --out <json>(占位符依次为命令、次数、毫秒数、JSON 文件)输出一个包含 command、trials、failures、rate、ci_low、ci_high、gap_ms 的 JSON。--gap-ms是每次试验之前的休息时间——第一次试验之前也要休息。因为不知道在它之前运行的是什么。 - 威尔逊得分区间(95%,z=1.96):中心是
(p + z²/2n) / (1 + z²/n),半宽是z/(1 + z²/n) × √(p(1-p)/n + z²/4n²)。即使失败为 0 次,宽度也不会变成 0。 - 计划约定:
python3 /root/flaky/plan.py --rate <실패율> --confidence <신뢰수준>(占位符依次为失败率、置信水平)输出一个包含 rate、confidence、runs 的 JSON。runs是满足(1-p)^n <= 1-c的最小整数,即向上取整的log(1-c)/log(1-p)。 - 收集约定:
python3 /root/flaky/collect.py --cmd "<명령>" --trials <횟수> [--gap-ms <밀리초>] --out <jsonl>(占位符依次为命令、次数、毫秒数、JSONL 文件)每行记录一次试验的 trial、exit_code、outcome、elapsed_ms、stderr、stdout。 - 重试预算:每多尝试一次,全部失败的概率就是 p 的幂。取满足
p^(r+1) <= 0.01的最小 r 作为预算。 - 常见错误:不写试验次数,把 0 次失败写成失败率为 0,带着观测去测量后报告症状消失,把一次成功当作修好的依据。
- 本实验的假设:95% 的置信水平和 1% 的重试预算标准,是本实验约定的值。在现场,它们会随失败的代价而变化。
- 不要编写负载测试。每次评分的预算是 60 秒,Pod 为 2 核。第 5 步的慢速测量 20 次就够了(因为有间隔,大约要花 1 分钟)。
拿到一个间歇性失败的任务
创建并运行 /root/flaky/gen_flaky.py,生成 /root/flaky/job.py。然后手动多运行几次 job.py,亲眼看到它时好时坏。
把这个脚本原样保存并运行即可。连续运行生成的 job.py 十次左右,退出码为 0 的运行和退出码为 3 的运行会混杂出现。先不要统计多少次失败一次——那是第 2 步的事。
把“偶尔”变成分数
创建 /root/flaky/measure.py,把 job.py 运行 200 次以上,并在 /root/flaky/rate.json 中写入 command、trials、failures、rate、ci_low、ci_high、gap_ms。区间是 95% 的威尔逊得分区间。
失败按退出码不为 0 来计数。只写比例,就分不清 20 次中的 2 次和 200 次中的 20 次,所以要把试验次数一并留下。威尔逊区间的两个公式在参考一节中——即使失败为 0 次宽度也不会变成 0,这就是使用这个区间的理由。
要跑多少次才能说已经修好
创建 /root/flaky/plan.py,并在 /root/flaky/runs_needed.json 中写入 observed_rate、包含置信水平 0.9、0.95、0.99 各自的 runs 的 table,以及 chosen(0.95)。
在没有修复的情况下,n 次全部通过的概率是 (1-p) 的 n 次方。把这个概率压到 1-c 以下的最小 n 就是答案,取对数就能一行算出。别忘了向上取整——不能跑小数次。
在失败的当场留下证据
用 /root/flaky/collect.py 运行 60 次以上,生成 /root/flaky/facts.jsonl,并在 /root/flaky/only_when_fail.json 中写入 trials、failures、exit_codes_on_failure、exit_codes_on_success、token、failure_trials。token 是存在于所有失败的标准错误中、且不存在于任何成功中的词。
间歇性失败无法再次召唤出来。每次试验都要把退出码和标准错误写在那一行里,之后才有可以询问的对象。用眼睛浏览失败记录,token 马上就能看到——它是一个大写的词。成功记录中不能出现这个词,它才算 token。
观测会让症状消失
把试验之间的间隔拉开到 2000 毫秒以上,重新测量 20 次以上,并在 /root/flaky/observer.json 中写入 fast 和 slow 两次测量(trials、failures、rate、gap_ms)以及 changed。放慢测量时,失败必须是 0 次。
加上日志并一步一步地运行,症状消失是常有的事。因为依赖时序的条件,一旦变慢就不成立了。症状消失这件事本身就是线索——它意味着原因在速度或顺序上。用 measure.py 的 --gap-ms 就能造出同样的效果。
计算重试预算和隔离标准
在 /root/flaky/policy.json 中写入 observed_rate、confidence(0.95)、green_runs_required、retry_budget、quarantine_after。retry_budget 是满足 p^(r+1) <= 0.01 的最小 r,quarantine_after 是大于等于 1 的整数。
重试并不是错误的应对,但次数必须有依据。知道失败率,就能计算“全部失败的概率”,预算就是从这里得出的。如果不做隔离,人们会学会忽视失败的习惯,而这种习惯同样会用在真正的失败上。
连同依据一起主张“已经修好”
把修复版(--fixed)运行第 3 步所计算次数以上,并在 /root/flaky/proof.json 中写入 command、required、trials、failures。failures 必须为 0,trials 必须大于等于 required。
通过一次不是证据。因为对失败率为 p 的问题,在没有修复的情况下通过的概率本来就是 1-p。把第 3 步的 chosen.runs 原样取来,跑那么多次,并把记录存成文件。评分器会亲自再运行同样的命令。
把概率汇总成一页纸进行报告
在 /root/flaky/summary.json 中写入 trials、failures、rate、ci_low、ci_high、token、green_runs_required、proof_trials、observer_changed,并在 /root/flaky/flaky_report.md 中分四节进行报告:## 얼마나 자주인가、## 실패할 때만 참인 것、## 관측이 바꾼 것、## 고쳤다고 말하려면(韩文,依次意为“多久发生一次”“仅在失败时成立的事实”“观测改变了什么”“如何才能说已经修好”)。
报告的价值不在结论,而在数字。试验次数和失败次数、区间、所需的连续通过次数都要写下来。加上观测后症状消失这件事也是线索,所以一并写下。