재시작할 때마다 자기가 처음인 줄 알았다
알림 봇 하나가 “설치 성공”을 계속 보냈다. 중복 방지는 분명히 넣어뒀다. 넣어뒀는데도 계속 왔다.
같은 소식이 계속 왔다
봇은 스스로 업데이트한다. 새 버전을 받으면 자기를 갱신하고, 끝났다고 알린다. 알림에는 당연히 중복 방지가 붙어 있다 — 이미 알렸으면 다시 안 보낸다.
그런데 같은 문장이 계속 왔다. 로그를 봐도 중복 방지 코드는 멀쩡히 그 자리에 있었다.
가드를 지우는 게 가드가 막으려던 행위였다
원인은 표시를 어디에 뒀느냐였다.
“이미 알렸다”는 표시가 프로그램 메모리 안에 있었다. 그런데 봇은 자기를 업데이트하면 재시작한다. 재시작하면 메모리가 비워진다. 그러니까 알림을 보낸 직후에 그 기록이 통째로 사라졌다.
봇 입장에서는 매번 처음이었다. 매번 처음이니까 매번 알렸다.
08:18 fix(bridge): 자기업데이트 재진입 가드를 디스크로
— 재시작이 가드를 지워 스팸 폭풍
수리는 한 줄이다. 표시를 메모리가 아니라 디스크에 둔다. 재시작해도 안 지워지는 자리로 옮긴 것뿐이다.
왜 이걸 못 봤나
코드만 읽으면 이 결함은 안 보인다. 중복 방지 로직 자체는 정확하다. 조건도 맞고 순서도 맞다.
안 맞는 건 그 코드가 사는 세계의 수명이다. 이 프로그램은 자기 수명을 스스로 끊는 종류였고, 표시는 그 수명 안에 갇혀 있었다. 코드를 아무리 들여다봐도 “이 프로세스가 곧 죽는다”는 사실은 코드에 안 적혀 있다.
같은 주에 비슷한 걸 하나 더 만났다. 배포됐는지를 확인하는 검사기가 “설치 로그가 남았는가”를 보고 있었는데, 어떤 기계에서는 파일이 개별로 연결돼 있어서 새로 고친 파일만 조용히 안 붙었다. 로그는 성공이라고 적혀 있고, 실제로 도는 건 옛날 코드였다. 그래서 판정 기준을 로그가 아니라 “지금 도는 것과 원본이 같은 내용인가” 로 바꿨다.
두 건의 모양이 같다. 상태를 간접 증거로 판정하고 있었다는 것.
배운 것
어떤 동작을 한 번만 하게 막으려면, 그 표시는 그 동작이 파괴하지 못하는 곳에 둬야 한다. 재시작을 유발하는 작업의 중복 방지를 메모리에 두는 건 자기 무효화다. 코드가 아니라 코드가 사는 자리의 문제라 리뷰로는 잘 안 걸린다.
그리고 “했다”는 기록과 “돼 있다”는 상태는 다른 값이다. 로그·성공 코드·설치 이력은 전부 “했다” 쪽이다. 상태를 알고 싶으면 상태를 직접 읽어야 한다. 이번 주에 잡은 결함 두 개가 모두 여기서 나왔다.
가드는 있었다. 다만 가드가 자기가 막으려던 행위에 같이 쓸려가고 있었다.
— 강대종 / @ssamssae