cron 배치 실패 알림이 하루에 수십 통씩 오고 내용이 다 같아서 아무도 보지 않았다. 그래서 한 시간에 한 통만 나가게 바꿨다. 그 뒤로 실패를 며칠 늦게 알게 됐다.
Table of contents
Open Table of contents
원인 — 억제 조건이 너무 넓었다
이렇게 만들었다.
$last = $this->cache->get('last_alert_time');
if ($last && (time() - $last) < 3600) {
return; // 한 시간 안에 보낸 적 있으면 안 보냄
}
sendAlert($message);
$this->cache->save('last_alert_time', time(), 7200);
마지막으로 보낸 시각을 memcached 키 하나에 담고 TTL 을 한 시간으로 줬다. 그 키가 하나라서 종류를 가리지 않았고 어떤 알림이든 한 시간에 한 통이 됐다.
상품 동기화 실패로 한 통이 나가면 그 뒤 한 시간 동안 결제 배치 실패가 묻힌다. 같은 알림을 줄이려던 것인데 다른 알림까지 줄인 것이다. 줄이는 대상을 정하지 않은 것이 원인이었다.
억제 단위를 나눴다
키 이름에 알림 종류를 넣었다.
$key = 'last_alert:' . $type; // 'last_alert:sync', 'last_alert:payment'
$last = $this->cache->get($key);
if ($last && (time() - $last) < 3600) {
return;
}
sendAlert($message);
$this->cache->save($key, time(), 7200);
$type 이 키에 들어가니 같은 종류만 그 TTL 안에서 억제되고 다른 종류는 그대로 나간다. 대상 번호까지 넣으면 더 좁게 나눌 수도 있었다.
$key = 'last_alert:' . $type . ':' . $targetId;
다만 너무 좁히면 억제를 안 하는 것과 결과가 같아진다. 같은 대상에서 계속 나는 실패는 다시 수십 통이 된다. 어느 단위로 나눌지가 이 작업의 판단이었다.
억제된 건수를 함께 보냈다
억제하면 그 사이에 몇 번 있었는지 알 수 없다. 그래서 안 보낸 횟수를 세어 두고 다음 알림에 붙였다.
$cnt = (int)$this->cache->get($key . ':count');
if ($last && (time() - $last) < 3600) {
$this->cache->save($key . ':count', $cnt + 1, 7200);
return;
}
$extra = $cnt > 0 ? " (지난 1시간 동안 {$cnt}건 더 발생)" : '';
sendAlert($message . $extra);
$this->cache->save($key . ':count', 0, 7200);
받는 알림은 이렇게 된다.
[배치실패] 상품 동기화 실패 (지난 1시간 동안 47건 더 발생)
여기서 한 번 걸렸다. Memcached::increment() 에 초기값을 주려면 OPT_BINARY_PROTOCOL 이 켜져 있어야 하고 아니면 인자 오류가 난다. 그래서 add() 로 먼저 만들고 increment() 를 부르는 쪽으로 했다.
한 건 실패한 것과 47건 실패한 것은 다른 상황이다. 숫자가 없으면 둘이 같은 알림으로 보인다. 이 한 줄이 대응 순서를 바꿨다.
비교 — 억제하지 않는 알림
알림 종류를 나눠 보니 억제해도 되는 것과 아닌 것이 갈렸다.
| 종류 | 억제 |
|---|---|
| 배치 실패 | 1시간 |
| 조회 지연 | 1시간 |
| 결제 처리 실패 | 안 함 |
| 데이터베이스 접속 실패 | 안 함 |
돈이 걸린 것과 전체가 멈추는 것은 memcached 를 아예 안 보고 매번 보내게 뒀다. 시끄러워도 그게 맞았다. 억제는 여러 번 와도 대응이 같은 것에만 걸었다.
복구됐을 때도 알리게 했다.
if ($nowOk && $this->cache->get('failing:' . $type)) {
sendAlert("[복구] {$type} 정상화");
$this->cache->delete('failing:' . $type);
}
복구 알림이 있으면 실패 알림을 받고도 기다려 볼 수 있다. 없으면 매번 들어가서 봐야 한다.
알림을 보내는 코드 자체가 죽으면 아무 알림이 없고 그러면 정상으로 보인다. 그래서 cron 으로 하루 한 번 정상 신호를 보내게 했다.
[일일점검] 배치 5종 정상, 어제 실패 3건
이게 안 오면 감시 자체가 멈춘 것이다. 아무 소식이 없는 것을 정상으로 읽으면 안 되고 정상일 때도 소식이 있어야 둘이 구분된다.
수신자도 설정 파일에 목록으로 뒀다. 한 사람 메일로만 가면 그 사람이 휴가일 때 아무도 못 보고 코드에 박아 두면 사람이 바뀔 때마다 배포한다.
정리
- 억제 조건이 넓으면 다른 사건까지 묻힌다
- 줄일 대상을 정하지 않으면 전부가 대상이 된다
memcached키 이름에$type을 넣어 단위를 나눈다- 너무 좁히면 억제를 안 하는 것과 같아진다
- 억제한 횟수를
increment()로 세어 다음 알림에 붙인다 - 한 건과 47건은 다른 상황이다
- 돈이 걸린 것과 전체가 멈추는 것은 억제하지 않는다
- 복구 알림이 없으면 매번 직접 들어가서 보게 된다
- 정상일 때도
cron으로 하루 한 번 신호를 보낸다 - 수신자는 설정 파일에 둔다. 코드에 박으면 사람이 바뀔 때마다 배포한다