자정 직전, 연쇄 장애를 막아낸 두 건의 수리
목차
어제 밤 11시, 딱히 새 기능을 만들겠다는 계획은 없었다. 그런데 모니터링 로그를 슬쩍 들여다보다가 신경 쓰이는 패턴을 발견했고, 결국 자정이 넘도록 코드를 붙잡게 됐다.
발단: DB 연결이 조용히 죽어 있었다
문제의 핵심은 단일 DB 커넥션을 재사용하는 스크래핑 파이프라인이었다. 이 파이프라인은 작업 간격이 길어질 때가 있는데, 그 틈에 MySQL의 wait_timeout이 조용히 커넥션을 끊어버린다. 이걸 모르고 다음 쿼리를 날리면?
OperationalError: (2006, 'MySQL server has gone away')
여기까지는 흔한 문제다. 진짜 골치 아팠던 건 이 끊김이 LLM API 호출 직전에 발생했을 때였다. DB에서 처리 대상 레코드를 못 가져오니까 파이프라인 전체가 None을 들고 API를 쳤고, 거기서 또 500이 터졌다. 하나의 끊김이 아래로 쭉 cascading되는 구조였던 거다.
수리 방향은 두 가지였다.
| 문제 | 원인 | 대응 |
|---|---|---|
| DB 커넥션 소실 | wait_timeout 초과 | 쿼리 전 ping → 끊겼으면 reconnect |
| LLM API 500 | 간헐적 외부 오류 | 최대 3회 재시도, 지수 백오프 |
DB 쪽은 쿼리를 날리기 직전에 ping(reconnect=True) 한 줄로 해결이 됐다. 단순하지만 이걸 빼먹으면 장기 운영 중에 반드시 터진다.
LLM 재시도 로직은 조금 더 신경 썼다. API 500은 보통 일시적 과부하인 경우가 많아서, 무작정 3번 때리면 역효과가 날 수 있다. 그래서 아래처럼 시도 간 대기를 넣었다.
for attempt in range(3):
try:
result = call_llm(prompt)
break
except APIStatusError as e:
if e.status_code == 500 and attempt < 2:
time.sleep(2 ** attempt) # 1s → 2s
continue
raise
3번 모두 실패하면 그냥 예외를 위로 올린다. 무한 재시도는 더 나쁘다.
작은 문서 정리도 같이
파이프라인 수리가 마무리되고 나서, 별도로 블로그 도메인 이전을 반영하는 문서 업데이트도 끼워 넣었다. 봇이 참조하는 CLAUDE.md에 구 도메인이 남아 있으면 언젠가 잘못된 컨텍스트를 들고 작업하는 일이 생길 수 있어서, 눈에 띄는 김에 바로 정정했다.
이런 류의 문서는 "나중에 하지 뭐" 하다가 몇 달씩 묵는다. 기능 작업 틈에 끼워 처리하는 게 현실적으로 제일 낫다.
이 시간을 관통한 키워드: 조용한 실패
오늘 밤 두 작업의 공통점은 소리 없이 죽는 실패였다. DB 끊김도, API 500 캐스케이드도, 당장 서비스가 멈추는 게 아니라 파이프라인 결과물이 슬그머니 비어버리는 방식으로 나타난다. 이런 종류의 버그는 알람이 울리지 않아서 오히려 오래 살아남는다.
- 커넥션 수명을 의식하지 않은 단일 커넥션 재사용
- 외부 API 실패를 단일 시도로 처리
- stale 상태로 남은 참조 문서
셋 다 "지금 당장은 괜찮다"는 안일함에서 비롯된다. 자정 직전에 이 정도 수리를 끝냈다는 건 나쁘지 않은 밤이었다.
🛒 이 글과 어울리는 추천 상품
*위 링크는 쿠팡파트너스 활동의 일환이며, 일정액의 수수료를 제공받을 수 있습니다.
댓글 0
첫 댓글 달아줘.