-
[Cloudflare Queues][운영] poison message를 격리할 때 body fingerprint와 DLQ 재처리 순서를 어떤 기준으로 먼저 나누나기타개발지식/풀스택개발 2026. 8. 9. 09:19
IT 리서치 노트
[Cloudflare Queues][운영] poison message를 격리할 때 body fingerprint와 DLQ 재처리 순서를 어떤 기준으로 먼저 나누나
Cloudflare Queues를 운영하다 보면 같은 메시지가 반복 실패할 때 모두 retry로 돌릴지, poison message로 격리할지 판단이 늦어지는 경우가 많다. 2026년 8월 9일 기준 Cloudflare 공식 문서를 다시 보면 DLQ는 max_retries에 도달한 뒤에야 메시지를 받으며, 이미 ack한 메시지는 재전달되지 않고, duplicate를 줄이려면 unique ID와 idempotency key를 먼저 설계하라고 안내한다. 이 글은 poison message를 격리할 때 body fingerprint와 DLQ 재처리 순서를 어떤 기준으로 먼저 나눠야 하는지 정리한 것이다.
1. 개요
결론부터 말하면 poison message triage는 예외 문장만으로 하지 말고
body fingerprint,idempotency key,failure class,batch disposition,dlq enqueue 시점다섯 칸으로 먼저 나누는 편이 맞다. DLQ는 max_retries 뒤에야 움직이므로, 실패 초반부터 같은 메시지인지와 재처리 가능한 실패인지 구분해 두어야 한다.이미 ack·retry·retryAll 분기 글과 write-before-check collision 글을 읽었다면, 이번 글은 그 다음 단계인 격리 기준과 재처리 순서다. 메시지 격리와 duplicate 로그는 consumer concurrency duplicate 로그 글과도 한 줄로 이어진다.
2. 어디서 실제로 막히는가
현장에서 가장 자주 꼬이는 지점은 세 가지다. 첫째, validation error처럼 반복 재처리해도 풀리지 않는 독성 메시지를 timeout 재시도와 같은 버킷에 넣는다. 둘째, 같은 body가 여러 번 깨지는데 fingerprint를 안 남겨 단발성 장애인지 poison인지 구분이 안 된다. 셋째, DLQ에 들어간 뒤에도 어떤 기준으로 다시 꺼낼지 메모가 없어 격리만 늘고 재처리 순서는 계속 흔들린다.
Cloudflare 문서는 DLQ가 max_retries 도달 뒤에만 메시지를 받는다고 적고, batching 문서는 이미 ack한 메시지는 다시 오지 않는다고 설명하며, delivery guarantees 문서는 idempotency key를 권장한다. 이 셋을 합치면 poison 판단은 단순 예외 종류보다 메시지 식별과 batch disposition 기록이 먼저라는 점이 분명해진다.
특히 poison message와 재처리 후보를 같은 메모로 보면 운영자가 두 가지를 동시에 놓친다. 한쪽은 빨리 격리해야 하는 독성 payload인데, 다른 한쪽은 downstream timeout처럼 다시 시도하면 풀릴 수 있는 메시지다. fingerprint와 failure class를 안 남기면 이 둘을 incident 회고에서 끝까지 분리하지 못한다.
- 증상: 같은 종류의 실패가 반복되는데 언제 DLQ로 보내야 할지 애매하다.
- 실패: validation error와 timeout 재시도를 같은 재처리 메모로 본다.
- 막힘: body fingerprint가 없어 같은 poison payload인지 확인하지 못한다.
- 누락: DLQ 이동 뒤 재처리 순서를 위한 메타데이터를 같이 남기지 않는다.
상황 먼저 볼 곳 판단 기준 같은 payload가 반복 실패한다 body fingerprint 동일 fingerprint 반복이면 poison 후보로 본다 같은 비즈니스 작업이 다시 들어온다 idempotency key 중복 작업인지 재전달인지 구분한다 DLQ로 보낸 뒤 다시 꺼낼지 고민된다 failure class와 enqueue 시점 재처리 가능 오류와 격리 유지 오류를 나눈다 3. 실무에서 적용하는 순서
실무 적용 순서는 다섯 단계가 가장 짧다. 먼저 body fingerprint를 고정한다. 두 번째로 idempotency key와 묶는다. 세 번째로 failure class를 timeout, downstream 5xx, validation error처럼 구분한다. 네 번째로 batch disposition을 ack, retry, retryAll, DLQ 이동 수준으로 남긴다. 마지막으로 DLQ 메시지에는 enqueue 시점과 재처리 우선순위를 따로 붙인다.
- body fingerprint를 payload 기준으로 만든다.
- idempotency key와 같은 trace로 저장한다.
- failure class를 재처리 가능/불가 기준으로 나눈다.
- batch disposition을 ack·retry·retryAll·DLQ로 나눈다.
- DLQ 재처리 순서는 enqueue 시점과 failure class로 정렬한다.
이 구조를 잡아 두면 poison triage와 재처리가 동시에 쉬워진다. 예를 들어 validation error가 같은 fingerprint로 세 번 반복되면 곧바로 격리 후보로 올리고, downstream timeout이 다른 fingerprint로 흩어져 있으면 먼저 재시도 정책이나 외부 API 상태를 본다. DLQ는 저장소일 뿐이므로, 재처리 메모 없이 쌓아 두면 같은 실패가 며칠 뒤 그대로 되돌아온다.
message_id=... idempotency_key=order:7781 body_fingerprint=sha256:... failure_class=validation_error batch_disposition=retry() dlq_enqueue_at=...재처리 시에도 같은 순서를 유지하는 편이 좋다. 먼저 fingerprint가 바뀌지 않았는지 보고, 그다음 failure class가 transient로 바뀌었는지 확인하고, 마지막으로 idempotency key가 이미 처리된 작업인지 다시 구분한다. consumer 로그 필드를 기록하고, replay 실행 전에 표와 코드 블록의 값이 맞는지 다시 확인하면 DLQ replay가 신규 메시지를 덮어쓰는 사고를 줄일 수 있다.
4. 공식 문서와 예시 화면으로 확인하기
첫 공식 화면은 Cloudflare의 DLQ 정의다. 여기서는 consumer가 max_retries 한도까지 실패한 뒤에야 메시지가 DLQ로 이동한다는 기본 조건을 먼저 확인해야 한다.
즉 DLQ는 첫 실패를 바로 모아 두는 저장소가 아니다. poison message를 격리하려면 어느 실패를 같은 메시지로 볼지와, 언제 재처리로 돌릴지를 별도 기준으로 남겨야 한다.
두 번째 자료는 explicit ack 규칙이다. 배치 안에서 이미 ack한 메시지는 뒤 메시지 실패와 무관하게 재전달되지 않는다고 문서가 설명한다.
이 규칙이 중요하다는 뜻은 poison 판단도 메시지 단위로 남겨야 한다는 뜻이다. 배치 전체를 한 덩어리로 메모하면 실제로 격리해야 할 단일 message와 재처리 가능한 정상 message가 섞인다.
세 번째 공식 화면은 delivery guarantees 문서의 idempotency key 구간이다. Cloudflare는 중복 처리 피해를 줄이려면 고유 ID를 primary key나 idempotency key로 쓰라고 직접 안내한다.
poison message도 이 기준 위에서 보는 편이 맞다. body fingerprint가 없으면 같은 메시지가 여러 번 깨지는 것인지, 다른 메시지들이 비슷한 오류를 내는 것인지 운영자가 빨리 구분하지 못한다.
실무에서는 같은 실패라도 먼저 갈라야 하는 축이 있다. body fingerprint, idempotency key, batch disposition을 같이 보면 DLQ 격리와 재처리 순서가 훨씬 짧아진다.
이미 ack·retry·retryAll 분기 글이 배치 동작을 다뤘다면, 이번 표는 poison 격리와 재처리 메모를 더 좁히는 다음 단계다.
마지막 자료는 poison triage용 로그 예시다. 중요한 점은 예외 문자열만 남기는 것이 아니라 fingerprint, retry class, DLQ 이동 시점을 한 trace에 남기는 것이다.
이 구조를 남겨 두면 duplicate 로그 글과 write-before-check collision 글에서 잡은 증거를 poison triage까지 그대로 이어 쓸 수 있다.
5. 주의사항과 리스크
첫 번째 리스크는 독성 payload를 timeout retry와 같은 버킷에 넣는 것이다. 두 번째는 body fingerprint 없이 메시지 ID와 예외 문자열만 보고 poison 여부를 판단하는 것이다. 세 번째는 DLQ 재처리 기준 없이 격리만 해 두어 같은 실패를 다시 소비하는 것이다.
운영 전에 확인할 때는 최소한 body fingerprint, idempotency key, failure class, batch disposition, dlq enqueue 시점 다섯 칸이 남는지 보는 편이 좋다. 이 다섯 칸이 없으면 poison triage와 DLQ replay를 서로 다른 워크플로우로 분리하기 어렵다.
- poison message와 transient retry를 먼저 나눠 본다.
- body fingerprint가 없으면 격리 기준이 흔들린다.
- DLQ에는 재처리 순서를 위한 메모가 같이 남아야 한다.
6. 결론
Cloudflare Queues에서 poison message를 격리할 때는 DLQ 설정만으로 충분하지 않다. body fingerprint와 idempotency key와 failure class를 먼저 고정하고, 그 위에 DLQ 재처리 순서를 얹어야 격리와 복구가 같이 정리된다.
- poison triage는 fingerprint부터 시작한다.
- DLQ 이동과 재처리 기준을 같은 trace에 남긴다.
- retry 정책과 격리 정책을 서로 다른 메모로 관리한다.
7. 참고 링크
'기타개발지식 > 풀스택개발' 카테고리의 다른 글