토픽은 순서를 보장하지 않는다
Kafka가 순서를 보장한다는 말은 절반만 맞다. 정확히는 파티션 안에서만 보장한다.
토픽은 파티션 여러 개로 나뉘고, 각 파티션은 독립적인 로그다. 서로 다른 파티션에 들어간 두 메시지 사이에는 아무런 순서 관계가 없다. 파티션이 병렬 처리의 단위인 이상 이건 피할 수 없다.
그래서 키가 중요하다
프로듀서가 메시지에 키를 지정하면, Kafka는 키를 해싱해 파티션을 정한다. 같은 키는 항상 같은 파티션으로 간다.
주문 이벤트라면 order_id를 키로 쓴다. 한 주문의 생성 → 결제 → 배송 이벤트가 같은 파티션에 순서대로 쌓이고, 컨슈머는 그 순서대로 읽는다. 다른 주문과의 순서는 보장되지 않지만 애초에 그럴 필요가 없다.
순서가 필요한 범위를 키로 표현하는 것이 설계의 핵심이다.
파티션 수의 trade-off
- 늘리면: 병렬 처리량이 올라간다. 컨슈머를 더 붙일 수 있다
- 늘리면: 순서가 보장되는 범위는 그대로지만, 전체적으로 순서가 섞이는 정도는 커진다
주의할 점이 하나 더 있다. 파티션 수를 나중에 늘리면 키의 해시 분배가 바뀐다. 기존 키가 다른 파티션으로 가면서, 전환 시점 전후로 같은 키의 메시지가 두 파티션에 나뉘어 순서가 깨질 수 있다.
컨슈머 쪽 제약
한 컨슈머 그룹 안에서 파티션 하나는 컨슈머 하나에만 할당된다. 그래서 컨슈머를 파티션 수보다 많이 띄워도 남는 컨슈머는 논다. 파티션 수가 병렬성의 상한이다.
재시도가 순서를 깬다
프로듀서가 전송에 실패해 재시도할 때, 뒤 메시지가 먼저 성공하면 순서가 뒤집힌다. max.in.flight.requests.per.connection을 1로 두거나, 멱등 프로듀서(enable.idempotence=true)를 켜면 브로커가 순서를 지켜준다.
정리
“Kafka는 순서를 보장한다”가 아니라 “파티션 단위로 보장한다”로 기억하면 설계가 명확해진다. 순서가 필요한 단위를 정하고, 그걸 키로 삼는 것부터 시작한다.