- name
- same-second-correlation-is-not-cause-remove-the-condition
- description
- 행·크래시·타임아웃을 추적하다 시스템 로그에서 '같은 초에' 일어난 사건을 찾으면 그게 원인처럼 읽힌다 — 두 번 반복되면 확신이 된다. 하지만 확정은 '그 조건을 없애면 증상이 사라지는가'로만 온다. 그 제거 실험이 대개 코드 0줄·10분인데도, 하기 전에 '원인 확정'이라고 쓰게 된다. 트리거 - 시스템 로그에서 타임스탬프가 일치하는 이벤트 발견, '이게 원인이다' 라고 쓰기 직전, 설정 하나로 조건을 없앨 수 있는데 안 해봤을 때, 같은 이슈에서 가설이 연달아 틀렸을 때.
- version
- 1.0.0
- task_types
- ["debugging","ops","review"]
- triggers
- [{"pattern":"시스템/커널 로그에서 증상과 초 단위로 일치하는 이벤트를 찾았을 때"},{"pattern":"'원인 확정' · '진범' 이라고 쓰려는 순간"},{"pattern":"설정/환경변수 하나로 그 조건을 제거해볼 수 있는데 아직 안 했을 때"},{"pattern":"같은 조사에서 가설이 두 번 이상 반증됐을 때"}]
- category
- trap
# 같은 초에 일어났다고 원인은 아니다 — 조건을 없애 봐라
## 어떻게 속는가
행(hang)을 추적하면 로그가 넘친다. 그러다 증상과 **초 단위로 일치하는** 시스템 이벤트를 찾으면
뇌가 즉시 인과로 읽는다. **두 번째 사례에서도 맞으면 확신이 된다.** 그리고 그 확신을 이슈·인수인계·
메모리에 *"원인 확정"* 으로 적는다 — 다음 사람이 그 위에 수정을 얹는다.
실제 사례: 서브프로세스가 `openat` 에서 영원히 블록했고, **정확히 같은 초에** 응답되지 않은 OS
동의 프롬프트(TCC `AUTHREQ_PROMPTING`, REPLY 없음)가 pending 이었다. 두 번의 장애에서 **둘 다**
그랬다. 메커니즘도 그럴듯했다. **전부 틀렸다** — 그 프롬프트를 0건으로 만들어도 행은 그대로였다.
**진범은 그 상관을 *설명하는* 제3의 원인이었다.** 프로세스가 autofs 자동마운트 경로
(`/home/...`)를 열고 있었고, 그게 오토마운터에 영원히 붙잡힌 것이다. TCC 프롬프트는
같은 접근의 **부산물**이었다 — `SystemPolicyNetworkVolumes` 가 지키는 게 정확히 그런 경로다.
⇒ **상관이 정교할수록 "둘 다 같은 원인의 결과"를 먼저 의심해라.**
## 규칙
> **확정은 상관의 정교함이 아니라 *제거*에서 온다.**
> "X 가 원인이다" 라고 쓰려면 **X 가 없는 상태에서 증상이 사라지는 걸 봐야 한다.**
제거 실험은 보통 생각보다 훨씬 싸다. 위 사례는 **환경변수 하나**(`WORKSPACE_ROOT`)를 바꿔
프롬프트 발생 자체를 없앴다 — **코드 0줄, 10분.** 그걸 하기 전에 "원인 확정"을 써 버린 게 실수였다.
**착수 전에 물어라: "이 조건을 없애는 가장 싼 방법은?" 답이 한 시간 안이면 쓰기 전에 해라.**
## 제거 실험을 설계할 때
* **변수를 하나만 바꿔라.** 위치를 옮기면서 `.git` 도 같이 넣으면 결과를 못 읽는다
* **조건이 정말 없어졌는지 먼저 재라.** 증상이 아니라 **조건**을. (여기선 "NetworkVolumes 이벤트 0건")
* **변경이 실제로 반영됐는지 재라.** launchd 는 `kickstart -k` 로 plist 를 **안 읽는다** —
bootout/bootstrap 후 `ps -Ewwo` 로 프로세스 env 를 직접 확인
* **되돌릴 방법을 먼저 적어라.** prod 설정을 건드리는 실험이면 특히
## 계측기가 죽어 있으면 상관조차 못 본다 (macOS)
* **`log` 는 zsh 빌트인이다.** `log show …` 는 `too many arguments` 로 조용히 죽고 **rc=0 으로 0줄**을
뱉는다. **`/usr/bin/log` 절대경로**를 써라. 양성 대조군(전체 줄 수)을 안 세면 반대로 오판한다
* **`lsof -p PID -i` 는 AND 가 아니라 OR 다.** `-a` 를 넣어라. 안 넣으면 **다른 프로세스의 소켓**이 섞인다
* **Debug 레벨 로그는 이틀만 남는다.** 아카이브가 한 달치여도 그렇다 — *"이전엔 없었다"* 는
**보존 한계이지 관측이 아니다**
* **행 난 프로세스는 죽기 전에 심문해라.** `lsof -a -p` · `sample` · `ps -Ewwo`(env 전량)는
살아 있는 동안에만 얻어진다
## 관련
[[a-check-that-cannot-flip-is-not-measuring-anything]] ·
[[a-timeout-names-the-waiter-not-the-cause-read-the-other-end]] ·
[[audit-findings-need-remeasurement-before-acting]]
在 GitHub 查看