서비스가 계속 죽어서 메모리 부족으로 보고 두 배로 늘렸다. 덜 죽었지만 여전히 죽었고 파고들어 보니 원인이 둘이었으며 그중 하나만 고쳤던 것이다.
Table of contents
Open Table of contents
같은 증상을 내는 두 원인
첫째는 메모리 부족이었다. 큰 파일을 매 요청마다 읽고 변환하니 사용량이 치솟았고 상한을 넘으면 강제 종료됐다.
둘째는 상태 확인 실패였다. 그 무거운 처리가 도는 동안 다른 요청을 처리하지 못해서 주기적으로 오는 상태 확인이 응답을 못 받고 죽은 것으로 판정돼 교체됐다. 둘 다 결과가 프로세스가 사라지는 것이라 겉으로는 같은 증상이었다.
종료 사유가 가른 것
메모리를 늘리면 첫째는 해소되고 둘째는 그대로여서 덜 죽지만 여전히 죽는 상태가 됐다. 둘을 가른 것은 종료 사유였는데 메모리 부족은 종료 코드에 흔적이 남고 상태 확인 실패는 검사 실패로 교체됐다는 기록이 남는다.
같은 죽음인데 사유가 달랐고 그것을 안 보고 전부 메모리로 취급한 것이 문제였다. 한쪽을 고쳐서 부분적으로 나아진 것이 함정이었는데 전혀 안 나았으면 다시 봤을 텐데 절반이 나아지니 부족했나 보다 하고 더 늘리게 됐다.
겹치고 이어지는 사례들
그 뒤로 비슷한 것을 몇 번 더 만났다. 처리 방식을 동기로 바꾸니 상태 확인은 통과했는데 이번에는 여러 개가 동시에 돌아 메모리가 합산됐고 원인이 해결된 것이 아니라 이동한 것이었다.
세 결함이 겹쳐 하나로 보인 적도 있다. 외부가 나중 단계에서야 값을 발급하는데 우리 수집 범위가 짧고 재수집해도 빈 값을 안 채우는 구조여서 셋 중 하나만 고치면 여전히 안 됐다. 네 단계가 이어진 경우도 있었는데 느린 처리가 시간 초과를 부르고 대체 경로로 넘어간 뒤 그쪽 출력 한도를 넘겨 응답이 잘리면서 파싱이 실패했다.
갈라내는 방법
지금 쓰는 방법이 넷이다. 실패를 유형별로 나눠 세는데 실패 100건이 아니라 시간 초과와 데이터 오류와 외부 거부로 나누면 어디를 고칠지가 비율로 정해진다.
한 번에 하나만 바꾸는 것도 규칙으로 뒀다. 두 가지를 동시에 고치면 무엇이 들었는지 알 수 없으므로 느리더라도 하나씩 한다. 그리고 부분 개선을 의심하는데 고쳤는데 절반만 나아졌으면 덜 고친 것이 아니라 다른 원인이 있는 것일 수 있다.
옮겨 간 원인
가장 헷갈리는 것이 원인이 옮겨 가는 경우다. 고쳤더니 증상이 사라졌는데 실제로는 다른 곳으로 옮겨 간 것일 때가 있다.
앞의 사례에서 처리 방식을 바꾸니 한 문제가 사라지고 다른 문제가 생겼는데 겉으로는 고쳤다가 다른 것이 터진 것으로 보이지만 근본은 요청마다 무거운 작업을 한다는 같은 구조였다. 그래서 고친 뒤에 무엇이 근본이었는지를 한 번 더 묻는데 증상이 사라진 것과 원인이 사라진 것은 다르기 때문이다.
정리
- 원인이 둘이면 하나를 고쳐도 부분적으로만 나아진다
- 부분 개선을 덜 고쳤다로 읽지 말고 다른 원인을 의심한다
- 같은 증상이라도 종료 사유와 오류 유형은 다르게 기록된다
- 실패를 유형별로 나눠 세면 우선순위가 정해진다
- 한 번에 하나만 바꾼다
- 동시에 고치면 무엇이 들었는지 모른다
- 증상이 사라진 것과 원인이 사라진 것은 다르다
- 고친 뒤에 원인이 옮겨 갔는지 한 번 더 본다