콘텐츠로 이동

Message Delivery Semantics

Kafka가 프로듀서에서 컨슈머까지 메시지를 전달할 때 보장하는 신뢰성 수준을 조절할 수 있으며, 이는 시스템의 요구사항에 따라 성능과 데이터 신뢰성 사이의 균형을 맞추는 중요한 설정이다.

메시지가 유실될 수는 있지만 절대 중복되지는 않음을 의미하며, 가장 낮은 신뢰성 수준을 제공하고 최고의 성능을 목표로 할 때 사용된다.

  • Producer 설정: acks=0 지정
    • 응답 대기 생략: send() 요청 후 브로커 응답을 기다리지 않아 네트워크 왕복 시간(RTT) 절약
    • 위험성: 브로커 장애나 네트워크 단절 시 메시지 유실 발생
  • Consumer 설정: 메시지 처리 전 오프셋 커밋
    • 자동 커밋 사용 시 처리 완료 전 커밋이 발생하여 유실 가능성 존재

메시지가 중복될 수는 있지만 절대 유실되지는 않음을 의미하며, 대부분의 애플리케이션에서 기본적으로 채택하는 신뢰성 수준이다.

  • Producer 설정: acks=allretries > 0 지정
    • 전송 보장: ISR의 모든 브로커가 메시지를 받았음을 확인 후 성공 간주
    • 중복 발생 시나리오: 브로커가 메시지 저장 후 응답을 보내기 전 장애 발생 시 프로듀서가 재전송을 시도하여 중복 기록됨
  • Consumer 설정: 메시지 처리 완료 후 수동 오프셋 커밋
    • 처리 방식: poll() 배치의 로직이 모두 끝난 후 commitSync() 또는 commitAsync() 호출
    • 중복 발생 시나리오: 로직 성공 후 커밋 전 컨슈머 장애 시 재시작 후 마지막 커밋 지점부터 다시 읽음

멱등적 소비자(Idempotent Consumer) 구현 전략

섹션 제목: “멱등적 소비자(Idempotent Consumer) 구현 전략”

At-least-once 방식에서 발생하는 중복 처리를 방지하기 위해 소비자의 멱등성 확보가 필수적이다.

  • 데이터베이스 활용: 메시지의 고유 키(예: 이벤트 ID, 주문 ID)를 데이터베이스의 Primary Key나 Unique Key로 사용하여 중복 삽입 방지
  • 버전 관리: 데이터에 버전 번호를 두고, 현재 저장된 버전보다 낮은 버전의 데이터가 들어오면 무시
  • 상태 저장소 활용: Redis나 DB에 처리된 메시지 ID를 저장하고, 메시지를 처리하기 전에 ID의 존재 여부를 먼저 확인하여 중복 실행 방지

Kafka의 순서 보장은 파티션 단위로만 성립하며, 그 외 단위에서는 별도 설계와 설정이 없으면 순서가 깨질 수 있다.

같은 파티션에 적재된 메시지는 프로듀서가 보낸 순서대로 오프셋이 매겨지고, 컨슈머도 동일한 순서로 읽는다.

  • 토픽 전체에 대한 순서 보장은 제공하지 않으며, 파티션 단위 보장만 존재
  • 같은 도메인 식별자(userId, orderId 등)의 순서를 유지하려면 해당 식별자를 메시지 키로 지정하여 동일 파티션으로 라우팅
  • 파티션은 컨슈머 그룹 내 단 하나의 컨슈머에게만 할당되므로, 컨슈머 측 단일 스레드 처리까지 유지되어야 순서가 보존

파티션 단위 순서 보장이 깨지거나 의도와 다르게 동작할 수 있는 대표 상황은 다음과 같다.

  • Producer 재시도로 인한 역전
    • max.in.flight.requests.per.connection이 1보다 크고 enable.idempotence=false인 상태에서 retries가 활성화되면 발생
    • 메시지 A(Seq 1)가 네트워크 오류로 실패한 사이 메시지 B(Seq 2)가 먼저 기록되고, 이후 A가 재전송되면 브로커 로그에는 B, A 순서로 저장
  • Key 없는 메시지의 분산 라우팅
    • 키가 지정되지 않은 메시지는 Sticky Partitioner가 처리량을 위해 임의 파티션에 배치
    • 같은 도메인 식별자로 묶여야 할 메시지가 서로 다른 파티션에 흩어져 컨슈머 간 처리 순서 보장 불가
  • 파티션 수 변경
    • 운영 중 파티션을 추가하면 hash(key) % partition_count 결과가 바뀌어 같은 키의 메시지가 변경 전후로 다른 파티션에 적재
    • 기존 파티션에 처리되지 않은 lag이 남아 있는 상태에서 변경되면 신/구 파티션 사이 처리 순서가 뒤섞일 수 있음
  • Consumer 멀티스레드 처리
    • 단일 파티션의 메시지를 worker pool로 분산하여 병렬 처리하면 그 시점부터 순서 보장이 깨짐
    • Kafka가 제공하는 보장은 파티션 → 단일 컨슈머 스레드까지이며, 이후 처리 모델은 애플리케이션 책임
  • 리밸런싱 중 중복 처리
    • 파티션 소유권이 이동할 때 직전 컨슈머가 커밋하지 못한 메시지를 새 컨슈머가 다시 처리할 수 있음
    • 순서 자체가 뒤바뀌는 것은 아니지만 같은 메시지가 중복 적용되어 도메인 정합성이 깨질 수 있음

상황별 원인을 사전에 차단하기 위한 프로듀서·컨슈머·운영 측 설정과 패턴이다.

  • 멱등적 프로듀서: enable.idempotence=true (Kafka 3.0 이후 기본값)
    • 시퀀스 검증: 브로커는 파티션별로 프로듀서의 시퀀스 번호를 추적하여 누락된 요청(Gap)은 거절하고, 이미 처리된 번호는 중복으로 판단해 저장하지 않음
    • 윈도우 보장: 브로커는 최근 5개의 시퀀스 번호를 관리하므로, max.in.flight.requests.per.connection을 5 이하로 설정하면 처리량을 유지하면서 순서 보장 가능
  • 키 라우팅: 도메인 식별자를 메시지 키로 지정하여 같은 단위의 메시지가 동일 파티션에 적재되도록 강제
  • 파티션 수 사전 산정: 예상 처리량을 기준으로 충분한 파티션을 미리 확보하고 운영 중 변경 최소화
  • 컨슈머 측 키 기반 분배: 단일 컨슈머가 받은 메시지를 멀티스레드로 분산해야 한다면, 같은 키는 항상 같은 worker 큐로 보내 파티션 단위 순서를 처리 단계까지 연장
  • cooperative-sticky 할당 전략과 group.instance.id(Static Membership): 리밸런싱 영향 범위를 최소화하여 일시 중단과 재처리 빈도 감소

이미 순서가 뒤섞이거나 중복으로 발행된 메시지를 사후에 정렬하는 것은 어렵기 때문에, 컨슈머 측에서 도메인 상태가 잘못 갱신되지 않도록 처리하거나 발행 단계의 정합성을 강화하는 방향으로 접근한다.

  • 페이로드의 시퀀스 / 버전 필드 검증
    • 메시지에 도메인 단위의 단조 증가 값(version, updatedAt 등)을 포함하고, 컨슈머는 현재 저장된 값보다 낮은 버전의 메시지를 무시
    • 예: UPDATE ... SET ... WHERE id = ? AND version < ?
  • 멱등적 컨슈머
    • 메시지 고유 식별자(event_id)를 데이터베이스의 Unique Key로 활용하여 재처리 시 중복 삽입 방지
    • 처리 결과와 오프셋 커밋을 동일 트랜잭션으로 묶어 부분 성공 상태 제거
  • 발행 단계 강화 (Outbox)
    • DB 트랜잭션과 메시지 발행을 분리하면 프로듀서 단계에서 순서·유실이 발생할 수 있음
    • Outbox 테이블에 이벤트를 트랜잭션 내에서 기록하고 별도 프로세스(CDC 등)가 Kafka로 발행하면 DB 변경 순서가 그대로 보존
  • Dead Letter Queue (DLQ) 격리
    • 특정 메시지에서 처리 실패가 반복되면 후속 메시지가 모두 막혀 lag이 누적되고 사고가 확산
    • N회 재시도 후 해당 메시지를 별도 DLQ 토픽으로 이동시켜 본 스트림은 진행, DLQ는 별도 배치로 분석·재처리

메시지가 유실되거나 중복되지 않고 정확히 단 한 번만 처리됨을 보장하는 가장 강력한 신뢰성 수준이다.

  • Producer: enable.idempotence=true / transactional.id=<UNIQUE_ID> 설정
    • enable.idempotence=true로 설정하여 브로커로의 메시지 중복 전송 방지
    • transactional.id를 고유하고 안정적인 값으로 설정하여 트랜잭션을 활성화
      • 이를 통해 여러 토픽 파티션에 걸친 ‘읽기-처리-쓰기’ 작업을 하나의 원자적 단위로 묶어줌
  • Consumer: isolation.level=read_committed 설정
    • 컨슈머는 최종적으로 커밋(Commit)된 트랜잭션에 포함된 메시지만 읽도록 설정
    • 만약 트랜잭션이 처리 도중 중단(Abort)되면, 해당 트랜잭션에 포함된 모든 메시지는 컨슈머에게 노출되지 않아 데이터 정합성 보장

전송 보장 수준별 성능 및 트레이드오프

섹션 제목: “전송 보장 수준별 성능 및 트레이드오프”

신뢰성이 높아질수록 추가적인 네트워크 통신과 디스크 I/O가 발생하여 성능 하락이 수반된다.

보장 수준처리량(Throughput)지연 시간(Latency)신뢰성주요 용도
At-most-once최고최저낮음로그 수집, 단순 모니터링 지표
At-least-once높음보통높음일반적인 비즈니스 이벤트 처리
Exactly-once보통높음최고금융 결제, 핵심 데이터 동기화

마지막 업데이트:

Kafka