website: 이메일 실패 알람 감지 주기 15분 → 5분 단축 (#113)
지적을 받고 다시 보니 15분을 고집할 이유가 딱히 없었다. `push-email-failure-metric.py`는 sqlite3로 `email_log` 테이블을 직접 SELECT만 하는 가벼운 조회라, 크론 주기를 낮춘다고 서버 부하가 늘어나는 게 아니었다. 게다가 같은 레포 안의 디스크 사용률 알람(`push-disk-metric.py`)과 gi...
#276에서 만들었던 모집 이메일 실패 알람의 감지 주기를 15분에서 5분으로 낮춘 PR이다.
#276에서 이메일 실패 알람을 새로 만들 때, pending-duration(P)·쿼리 윈도우(W)·크론 주기(C)를 전부 15분으로 맞췄었다. 디스크·메모리 알람처럼 "값이 임계치를 넘고 나서 몇 번 더 지속돼야 발동"하는 구조가 아니라, 이메일 발송은 뜸하다가 몰릴 때 한꺼번에 몰리는 성격이라 P를 C보다 크게 잡으면 burst성 장애를 놓칠 수 있다는 판단 하에 P=C=W로 맞춰서 첫 breach에 바로 발동하도록 설계했었다. 그 판단 자체는 맞았는데, 그 값을 왜 15분으로 잡았는지에 대한 근거는 사실 없었다. "P=W=C를 맞춘다"는 원칙만 지키려고 15분이라는 숫자를 임의로 골랐던 거고, 장찬욱이 그 부분을 지적했다.
바꾼 내용 자체는 단순하다. 크론 주기, 쿼리 윈도우, pending-duration 세 숫자를 15에서 5로 일괄 교체했다.
NAMESPACE = "custom_likelion"
WINDOW_MINUTES = 5 # cron 주기(*/5)와 일치 - observability.md "알람 튜닝 사고 모델"의 C (disk/git-drift와 동일 주기)
다만 이게 단순 치환으로 끝나는 건 아니었다. k_min = ceil((C-W)/C)+1 = 1 같은 계산식이나 "P=W=C라 첫 breach에 바로 발동"이라는 서술이 여러 문서(observability.md, RUNBOOK.md, CLAUDE.md)에 흩어져 있었는데, 이게 15분이라는 구체적인 숫자를 근거로 문장을 이루고 있어서 하나만 바꾸면 나머지가 앞뒤가 안 맞았다. 예를 들어 RUNBOOK.md의 복구 절차 설명에는 "마지막 실패가 15분 쿼리 윈도우 밖으로 밀려나는 시점(최대 약 1520분)에 알람이 자연히 OK로 전환된다"는 식으로 실제 대기 시간까지 명시돼 있었는데, 이것도 "최대 약 510분"으로 같이 고쳐야 했다. 숫자 하나를 바꾸는 게 아니라, 그 숫자를 근거로 삼은 설명 여러 군데를 같이 손보는 작업이었다.
observability.md의 "왜 pending-duration이 cron 주기와 같은가" 섹션도 다시 정리했다. 이 알람을 처음 설계할 때 "처음엔 git드리프트처럼 P를 C보다 크게 잡으려 했다가 이 지표엔 안 맞아서 P=C로 바꿨다"는 과거 판단 과정이 문서에 그대로 남아 있었는데, 여기에 이번 조정 이유("C(=W=P)는 15분으로 시작했다가, 장찬욱의 '이렇게까지 오래 기다려야 하나' 지적에 5분으로 낮췄다")를 이어붙였다. 알람 하나의 설계가 왜 지금 이 형태가 됐는지 그 히스토리를 문서에서 계속 추적할 수 있게 해두고 싶었다.
변경 범위는 infra/CLAUDE.md, infra/docs/RUNBOOK.md, infra/docs/observability.md, infra/scripts/push-email-failure-metric.py 네 파일, 각각 몇 줄씩이라 diff 자체는 작다. 코드 변경도 상수 하나 고친 게 전부다. 하지만 이 PR에서 실제로 반영되는 부분은 여기까지고, 서버 크론을 */15에서 */5로 갱신하는 것과 OCI에 등록된 알람 2개(prod/stage)의 query·pending-duration을 5분으로 업데이트하는 건 머지 후 서버에서 직접 해야 하는 별개 작업으로 남겨뒀다. 실제로 알람이 5분 만에 잘 발동하는지 stage DB에 테스트 FAILURE를 넣어 재검증하는 것도 마찬가지로 이 PR 범위 밖이다. 레포 안의 설정값과 문서를 먼저 맞춰두고, 실제 인프라 반영은 그다음 단계로 분리한 셈이다.
생성부터 병합까지 10분 정도 걸렸다. 코드 변경 자체는 상수 하나지만, 그 상수 하나가 여러 문서의 서술과 얽혀 있어서 실제로는 "숫자를 근거로 쓴 문장들을 전부 찾아서 일관되게 고치는" 작업에 가까웠다. 임의로 고른 값을 나중에 근거 없이 정당화하기보다, 지적이 들어왔을 때 "이 값을 왜 이렇게 잡았지"를 다시 따져보고 실제 근거(비용, 기존 패턴과의 일관성)로 대체하는 편이 문서 신뢰도에도 낫다는 걸 다시 확인한 PR이었다.