디버깅 실전 · 다시 하면 된다는 문제 · 실습
다시 하면 된다는 문제 — 몇 번을 돌려야 고쳤다고 말할 수 있나
목표
간헐 실패의 발생률을 시행으로 재고 추정치에 구간을 붙인다. 실패한 판에서만 참인 조건을 시행 기록에서 뽑아내고, 사이를 벌려 다시 재 관측이 증상을 지우는 것을 확인한다. 마지막으로 고쳤다고 말하려면 몇 번을 돌려야 하는지 계산하고 그 횟수를 실제로 돌려 증명한다.
왜 중요한가
"다시 해 보니 되던데요" 는 조사를 끝내는 문장처럼 들리지만 사실은 자료 두 건이다. 한 번 실패하고 한 번 성공했다. 여기서 할 일은 더 돌려 분수를 만드는 것이다.
간헐 실패가 위험한 이유는 고쳤는지를 한 번의 성공으로 판단하게 만들기 때문이다. 실패율 10%짜리를 안 고친 채 한 번 돌려 통과할 확률은 90%다. 그래서 "몇 번을 연속으로 통과시켜야 하는가" 를 먼저 계산해야 한다.
이 실습에서 어려운 것은 반복문이 아니라 판정의 근거다. 시행 횟수 없이 비율만 적으면 20번 중 2번과 200번 중 20번을 구별할 수 없고, 실패한 판의 증거를 그 자리에서 남기지 않으면 나중에 물을 대상이 없다.
채점기는 무작위에 흔들리지 않는다. 채점기가 만든 결정적인 표적(정확히 다섯 번에 한 번 실패하는 명령)에 여러분의 도구를 돌려 숫자를 대조하고, 여러분이 남긴 기록과 계산은 채점기가 같은 공식으로 다시 계산해 맞춥니다.
단계
1. /root/flaky/gen_flaky.py 를 만들어 실행해 /root/flaky/job.py 를 만드세요.
2. /root/flaky/measure.py 로 job.py 를 200번 이상 돌려 /root/flaky/rate.json 에 실패율과 윌슨 구간을 적으세요.
3. /root/flaky/plan.py 와 /root/flaky/runs_needed.json 으로 신뢰수준별 필요한 연속 통과 횟수를 구하세요.
4. /root/flaky/collect.py 로 시행마다 증거를 남겨 /root/flaky/facts.jsonl 을 만들고, 실패할 때만 참인 것을 /root/flaky/only_when_fail.json 에 적으세요.
5. 시행 사이를 벌려 다시 재고 /root/flaky/observer.json 에 두 측정을 나란히 적으세요.
6. /root/flaky/policy.json 에 재시도 예산과 격리 기준, 초록 판정에 필요한 연속 통과 횟수를 계산으로 정하세요.
7. 고친 판을 계산된 횟수만큼 돌려 /root/flaky/proof.json 에 증거를 남기세요.
8. /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분쯤 걸립니다).
단계 8개
- 간헐 실패하는 작업 손에 쥐기
- 가끔을 분수로 바꾸기
- 몇 번을 돌려야 고쳤다고 말할 수 있나
- 실패한 그 자리에서 증거 남기기
- 관측이 증상을 지운다
- 재시도 예산과 격리 기준 계산하기
- 고쳤다를 근거와 함께 주장하기
- 확률을 한 장으로 보고하기