메시지 전달 시스템이 약속하는 수준은 최소 한 번과 정확히 한 번으로 갈린다. 최소 한 번은 성공할 때까지 재시도해서 중복을 감수하고, 정확히 한 번은 딱 한 번만 반영되는 대신 비용을 치른다. 카프카가 기본으로 보장하는 것은 최소 한 번이다.
유실과 중복 사이의 선택
기본이 최소 한 번인 이유는 재시도의 대가에 있다. 응답을 못 받았을 때 다시 보내지 않으면 유실이고, 다시 보내면 중복이다. 어느 쪽으로 가든 손해가 나는데 유실보다 중복이 낫다는 판단이 최소 한 번이다.
중복이 문제가 되는 자리
중복이 별것 아닌 경우도 많다. 개별 레코드를 변환하거나 필터링만 한다면 갱신할 상태가 없으니 출력 스트림에서 걸러내면 된다. 문제는 집계와 조인이다. 여러 레코드를 합쳐 하나로 만들면 결과 레코드가 잘못되었는지 판단할 방법이 없다. 어떤 입력이 두 번 처리되었는지 결과만 보고는 알 수 없고, 그 입력을 다시 처리하지 않으면 결과를 고칠 수도 없다.
중복 이벤트 하나 때문에 평균값이 틀어진 것을 알아내는 일이 실제로 얼마나 어려운지가 이 기능이 존재하는 이유다. 금융 애플리케이션이 전형적인 예다.
멱등적 프로듀서와 트랜잭션의 조합
두 기능의 조합으로 도달한다. 멱등적 프로듀서가 프로듀서 재시도로 생기는 중복을 브로커 쪽에서 걸러내고, 카프카 트랜잭션이 결과 쓰기와 오프셋 커밋을 원자적으로 묶는다. 각각 다른 원인의 중복을 막으므로 하나만으로는 부족하다.
보장의 경계
보장의 경계는 카프카에 쓰는 것까지다. 이메일 발송이나 REST API 호출 같은 부수 효과, 외부 DB에 쓰기, 클러스터 간 복제의 원자성은 여기에 들어오지 않는다. 무엇이 왜 빠지는지는 트랜잭션이 해결하지 못하는 다섯 가지에서 본다.
데이터 파이프라인 관점에서 보면, 카프카 자체는 최소 한 번을 보장하고 트랜잭션 모델이나 고유 키를 지원하는 외부 데이터 저장소와 결합되었을 때 정확히 한 번까지 간다. 많은 엔드포인트가 그런 저장소이므로 실무의 카프카 파이프라인은 대체로 정확히 한 번을 달성하지만, 그것은 카프카 혼자 한 일이 아니다.
참고
원본에 나오지 않는 세 번째 수준이 하나 더 있다. 보내고 잊어버려서 유실을 감수하는 최대 한 번이다. 카프카 문서는 최대 한 번을 “메시지가 유실될 수는 있으나 다시 전달되지는 않는다”, 최소 한 번을 “메시지가 유실되지는 않으나 다시 전달될 수 있다”, 정확히 한 번을 “각 메시지가 단 한 번만 처리된다”로 적고, 카프카가 기본으로 최소 한 번을 보장하며 프로듀서의 재시도를 끄고 컨슈머가 배치를 처리하기 전에 오프셋을 커밋하면 최대 한 번이 된다고 덧붙인다. Apache Kafka, Design - Message Delivery Semantics