본문 바로가기

server/kafka

[kafka] KIP-98 Exactly Once Delivery and Transactional Messaging

https://cwiki.apache.org/confluence/display/KAFKA/KIP-98+-+Exactly+Once+Delivery+and+Transactional+Messaging

 

KIP-98 - Exactly Once Delivery and Transactional Messaging - Apache Kafka - Apache Software Foundation

[This KIP proposal is a joint work between Jason Gustafson, Flavio Paiva Junqueira,  Apurva Mehta, Sriram, and Guozhang Wang] Status Current state: Adopted Discussion thread: http://search-hadoop.com/m/Kafka/uyzND1jwZrr7HRHf?subj=+DISCUSS+KIP+98+Exact

cwiki.apache.org

 

 

 

1.  사건의 시작 
https://cwiki.apache.org/confluence/display/KAFKA/Transactional+Messaging+in+Kafka

2014년 kafka는 at-least-once만 보장하여 다음 문제 발생

- 프로듀서 재시도 -> 중복 메시지

- 컨슈머 재시작 -> 같은 메시지 다시 처리 

 

두번째는 오프셋을 통해 해결 가능하지만, 첫번째는 불가능했음. 

 

이를 해결하기 위해 멱등성(idempotent producer https://cwiki.apache.org/confluence/display/KAFKA/Idempotent+Producer ) 고려. 

 

 

2. 멱등성

1. 첫번째 제안 - UUID

클라이언트가 전송하는 각 메시지에 대해 고유한 ID(예: UUID)를 생성하고, 서버가 보관하는 모든 메시지에 대해 해당 ID를 저장하도록 하는 것 ->  but 최소한 기본 요구 사항은 충족하지만, 브로커가 메시지 마다 데이터베이스에 O(num_messages)를 탐색해야 되어 매우 비효율적

 

2. 두번째 제안 - PID + sequence Number

각 프로듀서에 고유 ID(PID)를 할당하고 전송되는 각 메시지마다 증가하는 일련 번호를 유지하는 것 ->  같은 메시지를 재전송할 때 시퀀스 번호가 이전과 같거나 더 작음 → 브로커가 “이건 이미 본 거네?” 하고 버림

 

 

3. 세번째 제안 파티션별 시퀀스 증가 

파티션에 해당하는 시퀀스가 반드시 1씩만 증가하게 됨 -> 중간에 유실 or 순서 꼬임시 바로 에러 처리 가능 

 

 

3. 트랜잭션 메시지 

다시 트랜잭션으로 돌아와서 

 

프로듀서는 트랜잭션 세션을 명시적으로 시작하고, 해당 세션 내에서 (트랜잭션) 메시지를 전송하고, 트랜잭션을 커밋하거나 중단할 수 있음.  트랜잭션은 다음의 속성을 가지고 있음.

  1. 원자성: 소비자의 애플리케이션은 커밋되지 않은 트랜잭션의 메시지에 노출되어서는 안된다.
  2. 내구성: 브로커는 commit된 데이터는 잃어서는 안된다. 
  3. consumer: 브로커를 인식하는 consumer는 각 파티션 내에서  순서대로 메시지를 확인해야 한다.
  4. 인터리빙: 각 파티션은 트랜잭션 및 비트랜잭션 producer 모두로부터 메시지를 수락할 수 있어야 한다.
  5. 브로커 내에 중복된 메시지가 있어서는 안된다.

트랜잭션 메시지와 비트랜잭션 메시지의 인터리빙이 허용되는 경우, 비트랜잭션 메시지와 트랜잭션 메시지의 상대적 순서는 추가(비트랜잭션 메시지의 경우)와 최종 커밋(트랜잭션 메시지의 경우)의 상대적 순서에 따라 결정됩니다.

 

 

모든 구현은 각 트랜잭션의 입력 상태를 트랜잭션 자체와 연관시킬 수 있는 기능을 제공해야 합니다. 이는 트랜잭션 재시도를 용이하게 하기 위해 필수적입니다. 즉, 트랜잭션을 중단하고 재시도해야 하는 경우, 해당 트랜잭션의 전체 입력을 다시 실행해야 합니다.

 

 

 

 

4. 구현 

트랜잭션 코디네이터(Transaction coordinator) 와 저널 로그(journal log) 를 통해 트랜잭션 관리 

 

Transaction group 은 특정 트랜잭션 코디네이터에 매핑되기 위한 단위다.
예를 들어, __transaction_control 토픽의 파티션 수(= 저널 파티션 수)를 기준으로 해싱을 해서 특정 파티션의 리더 = 특정 코디네이터 로 정할 수 있다.

  • 같은 트랜잭션 그룹에 속한 프로듀서들은 동일한 코디네이터 를 사용한다.
  • 그룹에서 발생하는 트랜잭션들에 대해 엄격한 순서 보장(strict ordering) 이 가능해진다.

 

다음 두개의 토픽으로 멱등성과 트랜잭션을 구현한다. 

__consumer_offsets     = Consumer Group의 커밋 오프셋 저장소 (컨슈머가 데이터를 중복 읽지 않도록 보장)
__transaction_state     = Transaction Coordinator가 유지하는 시스템 상태 저장소 (Producer 트랜잭션의 시작/진행/커밋/중단 상태를 관리)

 

 

간단하게 설명하자면 

 

producer가 중복 메시지를 보낼때 -> PID (Producer ID) +Sequence Number 를 통해서 해당 메시지가 어디까지 왔는지 알수 있으므로(__transaction_state 에 저장되어 있음), producer가 중복으로 메시지를 전송해도 해당 Sequence Number가 더 낮으므로, 중복으로 간주하고 무시하면 된다

 

consumer가 중복 메시지를 받을때 ->  offset/트랜잭션 메커니즘을 통해서 해결하는데, __consumer_offsets 에 consumer가 어디까지 받았는지 offset이 저장되어 있으므로, 해당 consuer가 offset을 통해서 어디까지 읽었는지 파악하고 처리

 

 

결국 

Producer 중복 → Kafka가 PID/Seq 로 자동 제거
Consumer 중복 → offset commit(또는 트랜잭션)을 통해 컨슈머가 해결

가 된다. 

 

 

 

 

 

참고사항

https://bistros.tistory.com/166

https://velog.io/@bcj0114/Kafka-KafkaStreams-%EC%9D%98-Exactly-once-SementicsEOS

https://itsurvival.tistory.com/18

https://m.blog.naver.com/ass1225/222035963333

'server > kafka' 카테고리의 다른 글

[kafka] kafka connect to es  (1) 2025.11.17
Kafka producer/consumer 튜닝 (acks, batch.size, linger.ms 등)  (2) 2025.08.08
kafka-reassign-partitions  (3) 2025.07.30
fluentd grpc ruby 버전 에러  (3) 2025.07.29
ksqldb  (1) 2025.07.17