주문이 두 번 도착했고 한 번은 사라졌다 · 두 번째 도착 · 실습
재시도가 두 번째 도착을 만든다 — 멱등 프로듀서로 막는다
이 실습은 VM 에서 돕니다
우분투 VM 에 Apache Kafka 4.3.1 이 KRaft 단일 노드로 떠 있습니다
(localhost:9092). 도우미 kafka-lab-slow on|off 는 브로커로 가는 4KB 이상의
요청만 600ms 늦춰 프로듀서의 시간 초과와 재시도를 일으킵니다. 처음 뜨는 데
4분쯤 걸립니다.
목표
프로듀서가 응답을 받지 못해 재시도할 때 로그에 같은 레코드가 여러 벌 남는 것을
재현하고, 멱등 프로듀서가 그것을 한 벌로 만드는 것을 kafka-dump-log.sh 로
확인합니다. 그리고 멱등이 덮지 못하는 자리(프로듀서 세션을 넘는 재전송)도
봅니다.
왜 중요한가
설계 문서가 말하듯 프로듀서는 네트워크 오류를 만나면 그 오류가 메시지가
커밋되기 전에 난 것인지 뒤에 난 것인지 알 수 없습니다. 그래서 다시 보내고,
그러면 원래 요청이 성공했던 경우 로그에 두 번 남습니다 — at-least-once 입니다.
멱등 프로듀서는 브로커가 프로듀서마다 ID 를 주고 레코드마다 순번을 붙여
같은 순번을 걸러 내는 방식으로 이것을 막습니다. 4.x 에서는 기본으로 켜져
있지만(enable.idempotence 기본값 true), 설정 하나를 잘못 건드리면 조용히
꺼지고, 세션을 넘는 재전송은 애초에 덮지 않습니다. 이 셋을 눈으로 봐 두면
"두 번 도착" 신고를 받았을 때 어디를 볼지 압니다.
단계
1. 토픽 payments 를 파티션 1개로 만들고 기본 설정으로 두 줄(pay-1, pay-2)을 넣으세요.
2. kafka-dump-log.sh 로 payments-0 의 세그먼트를 덤프해 /root/kafka/dump-default.txt 에 저장하세요. 배치의 producerId 가 -1 이 아니고 baseSequence 가 0 이어야 합니다(기본 프로듀서는 멱등입니다).
3. enable.idempotence=false 로 한 줄(pay-3)을 더 넣고 다시 덤프해 /root/kafka/dump-noidem.txt 에 저장하세요. 새 배치는 producerId: -1 이어야 합니다.
4. kafka-lab-slow on 을 켜고 토픽 payments-dup 을 만든 뒤, 5,000자짜리 한 줄(/root/kafka/big.txt)을 멱등을 끄고 request.timeout.ms=300, delivery.timeout.ms=2000, retries=2, max.block.ms=10000 으로 넣으세요. 프로듀서의 stderr 를 /root/kafka/dup-producer.log 에 남기고, 끝나면 kafka-lab-slow off. 로그에는 같은 레코드가 두 벌 이상 남아야 합니다.
5. 같은 조건으로 토픽 payments-idem 에 멱등을 켜고(enable.idempotence=true) 넣고 stderr 를 /root/kafka/idem-producer.log 에 남기세요. 재시도는 일어나지만 로그에는 한 벌만 남아야 합니다.
6. slow 를 끈 상태에서 토픽 payments-app 에 pay-77 을 프로듀서를 두 번 따로 실행해 넣으세요(멱등은 켠 채). 덤프를 /root/kafka/two-sessions.txt 에 저장하세요 — 배치 두 개의 producerId 가 서로 달라야 합니다.
7. /root/kafka/producer-report.txt 에 dup_copies=<payments-dup 의 레코드 수>, idem_copies=<payments-idem 의 레코드 수>, two_sessions_copies=<payments-app 의 레코드 수>, acks_default=all, idempotence_default=true 다섯 줄을 쓰세요.
참고
- 덤프:
kafka-dump-log.sh --files /var/lib/kafka/<토픽>-0/00000000000000000000.log --print-data-log. 배치 줄에producerId·baseSequence가, 레코드 줄에payload가 나옵니다. - 레코드 수:
kafka-get-offsets.sh --bootstrap-server localhost:9092 --topic <토픽>의 마지막 숫자(로그 끝 오프셋)입니다. - 클라이언트 설정은
--command-property 키=값으로 줍니다(4.3 에서--producer-property는 deprecated). 5,000자 한 줄은head -c 5000 /dev/zero | tr '\0' x > big.txt; echo >> big.txt로 만듭니다. - 프로듀서 설정 문서:
delivery.timeout.ms는request.timeout.ms + linger.ms이상이어야 하고, 멱등을 켜려면acks=all,retries>0,max.in.flight.requests.per.connection<=5여야 합니다. 멱등을 명시적으로 켠 채 충돌하는 값을 주면 ConfigException 입니다. - 흔한 실수 1: slow 를 켠 채 4단계를 끝내고 끄지 않는 것. 작은 요청은 영향이 없어 눈치채기 어렵지만, 큰 레코드를 다루는 다음 단계가 느려집니다.
- 흔한 실수 2: 6단계에서 한 프로듀서에 두 줄을 넣는 것. 그것은 같은 세션이라 순번이 이어지고 한 배치가 됩니다. "두 번 따로 실행" 이어야 세션이 둘입니다.
단계 7개
- 기본 프로듀서로 두 건
- 기본 프로듀서는 멱등이다
- 멱등을 끄면 ID 가 없다
- 재시도가 두 번째 도착을 만든다
- 멱등 프로듀서는 한 벌만 남긴다
- 세션이 다르면 멱등이 덮지 못한다
- 세 토픽의 레코드 수로 정리한다