Chapter 08. ‘정확히 한 번’ 의미 구조
8.1 멱등적 프로듀서 ✨
8.1.1 멱등적 프로듀서란 🤔
- 멱등성은 동일한 작업을 여러 번 실행해도 결과가 동일한 작업을 의미
데이터베이스 예시
1 2 3 4 5
-- 멱등적이지 않음 (실행할 때마다 결과가 달라짐) UPDATE t SET x = x + 1 WHERE y = 5; -- 멱등적임 (여러 번 실행해도 결과가 같음) UPDATE t SET x = 18 WHERE y = 5;
- 카프카에서 메시지 전송 시 네트워크 지연이나 장애로 인해 중복 메시지 발생 가능
- 파티션 리더 장애 발생 이후, 새로운 리더로 교체되면 중복 메시지가 저장될 가능성 존재
8.1.2 멱등적 프로듀서의 작동 원리 ⚙️
- 메시지에 프로듀서 ID(PID)와 시퀀스 넘버를 추가해 메시지 고유성 식별
- 브로커는 각 파티션에서 마지막 5개의 메시지를 추적해 중복 메시지 차단
- 예상보다 높은 시퀀스 넘버가 수신되면
out of order sequence number
에러 발생
8.1.3 장애 발생 시 처리 방법 🚨
- 프로듀서 재시작
- 장애 후 새로운 프로듀서 생성 시, 새로운 PID를 할당받아 기존 메시지 중복 감지 불가
- 브로커 장애 복구
- 리더 변경 시 새로운 리더가 이전 리더의 시퀀스 상태를 이어받아 중복 메시지 방지
- 종료 전 상태를 스냅샷으로 저장해 복구 시 활용
8.1.4 멱등적 프로듀서 사용법 📋
enable.idempotence=true
를 설정해 활성화acks=all
과 함께 사용 시 성능 저하 없음- 메시지 순서를 유지하며 중복 메시지를 차단
8.1.5 멱등적 프로듀서의 한계 ⚠️
- 내부 재시도로 발생한 중복만 방지
- 애플리케이션에서 동일 메시지를
producer.send()
로 여러 번 호출하면 중복 발생 가능 - 다중 프로듀서 환경에서 중복 가능성
- 여러 프로듀서가 동일 데이터를 처리하면 중복 메시지가 발생할 수 있음
8.2 트랜잭션 🔄
8.2.1 트랜잭션 도입 배경 🏗️
- 카프카의 스트림 처리 애플리케이션에서 정확히 한 번 처리를 보장하기 위해 도입
- 입력 레코드와 처리한 결과를 출력하며, 집계 및 조인 작업에서 데이터의 정확성 보장
8.2.2 트랜잭션의 주요 기능 💡
- 메시지 처리와 오프셋 커밋을 원자적으로 수행
- 트랜잭션 실패 시 메시지 출력과 오프셋 커밋을 모두 중단
- 동일
transactional.id
를 가진 좀비 인스턴스를 차단해 메시지 중복 방지
8.2.3 트랜잭션이 해결하는 문제 ✅
- 애플리케이션 장애로 인한 중복 처리 방지
- 메시지 출력 후 오프셋 커밋 전에 애플리케이션 장애 발생 시 중복 처리 방지
- 좀비 애플리케이션 문제 해결
- 하트비트를 잃은 애플리케이션이 재활성화되어 동일 메시지 처리 시도를 차단
8.2.4 트랜잭션으로 해결할 수 없는 문제 ❌
- 외부 시스템과의 작업은 트랜잭션으로 처리 불가 (ex. 이메일 발송, API 호출)
- 카프카 메시지를 데이터베이스에 저장하는 작업은 보장 불가
- 클러스터 간 데이터 복제에서 정확히 한 번 처리는 보장되지 않음
8.2.5 트랜잭션 사용법 🛠️
- 트랜잭션 활성화
enable.idempotence=true
설정 필수- 트랜잭션 ID(
transactional.id
) 지정 필요
- 코드 사용 흐름
beginTransaction()
: 트랜잭션 시작sendOffsetsToTransaction()
: 소비한 오프셋을 트랜잭션에 포함commitTransaction()
: 메시지와 오프셋 확정abortTransaction()
: 메시지와 오프셋 롤백
- 주요 특징
- 프로듀서 실패 시, 트랜잭션 복구 가능
- 컨슈머 그룹의 정확히 한 번 처리(
exactly-once semantics
) 보장 - 단일 트랜잭션 내에서 동일한 Topic-Partition에 대한 메시지 순서 보장
- 주의사항
- 트랜잭션 처리 시간 제한이 있으며 초과 시 오류 발생
8.2.6 트랜잭션 ID와 펜싱 🆔
- 트랜잭션 ID
- 프로듀서 인스턴스마다 고유하게 설정
- 트랜잭션 상태와 Epoch 번호로 인스턴스 충돌 방지
- 동일한 트랜잭션 ID를 가진 이전 인스턴스(좀비 프로듀서)는 차단됨
- 펜싱(Fencing)
- Epoch 번호를 증가시켜 이전 세션의 작업 차단
- 장애 복구 중 프로듀서 중복 실행을 방지
8.2.7 트랜잭션의 작동원리 🔍
- 트랜잭션 시작
beginTransaction()
호출로 상태 초기화- 트랜잭션 ID로 파티션과 프로듀서 연결
- 트랜잭션 진행
- 메시지 기록 시 각 메시지에 트랜잭션 정보를 포함
- 트랜잭션 종료
commitTransaction()
호출로 메시지를 확정 (커밋 로그에 반영)abortTransaction()
호출 시 메시지를 폐기
- 트랜잭션 관리
- 컨트롤러와 코디네이터가 트랜잭션 상태 및 오프셋 처리 관리
8.3 트랜잭션 성능 🚀
프로듀서 측 성능 ⚡
- 트랜잭션 초기화와 커밋 요청이 동기적으로 처리돼 데이터 전송 지연 가능
- 트랜잭션 내 메시지 수가 많을수록 오버헤드 감소 및 처리량 증가
컨슈머 측 성능 📊
read_committed
모드에서 열린 트랜잭션 메시지가 반환되지 않아 지연 발생 가능- 브로커가 열린 트랜잭션 메시지를 반환하지 않아 처리량에는 영향 없음
참고 🕶️
This post is licensed under CC BY 4.0 by the author.