배포는 이틀 전에 끝났고 그때부터 매트릭은 정상이었다. 그날 구 캐시 갱신 배치를 제거하고 잔존 캐시를 정리하자, 외부 광고 응답이 멈췄다. 그제야 알았다 — 이틀 전 배포한 변경점은 사실 적용되지 않은 채 잠재해 있었다.
타임라인
11-25 에 새 캐시 모듈로 교체하는 배포가 끝났다. 모니터링은 정상이었고 사용자 트래픽도 흔들리지 않았다. 그러나 패키지 버전 잠금 실수로, 새 모듈의 코드는 들어왔지만 의존성은 구 버전이 그대로 잠긴 채 실행되고 있었다. 시스템은 구 캐시 키를 보고 있었고, 구 캐시는 별도 배치로 여전히 갱신되고 있어 동작에 이상이 없었다.
11-27 18:20 에 구 캐시 갱신 배치를 제거했다. TTL 이 살아있는 동안은 구 캐시가 남아있었지만, 18:39 에 잔존 캐시를 수동으로 삭제하자 그 즉시 구 캐시 미스가 시작됐다. 새 캐시 키 쪽에는 데이터를 채워줄 주체가 없었으므로 광고 데이터를 가져오지 못했다. 18:40 부터 외부 광고 응답이 멈췄고, 19:23 에 외부 SSP 제보로 인지, 20:05 에 복구됐다.
근본 원인
배포가 끝났다는 사실과 변경점이 적용됐다는 사실은 다르다. 그날 이전까지 모니터링은 “배포가 끝났고 시스템은 정상이다” 만 보고하고 있었다. “새 모듈로 정말 전환됐는가” 는 어떤 매트릭에도 잡히지 않았다.
이번 경우 검증의 단서는 명확했다. 새 캐시 키의 조회 비율이 0 이 아니어야 했고, 구 캐시 키의 조회 비율은 0 에 수렴해야 했다. 11-25 배포 직후에 이 둘을 비교하기만 했어도 잘못된 상태를 즉시 발견할 수 있었다. 그러나 그런 매트릭은 없었다. 시스템이 “구 캐시로 정상 동작 중” 인 상태와 “새 캐시로 정상 동작 중” 인 상태가 매트릭 수준에서 구분되지 않았다.
발견을 더 늦춘 두 가지 부수 요인도 있었다. 광고 데이터가 비었을 때도 시스템이 200 응답을 반환하던 버그, 내부 지면 Fallback 이 알람 신호를 함께 흡수한 점. 이 둘이 합쳐져 캐시 미스 직후에도 대시보드는 평온해 보였다. 그러나 근본 원인은 아니었다. 진짜 원인은 그보다 이틀 앞선 시점에 이미 자리잡고 있었다.
회고
배포 후 모니터링은 “주요 매트릭이 그대로인가” 보다 “변경점이 의도대로 반영됐는가” 가 우선이라고 봤다. 의존성 버전 해시, 새 캐시 키 조회 비율, 구 캐시 키 조회 비율 — 어떤 형태든 변경 전과 후를 매트릭으로 분리해서 볼 수 있어야 한다. 그것이 없으면 잘못된 상태는 시간차를 두고 폭발한다.
돌아보면 이틀 동안의 평온함이 가장 위험했던 신호였다. 매트릭이 가만히 있다는 것은 변경점이 적용된 후에도, 적용되지 않은 채 묻혀있을 때도 똑같이 가능한 모습이었다.