Dead Letter Queue가 폭증했던 원인 분석

DLQ 폭증은 결과일 뿐이다: 실제 원인은 시스템 어딘가에 숨어 있다.
Dead Letter Queue가 폭증했던 원인 분석
메시지 기반 시스템을 운영하다 보면 누구나 한 번쯤은 데드 레터 큐(Dead Letter Queue, DLQ)가 예상치 못하게 폭증하는 상황을 경험하게 됩니다. 평소에는 몇 건 수준이던 DLQ가 갑자기 수천 건, 수만 건까지 증가하면 운영자는 당황할 수밖에 없습니다. 많은 사람들이 이 순간 “메시지 처리에 실패했다”는 사실에만 집중하지만, 실제로 DLQ 폭증은 문제의 시작이 아니라 이미 문제가 한참 진행된 뒤 나타나는 결과인 경우가 대부분입니다.
필자가 운영했던 워크플로우 기반 시스템에서도 비슷한 경험이 있었습니다. 평소 하루 평균 DLQ 발생 건수는 수십 건 수준이었지만 어느 날 갑자기 수만 건 이상이 적재되기 시작했습니다. 처음에는 단순한 코드 오류를 의심했지만 분석 결과는 예상과 달랐습니다. 실제 원인은 여러 요소가 복합적으로 얽혀 있었고, DLQ는 그 결과를 보여주는 마지막 경고등에 불과했습니다.
이번 글에서는 실제 운영 관점에서 DLQ가 폭증하는 원인과 이를 예방하기 위한 전략을 자세히 살펴보겠습니다.
Dead Letter Queue란 무엇인가
DLQ는 메시지 처리에 실패한 데이터를 격리하는 공간입니다.
일반적으로 메시지 처리 흐름은 다음과 같습니다.
- Producer가 메시지 생성
- Queue에 적재
- Consumer가 처리
- 처리 성공 시 삭제
문제는 처리 실패가 발생할 때입니다.
일시적인 네트워크 오류나 외부 API 장애가 발생하면 시스템은 재시도를 수행합니다. 하지만 설정된 재시도 횟수를 초과하면 더 이상 정상 처리가 불가능하다고 판단하고 메시지를 DLQ로 이동시킵니다.
즉 DLQ는 실패한 메시지를 버리지 않고 보관하는 안전장치입니다.
DLQ 자체는 문제가 아닙니다.
오히려 DLQ가 없으면 실패한 메시지가 유실될 수 있기 때문에 안정적인 시스템 운영을 위해 반드시 필요한 구성 요소입니다.
DLQ가 폭증하는 가장 흔한 원인
1. 코드 배포 이후 발생한 예외
가장 흔한 원인 중 하나입니다.
신규 기능 배포 후 특정 필드가 추가되거나 데이터 형식이 변경되었는데 Consumer가 이를 처리하지 못하는 경우가 많습니다.
예를 들어
{
"userId":123,
"status":"ACTIVE"
}
였던 구조가
{
"userId":123,
"status":"ACTIVE",
"membership":"VIP"
}
로 변경되었는데 Consumer가 새로운 필드를 처리하지 못하면 예외가 발생할 수 있습니다.
특히 마이크로서비스 환경에서는 Producer와 Consumer가 서로 다른 배포 주기를 가지기 때문에 이런 문제가 자주 발생합니다.
2. 외부 API 장애
운영 현장에서 가장 많이 만나는 원인입니다.
Consumer가 메시지를 처리하는 과정에서 다음과 같은 외부 시스템을 호출합니다.
- 결제 API
- 문자 발송 API
- 이메일 서비스
- ERP 시스템
- CRM 시스템
외부 API 응답 시간이 갑자기 증가하거나 장애가 발생하면 Consumer는 타임아웃을 경험하게 됩니다.
이후 재시도가 반복되고 결국 DLQ로 이동하게 됩니다.
실제로 DLQ 폭증 사례를 분석해 보면 상당수가 외부 서비스 장애와 연결되어 있습니다.
3. Retry Storm 발생
DLQ 폭증의 숨겨진 원인입니다.
많은 시스템이 실패 시 자동 재시도를 수행합니다.
문제는 재시도 정책이 잘못 설정된 경우입니다.
예를 들어
- 원본 메시지 1만 건
- 재시도 횟수 5회
라면 최악의 경우 5만 건 이상의 추가 작업이 발생할 수 있습니다.
이 현상을 Retry Storm이라고 부릅니다.
처음에는 작은 장애였지만 재시도 트래픽이 원본 트래픽보다 많아지면서 Queue 전체가 마비되는 상황이 발생할 수 있습니다.
4. 데이터베이스 병목
운영자는 종종 Queue 자체를 의심합니다.
하지만 실제 병목은 Queue 뒤에 존재하는 데이터베이스인 경우가 많습니다.
대표적인 징후는 다음과 같습니다.
- 커넥션 풀 부족
- Lock 경합 증가
- 느린 쿼리 발생
- 트랜잭션 대기 증가
Consumer는 정상 동작하지만 DB 응답이 느려지면서 처리 시간이 증가하고 결국 타임아웃이 발생합니다.
그 결과 DLQ가 증가하기 시작합니다.
5. 리소스 부족
CPU나 메모리 부족도 흔한 원인입니다.
특히 컨테이너 환경에서는 메모리 제한(OOM)으로 인해 Consumer 프로세스가 종료되는 경우가 많습니다.
이 상황에서는 메시지가 중간 상태에서 실패하며 반복적으로 재처리됩니다.
운영 환경에서 DLQ 증가와 함께 다음 지표를 반드시 확인해야 합니다.
- CPU 사용률
- Memory 사용률
- GC 시간
- Pod Restart 횟수
- Container OOM 이벤트
DLQ 폭증 시 가장 먼저 확인해야 할 지표
많은 운영자가 DLQ 건수만 봅니다.
하지만 더 중요한 것은 증가 패턴입니다.
반드시 확인해야 하는 항목
- DLQ 증가 속도
- 실패 메시지 유형
- Consumer 에러율
- Retry 횟수
- API 응답 시간
- DB 응답 시간
- Queue 처리량
- Queue 적체율
특히 특정 유형의 메시지만 실패하는지 확인하는 것이 중요합니다.
실제 장애 분석 시간을 가장 크게 줄여주는 정보가 바로 이것입니다.
DLQ 대응 프로세스
1단계: 샘플 메시지 분석
DLQ 메시지 100만 건을 전부 볼 필요는 없습니다.
상위 10~20건만 분석해도 원인을 찾는 경우가 많습니다.
먼저 확인할 항목은 다음과 같습니다.
- 공통 필드 존재 여부
- 특정 사용자 관련 여부
- 특정 이벤트 관련 여부
- 특정 서비스 호출 여부
2단계: 신규 배포 확인
실무에서는 “무엇이 바뀌었는가”가 가장 중요합니다.
DLQ 폭증 직전
- 코드 배포
- 설정 변경
- 스키마 변경
- 인프라 변경
이 있었는지 확인해야 합니다.
실제 장애의 상당수는 최근 변경 사항과 연결됩니다.
3단계: 재시도 중단
문제가 해결되지 않은 상태에서 재시도를 계속 수행하면 상황이 악화됩니다.
따라서 필요하다면
- Consumer 일시 중지
- Retry Queue 중지
- 신규 메시지 유입 제한
등의 조치를 통해 확산을 막아야 합니다.
4단계: 수정 후 점진적 재처리
문제 해결 후 DLQ를 한 번에 재처리하면 또 다른 장애가 발생할 수 있습니다.
예를 들어 10만 건의 DLQ를 한꺼번에 복구하면 DB와 API에 순간 부하가 발생합니다.
따라서
- 초당 처리량 제한
- Batch 단위 복구
- 우선순위 기반 복구
전략을 사용하는 것이 안전합니다.
DLQ 폭증을 예방하는 설계 방법
멱등성(Idempotency) 확보
같은 메시지가 여러 번 처리되어도 결과가 동일해야 합니다.
이 원칙이 없으면 재시도 과정에서 데이터 중복이 발생합니다.
지수 백오프 적용
재시도 간격을 점진적으로 늘리는 방식입니다.
예시
- 1차 재시도: 1초
- 2차 재시도: 5초
- 3차 재시도: 30초
- 4차 재시도: 5분
일시적인 장애를 자연스럽게 흡수할 수 있습니다.
Circuit Breaker 적용
외부 시스템 장애 시 무한 호출을 막는 패턴입니다.
API 장애가 발생하면 일정 시간 동안 호출을 차단하여 Queue 전체가 영향을 받지 않도록 보호합니다.
스키마 호환성 유지
Producer와 Consumer가 독립적으로 배포되는 환경에서는 스키마 변경에 매우 신중해야 합니다.
Backward Compatibility를 유지하는 것이 DLQ 예방의 핵심입니다.
운영하면서 얻은 가장 큰 교훈
DLQ 폭증은 대부분 Queue의 문제가 아닙니다.
실제 원인은 다음 중 하나인 경우가 많습니다.
- 외부 API 장애
- 데이터베이스 병목
- 잘못된 재시도 정책
- 스키마 변경
- 신규 배포 오류
DLQ는 단순히 그 사실을 가장 먼저 알려주는 경고등일 뿐입니다.
따라서 운영자는 DLQ 숫자 자체보다 “왜 실패가 발생했는가” 에 집중해야 합니다.
DLQ가 100건 발생하는 것은 문제가 아닐 수 있습니다. 하지만 DLQ 증가 속도가 평소보다 10배 빨라졌다면 그것은 시스템 어딘가에서 이미 장애가 시작되고 있다는 강력한 신호입니다.
결국 안정적인 시스템 운영의 핵심은 DLQ를 없애는 것이 아니라, DLQ가 발생하는 원인을 빠르게 발견하고 반복되지 않도록 개선하는 것입니다. DLQ를 단순한 실패 보관소가 아니라 시스템 건강 상태를 보여주는 관측 지표로 활용한다면 장애 대응 능력은 한 단계 더 성장하게 될 것입니다.




댓글 0
첫 댓글을 남겨보세요.