-
[Cloudflare Queues][운영] consumer concurrency를 늘릴 때 duplicate 처리와 idempotency key 검증을 어떤 로그부터 남기나기타개발지식/풀스택개발 2026. 8. 8. 09:16
IT 리서치 노트
[Cloudflare Queues][운영] consumer concurrency를 늘릴 때 duplicate 처리와 idempotency key 검증을 어떤 로그부터 남기나
Cloudflare Queues에서 consumer concurrency를 올리기 시작하면 backlog는 줄어도 duplicate 해석이 더 어려워질 때가 많다. 2026년 8월 8일 기준 Cloudflare 공식 문서를 다시 보면 concurrency는 backlog와 error rates에 따라 자동으로 조정되고, stronger delivery guarantees를 위해서는 idempotency key 설계가 중요하며, observability는 backlog와 consumer concurrency와 message operations를 같이 보라고 한다. 이 글은 consumer concurrency를 늘릴 때 duplicate 처리와 idempotency key 검증을 어떤 로그부터 남겨야 하는지 정리한 것이다.
1. 개요
결론부터 말하면 concurrency를 올리기 전에 먼저 남겨야 할 것은 message id, idempotency key, invocation id, retry 시도 수, backlog·concurrency snapshot이다. Cloudflare Queues는 at-least-once 전제 위에서 동작하므로 duplicate를 0으로 만든다는 목표보다 duplicate를 빠르게 판별할 로그 구조를 먼저 세우는 편이 맞다.
즉 scale 조절과 duplicate 처리 설계는 같은 문제다. 처리량만 보고 consumer concurrency를 늘리면 backlog는 줄어도 '이게 같은 메시지의 재전달인지, 다른 consumer가 같은 비즈니스 작업을 동시에 잡은 것인지'를 나중에 설명하지 못한다.
2. 어디서 실제로 막히는가
현장에서 가장 자주 꼬이는 지점은 세 가지다. 첫째, message id는 남기지만 business key를 안 남겨 duplicate와 별도 신규 요청을 구분하지 못한다. 둘째, retry 시도와 backlog 변화는 보지만 어느 consumer invocation이 먼저 commit했는지는 남기지 않는다. 셋째, autoscale 직전과 직후의 message operations를 보지 않아 duplicate burst 시점을 backlog spike와 연결하지 못한다.
Cloudflare 문서는 consumer concurrency가 backlog와 error rates를 기준으로 자동 조정된다고 설명하고, delivery guarantees 문서는 idempotency key를 기준으로 stronger guarantee를 구현해야 한다고 짚는다. observability 문서는 backlog와 concurrency와 message operations를 같이 보라고 한다. 이 셋을 합치면 운영자가 먼저 만들어야 할 것은 '더 빠른 consumer'가 아니라 '더 잘 읽히는 duplicate 로그'라는 점이 분명해진다.
- 증상: concurrency를 올린 뒤 간헐적 duplicate나 중복 write가 보이기 시작한다.
- 실패: message id만 남기고 business key는 안 남긴다.
- 막힘: retry와 duplicate를 같은 로그 패턴으로 본다.
- 누락: backlog와 concurrency 증거를 app 로그와 붙여 보지 않는다.
3. 실무에서 적용하는 순서
가장 짧은 적용 순서는 다섯 단계다. 먼저 idempotency key를 비즈니스 단위로 정한다. 두 번째로 message id와 invocation id를 같은 로그에 넣는다. 세 번째로 attempt와 retry reason을 남긴다. 네 번째로 backlog와 consumer concurrency snapshot을 같은 타임라인에 붙인다. 마지막으로 중복 write가 발생했을 때 key 충돌과 정상 재전달을 अलग개 카운트한다.
- idempotency key를 먼저 정의한다.
- message id와 invocation id를 같이 남긴다.
- attempt와 retry reason을 남긴다.
- backlog와 concurrency snapshot을 붙인다.
- duplicate와 정상 retry를 अलग개 집계한다.
실제 운영에서는 consumer batch 안에서 message id와 order id를 묶고, DB write 직전과 직후의 key 상태를 남기고, GraphQL 또는 대시보드에서 backlog와 concurrency를 같은 시각대로 저장하고, burst 구간의 retry reason을 다시 모아 보는 편이 좋다. 이렇게 해야 scale-up 뒤 duplicate가 애플리케이션 설계 문제인지, 정상적인 at-least-once 재전달인지, downstream timeout 때문에 생긴 현상인지 분리된다.
message_id=... idempotency_key=order:7781 invocation_id=... attempt=3 retry_reason=timeout backlog_count=1200 consumer_concurrency=184. 공식 문서와 예시 화면으로 확인하기
첫 공식 화면은 consumer concurrency 동작 설명이다. Cloudflare는 backlog와 error rates를 기준으로 concurrent consumers를 조정한다고 적고 있다.
즉 concurrency를 올리면 처리량만 늘어나는 것이 아니라 같은 메시지가 다시 보일 수 있는 운영 압력도 같이 커진다. duplicate 분리 로그 없이 scale만 올리면 같은 증상을 오래 해석하게 된다.
두 번째 자료는 delivery guarantees 문서의 idempotency key 구간이다. at-least-once 경로에서는 중복 처리를 전제로 받아들이고 idempotency를 직접 설계해야 한다.
그래서 duplicate 자체를 0으로 만들겠다는 접근보다, 어떤 key를 같은 처리로 볼지 먼저 적는 편이 맞다. key 정의가 없으면 로그가 많아도 운영자는 같은 메시지인지 다른 메시지인지 구분하지 못한다.
세 번째 화면은 metrics 문서다. Cloudflare는 backlog, consumer concurrency, message operations를 같이 보라고 한다.
즉 duplicate triage는 application log만의 일이 아니다. concurrency 상승 시점과 backlog 변화와 message operation을 같은 타임라인에 두어야 duplicate burst를 설명할 수 있다.
실무에서는 로그 항목을 먼저 고정해 두는 편이 빠르다. consumer count가 늘어난 순간부터 어떤 필드를 남겨야 duplicate와 정상 재시도를 가를 수 있는지 표로 정리했다.
이미 ack·retry·retryAll 분기 글이 메서드 선택을 다뤘다면, 이번 표는 concurrency를 올린 뒤 운영 로그를 어떻게 남길지의 후속편이다.
마지막 자료는 consumer 로그 예시다. idempotency key와 backlog snapshot을 같은 레코드로 남기면 duplicate burst를 한 번에 복기할 수 있다.
핵심은 단순 console 출력이 아니라, 메시지 식별자와 비즈니스 key와 concurrency 지표를 같은 단위로 묶는 것이다. 그래야 scale-up 뒤 duplicate 처리 로직이 실제로 동작했는지 검증할 수 있다.
5. 주의사항과 리스크
첫 번째 리스크는 idempotency key 없이 duplicate를 application log 문장만으로 추적하는 것이다. 두 번째는 autoscale 순간의 backlog와 concurrency 증거를 안 남겨 duplicate burst를 재현하지 못하는 것이다. 세 번째는 정상 retry를 장애 duplicate와 같은 카운터에 넣어 운영 판단을 어지럽히는 것이다.
운영 전에 확인할 때는 최소한 message id, business key, invocation id, attempt, backlog·concurrency snapshot 다섯 칸이 한 레코드에 있는지 보는 편이 좋다. 이 다섯 칸이 없으면 consumer concurrency를 조정할수록 duplicate 해석 비용이 더 커진다.
6. 결론
Cloudflare Queues에서 consumer concurrency를 늘릴 때는 먼저 duplicate를 줄이는 코드보다 duplicate를 읽을 수 있는 로그 구조를 고정하는 편이 맞다. message id와 idempotency key와 backlog·concurrency snapshot을 같은 묶음으로 남기면 scale-up 이후의 중복 처리와 재전달을 훨씬 짧게 설명할 수 있다.
같은 로그 구조를 이미 갖췄는데도 multi-consumer에서 같은 key collision이 애매하게 남는다면, 다음 단계는 write-before-check 로그와 batch disposition을 같이 보는 후속 글로 내려가 충돌이 정상 unique conflict인지 재전달 conflict인지부터 다시 가르는 편이 빠르다.
- idempotency key는 scale 전부터 정의한다.
- duplicate triage 로그는 message id만으로 끝내지 않는다.
- backlog와 concurrency 증거를 app 로그와 같이 본다.
7. 참고 링크
'기타개발지식 > 풀스택개발' 카테고리의 다른 글