やり直せば通る問題 — 何回回せば直ったと言えるか
한국어 원문으로 표시합니다.
목표
간헐 실패의 발생률을 시행으로 재고 추정치에 구간을 붙인다. 실패한 판에서만 참인 조건을 시행 기록에서 뽑아내고, 사이를 벌려 다시 재 관측이 증상을 지우는 것을 확인한다. 마지막으로 고쳤다고 말하려면 몇 번을 돌려야 하는지 계산하고 그 횟수를 실제로 돌려 증명한다.
왜 중요한가
"다시 해 보니 되던데요" 는 조사를 끝내는 문장처럼 들리지만 사실은 자료 두 건이다. 한 번 실패하고 한 번 성공했다. 여기서 할 일은 더 돌려 분수를 만드는 것이다. 간헐 실패가 위험한 이유는 고쳤는지를 한 번의 성공으로 판단하게 만들기 때문이다. 실패율 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>은 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>은 한 줄에 한 시행씩 trial·exit_code·outcome·elapsed_ms·stderr·stdout 을 적습니다. - 재시도 예산: 한 번 더 시도할 때마다 모두 실패할 확률은 p 의 거듭제곱입니다.
p^(r+1) <= 0.01을 만족하는 가장 작은 r 을 예산으로 씁니다. - 흔한 실수: 시행 횟수를 적지 않기, 실패 0건을 실패율 0 으로 적기, 관측을 붙인 채 재고 사라졌다고 보고하기, 고쳤다는 근거를 한 번의 성공으로 삼기.
- 이 실습의 가정: 신뢰수준 95%와 재시도 예산 기준 1%는 이 실습에서 정한 값입니다. 현장에서는 실패의 비용에 따라 달라집니다.
- 부하 시험을 만들지 마세요. 채점 하나의 예산은 60초이고 파드는 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 에 ## 얼마나 자주인가 ## 실패할 때만 참인 것 ## 관측이 바꾼 것 ## 고쳤다고 말하려면 네 절로 보고하세요.
보고서의 값어치는 결론이 아니라 숫자에 있습니다. 시행 횟수와 실패 횟수, 구간, 필요한 연속 통과 횟수를 모두 적으세요. 관측을 붙였을 때 사라졌다는 사실도 단서이므로 함께 적습니다.