프로덕션에서 결제 이벤트를 Kafka로 발행하는 서비스가 있었는데, 한 달에 한두 번씩 “메시지가 유실됐다”는 보고가 올라왔습니다. 확인해 보니 retry 설정을 건드리지 않아 기본값으로 돌고 있었고, broker 리더 교체 타이밍에 조용히 실패하고 있었습니다. retries, acks=all, enable.idempotence=true로 바꾼 뒤 유실 건이 사라졌습니다.
Kafka producer retry는 쉽게 tuning 문제처럼 보이지만, 실제 incident에서는 retry가 timing 증상인 경우가 훨씬 많습니다. producer는 보통 “retry 숫자가 이상하다”보다 “delivery path가 더 오래 걸리고 있다”, “acknowledgement budget이 모자란다”, “지키려는 guarantee가 지금 경로에서 버겁다”를 먼저 드러냅니다.
짧게 말하면 retries부터 올리거나 내리지 마세요. retry는 delivery.timeout.ms, request.timeout.ms, acknowledgement 기대치, broker health와 같이 읽어야 합니다.
이 글이 맞는 상황
아래 중 하나면 여기서 시작하면 됩니다.
- app 코드는 안 바뀌었는데 producer retry가 갑자기 늘었다
- send latency와 retry count가 같이 올라간다
- broker pressure, leader movement, partition event 뒤에 retry가 뛰었다
- 팀이
retries,acks, producer timeout 중 무엇을 건드려야 할지 모르겠다 - retry 관련 설정을 바꾼 뒤 ordering이나 duplicate risk가 헷갈려졌다
처음 10분에 볼 것
cluster 쪽 한 장면과 producer 쪽 한 장면을 같이 보세요.
kafka-topics.sh --bootstrap-server <broker:9092> --describe --topic <topic>
그리고 producer의 retry 추세와 request latency 추세를 같이 비교하세요.
이 단계에서 답해야 할 질문은 네 개입니다.
- retry가 request latency 또는 acknowledgement 지연과 같이 올랐는가
- leader 이동, broker restart, ISR 변화가 먼저 있었는가
- producer가
delivery.timeout.ms또는request.timeout.ms에 막히고 있는가 - ordering과 durability guarantee가 현재 경로가 감당하기 어려운 수준으로 강한가
retries가 메인 타이머는 아닙니다
Kafka producer 문서는 사용자가 보통 retries를 직접 만지기보다 delivery.timeout.ms로 retry 동작을 제어하는 편이 낫다고 설명합니다.
이 한 줄 때문에 incident 해석이 달라집니다.
retries는 전체 delivery budget 그 자체가 아니다- producer는 send가 성공하거나, non-transient error를 만나거나, delivery deadline이 끝날 때까지 계속 retry할 수 있다
- retry spike는 retry 숫자가 잘못됐다기보다 path가 먼저 느려졌다는 뜻일 때가 많다
즉 “retry를 줄여야 하나?”는 대개 첫 질문이 아닙니다.
delivery.timeout.ms가 진짜 delivery budget입니다
Kafka 문서는 delivery.timeout.ms를 send()가 반환된 뒤 성공 또는 실패를 보고하기까지 허용되는 전체 상한 시간이라고 정의합니다.
그 budget 안에는 아래가 다 들어갑니다.
- 전송 전에 지연되는 시간
- broker acknowledgement를 기다리는 시간
- retriable failure 동안 재시도에 쓰는 시간
그리고 Kafka 문서는 이 값이 request.timeout.ms + linger.ms보다 크거나 같아야 한다고 설명합니다.
즉 retry storm는 아래 둘 중 하나로 생기기 쉽습니다.
- path가 실제로 느려졌다
- budget이 실제 경로에 비해 너무 작아졌다
request.timeout.ms가 불필요한 retry를 만들 수 있습니다
Kafka 문서는 request.timeout.ms를 request response를 기다리는 시간으로 설명하고, unnecessary producer retry를 줄이려면 broker의 replica.lag.time.max.ms보다 크게 잡는 것이 좋다고 설명합니다.
이게 producer incident에서 정말 자주 놓치는 부분입니다.
- broker replication이 평소보다 느리다
- request timeout이 너무 빡빡하다
- client는 사실 성공했을 수도 있는 경로를 너무 빨리 retry한다
즉 retry가 늘었다고 해서 곧바로 broker failure는 아닙니다. timeout budget mismatch일 수 있습니다.
acks는 성공의 의미 자체를 바꿉니다
Kafka producer 문서는 이 부분도 명확합니다.
acks=0: producer는 acknowledgement를 기다리지 않으므로 retry가 같은 의미로 도움되지 않는다acks=1: leader가 local write 뒤 ack하지만 follower 전체 복제까지는 기다리지 않는다acks=all: leader가 in-sync replica 전체를 기다리므로 가장 강한 guarantee를 준다
즉 retry는 반드시 선택한 guarantee와 같이 읽어야 합니다. 더 강한 acknowledgement path는 timing pressure를 더 잘 드러내고, 그 자체가 나쁜 건 아니지만 incident 모양은 바꿉니다.
idempotence와 ordering도 retry 이야기의 일부입니다
현재 Kafka producer 문서는 conflicting config가 없으면 idempotence가 기본 활성화라고 설명합니다.
그리고 아래도 같이 설명합니다.
- idempotence는
acks=all이 필요하다 - idempotence는
retries > 0이 필요하다 - idempotence는
max.in.flight.requests.per.connection <= 5가 필요하다
또 문서는 중요한 ordering 경고를 줍니다. retry가 가능하고 enable.idempotence=false이며 max.in.flight.requests.per.connection > 1이면, 실패 후 재전송 과정에서 record 순서가 바뀔 수 있습니다.
그래서 retry incident는 latency 문제만이 아니라 correctness 문제이기도 합니다.
먼저 읽어야 할 실전 config 블록
acks=all
enable.idempotence=true
delivery.timeout.ms=120000
request.timeout.ms=30000
max.in.flight.requests.per.connection=5
이게 정답 하나는 아니지만, 대부분의 retry incident가 이 경계들 안에서 설명됩니다.
retry spike는 대개 이 패턴 중 하나입니다
| 패턴 | 보통 의미 | 다음 확인 포인트 |
|---|---|---|
| retry가 request latency와 같이 오른다 | delivery path slowdown | broker ack timing과 ISR health 확인 |
| leader movement 뒤 retry가 오른다 | cluster topology가 먼저 바뀜 | partition leader와 broker event 확인 |
| timeout 변경 뒤 retry가 오른다 | waiting budget이 달라짐 | delivery.timeout.ms, request.timeout.ms, linger.ms 확인 |
| idempotence off + high in-flight에서 retry가 오른다 | correctness risk 증가 | ordering guarantee와 duplicate tolerance 확인 |
| broker는 멀쩡해 보이는데 retry만 오른다 | client budget 또는 network timing이 빡빡함 | timeout과 network jitter 확인 |
retry를 failure rate와 같은 것으로 보면 안 됩니다
retry spike가 있다고 해서 곧바로 end-to-end failure를 뜻하지는 않습니다.
그래서 raw retry count보다 더 중요한 비교는 아래입니다.
- retry 대 request latency
- retry 대 send error type
- retry 대 broker restart 또는 leader event
- retry 대 partition health와 ISR 안정성
retry는 늘었지만 delivery는 끝내 성공하는 상황이라면, producer가 cluster stress를 흡수하고 있는 것일 수 있습니다.
흔한 원인
1. broker acknowledgement가 느려짐
cluster는 살아 있지만 producer가 평소보다 오래 기다립니다.
2. request timeout이 현재 broker 상태에 비해 너무 짧음
사실 완료될 수 있는 경로를 client가 너무 일찍 retry합니다.
3. leader 이동 또는 broker restart가 delivery path를 바꿈
retry는 cluster event의 client-side 그림자일 수 있습니다.
4. 더 강한 guarantee가 timing pressure를 더 잘 드러냄
acks=all과 idempotence는 보통 맞는 선택이지만, delay를 숨기지 않고 드러냅니다.
5. retry 관련 config 변경이 ordering semantics까지 바꿈
incident가 성능 문제를 넘어서 correctness 문제 일부가 됩니다.
흔한 잘못된 시작
delivery.timeout.ms도 안 보고retries부터 낮추기- retry를 client-only metric으로 보기
- durability guarantee를 약하게 만드는 줄 모르고
acks바꾸기 - symptom을 잠재우려고 idempotence를 가볍게 끄기
- broker 쪽 leader movement와 ISR 변화를 무시하기
실전 점검 순서
1. retry와 request latency를 같이 봅니다
둘이 같이 움직였으면 delivery path가 첫 번째 suspect입니다.
2. delivery.timeout.ms와 request.timeout.ms를 확인합니다
producer가 실제 경로를 기다릴 만큼 budget을 가지고 있었는지 봐야 합니다.
3. acks, idempotence, max.in.flight.requests.per.connection을 확인합니다
producer가 지키려는 guarantee를 먼저 알아야 합니다.
4. 최근 broker, leader, ISR event를 확인합니다
retry는 client에서 먼저 보이지만 원인은 cluster가 먼저 바뀐 경우가 많습니다.
5. 답이 timing인지, broker health인지, guarantee인지 결정합니다
이 셋을 한 knob로 뭉개면 안 됩니다.
체크리스트
- retry와 request latency를 비교했다
delivery.timeout.ms를 확인했다request.timeout.ms를 확인했다acks, idempotence, in-flight limit를 확인했다- 최근 leader 또는 ISR 변화를 확인했다
FAQ
Q. retry가 높아지면 retries를 줄여야 하나요?
보통은 아닙니다. Kafka 문서도 retry behavior는 delivery.timeout.ms로 읽는 편이 낫다고 설명합니다.
Q. cluster가 대체로 건강한데 retry만 오를 수 있나요?
네. producer가 느려진 acknowledgement나 일시적 path pressure를 흡수하는 중일 수 있습니다.
Q. 왜 acks=0이면 얘기가 달라지죠?
producer가 broker acknowledgement를 거의 기다리지 않기 때문에 retry가 같은 의미로 작동하지 않기 때문입니다.
Q. retry가 ordering risk가 되는 조건은 뭔가요?
retry가 가능하고, idempotence가 꺼져 있고, max.in.flight.requests.per.connection이 1보다 크면 ordering risk가 생길 수 있습니다.
Read Next
- retry가 broker hotspot이나 leadership skew 뒤에 올라갔다면 Kafka Leader Imbalance를 보세요.
Related Posts
Sources:
- https://kafka.apache.org/42/configuration/producer-configs/
- https://kafka.apache.org/42/operations/basic-kafka-operations/
먼저 읽어볼 가이드
검색 유입이 많은 핵심 글부터 이어서 보세요.
- 미들웨어 트러블슈팅 가이드: Redis, RabbitMQ, Kafka 중 어디부터 볼까 Redis, RabbitMQ, Kafka가 함께 있는 시스템에서 지금 보이는 장애가 어느 계층에 더 가까운지, 첫 10분 안에 무엇을 확인하고 어떤 글로 들어가야 하는지 정리한 실전 허브 가이드입니다.
- Kubernetes CrashLoopBackOff: 먼저 볼 것들 startup failure, probe, config, resource limit 관점에서 CrashLoopBackOff를 어떻게 나눠서 봐야 하는지 정리한 가이드입니다.
- Astro 기술 블로그 SEO 체크리스트: 트래픽 기다리기 전에 먼저 고칠 것 Astro 기술 블로그를 위한 실전 SEO 체크리스트입니다. 배포 호스트 확인, robots.txt, sitemap, canonical, hreflang, 구조화 데이터, 페이지별 메타데이터, noindex 판단, 검증 명령까지 우선순위대로 정리합니다.
- 다국어 블로그 canonical과 hreflang 설정 가이드: 무엇을 확인하고 어디서 깨질까 다국어 블로그에서 canonical과 hreflang을 어떻게 설정해야 하는지 실전 기준으로 정리합니다. self-canonical, 상호 연결되는 hreflang 묶음, x-default, 카테고리 페이지, 최종 렌더 HTML 점검, 한 언어 버전이 다른 언어 버전을 눌러버리는 실수까지 다룹니다.
- OpenAI Codex CLI 설치 가이드: 설치, 인증, 첫 작업까지 OpenAI Codex CLI를 실전 기준으로 설치하는 방법을 정리했다. 설치, 로그인, 첫 실행, Windows 주의점, 첫 작업을 어떻게 시작하면 좋은지까지 다룬다.