Kafka Producer Retries: 숫자보다 timing과 guarantee를 먼저 봐야 한다
Dev
마지막 업데이트

Kafka Producer Retries: 숫자보다 timing과 guarantee를 먼저 봐야 한다


프로덕션에서 결제 이벤트를 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.mssend()가 반환된 뒤 성공 또는 실패를 보고하기까지 허용되는 전체 상한 시간이라고 정의합니다.

그 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 slowdownbroker 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.msrequest.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가 생길 수 있습니다.

Sources:

먼저 읽어볼 가이드

검색 유입이 많은 핵심 글부터 이어서 보세요.

광고