본문으로 바로가기
ai-automation2026년 4월 7일·조회 68

메시지 큐 재시도 로직에서 발생한 교착 상태 해결 사례

Rate Limit 재시도 시 메시지가 영구 처리 중 상태에 갇히는 문제와 해결 방법

SP

SpacePlanning

SpacePlanning AI Team

## 들어가며 분산 시스템에서 메시지 큐를 운영하다 보면 재시도(retry) 로직은 필수적입니다. 특히 외부 API 호출 시 rate limit에 걸렸을 때 메시지를 재시도 큐로 보내는 패턴은 매우 흔합니다. 하지만 이 과정에서 메시지가 영구적으로 '처리 중(processing)' 상태에 갇혀버리는 문제를 경험한 적이 있으신가요? 오늘은 실제 프로덕션 환경에서 발생한 메시지 큐 교착 상태 문제와 그 해결 과정을 공유합니다. ## 문제 상황: 메시지가 사라지다 ### 증상 재시도 큐(retry_queue)에서 처리되어야 할 메시지들이 계속 '처리 중' 상태로 남아있고, 실제로는 처리되지 않는 현상이 발생했습니다. ### 원인 분석 문제의 근본 원인은 메시지 상태 전이(state transition)와 클레임(claim) 로직 사이의 불일치였습니다. **정상 흐름:** 1. 워커가 메시지를 가져와 처리 시작 → 상태: `pending` → `processing` 2. Rate limit 발생 → 재시도 큐에 추가 3. 일정 시간 후 재시도 큐가 메시지 재처리 시도 **문제 발생 지점:** 재시도 큐가 메시지를 다시 처리하려 할 때, 기존 클레임 조건은 이렇게 설정되어 있었습니다: ```sql SELECT * FROM messages WHERE id = $1 AND status IN ('pending', 'queued', 'routing', 'pending_local') FOR UPDATE ``` 문제는 메시지가 이미 `processing` 상태라는 점입니다. 위 쿼리는 `processing` 상태인 메시지를 클레임할 수 없으므로: - 클레임 실패 → "already claimed by another worker" 메시지 출력 - 재시도 로직은 메시지를 건너뜀 - 메시지는 영구히 `processing` 상태로 남음 ## 해결 방법: Self-Reclaim 패턴 핵심 아이디어는 **같은 워커가 자신이 이미 클레임한 메시지를 다시 클레임할 수 있도록** 허용하는 것입니다. ### 개선된 클레임 쿼리 ```sql SELECT * FROM messages WHERE id = $1 AND ( status IN ('pending', 'queued', 'routing', 'pending_local') OR (status = 'processing' AND worker_id = $2) -- 핵심: 같은 워커면 재클레임 허용 ) FOR UPDATE ``` ### 동작 원리 1. **새로운 메시지**: `status`가 `pending` 등이므로 첫 번째 조건으로 클레임 2. **재시도 메시지**: `status`가 `processing`이지만 `worker_id`가 현재 워커와 같으므로 두 번째 조건으로 클레임 3. **다른 워커의 메시지**: 두 조건 모두 만족하지 않아 클레임 실패 (정상적인 충돌 방지) ## 적용 결과 - ✅ Rate limit 재시도 시 메시지가 정상적으로 재처리됨 - ✅ 더 이상 `processing` 상태에 갇힌 메시지 없음 - ✅ 다른 워커와의 충돌 방지 기능은 그대로 유지 ## 핵심 교훈 ### 1. 상태 전이 설계 시 재시도 시나리오 고려 메시지 큐 설계 시 "재시도도 하나의 정상 흐름"으로 간주하고 상태 전이 다이어그램에 명시적으로 포함시켜야 합니다. ### 2. 클레임 로직의 멱등성(Idempotency) 같은 워커가 같은 메시지를 여러 번 클레임하는 것은 안전해야 합니다. 이는 재시도뿐 아니라 네트워크 타임아웃 등 다양한 엣지 케이스에서 유용합니다. ### 3. 모니터링의 중요성 `processing` 상태로 일정 시간 이상(예: 1시간) 남아있는 메시지를 감지하는 알람을 설정하면 이런 문제를 조기에 발견할 수 있습니다. ## 마치며 분산 시스템에서 재시도 로직은 단순해 보이지만 상태 관리와 결합되면 복잡한 엣지 케이스를 만들어냅니다. 이번 사례처럼 "같은 워커의 재클레임 허용" 패턴은 많은 메시지 큐 시스템에서 유용하게 활용될 수 있습니다. 여러분의 시스템에서도 비슷한 문제를 겪고 계신가요? 댓글로 경험을 공유해주세요!
#메시지큐#분산시스템#재시도로직#PostgreSQL#트러블슈팅
공유하기:

이 주제에 대해 더 알아보고 싶으신가요?

프로젝트 상담을 통해 맞춤형 솔루션을 제안받으세요.