같은 키를 갖는 메시지는 "항상" 같은 파티션에 들어갈까?
위 질문에 대한 답을 구하고자 포스팅을 작성한다.
위 질문에 대한 답이 Yes라면 메시지에 키를 설정하는 것에 대해 회의적일 것이고 No라면 긍정적일 것이다.
메시지의 키의 역할
카프카 토픽에 들어가는 메시지는 key와 value의 쌍이다.
여기서 key는 Optional 필드이고 value를 필수이다.
일반적으로 키는 메시지가 저장되는 파티션을 지정하기 위해 사용되는 정보이며 Partitional(파티셔너)에 의해 특정 파티션으로 메시지가 보내진다.
또한, 이 포스팅의 핵심이라 할 수 있는 기본적인 개념인 "같은 키를 갖는 메시지는 같은 파티션으로 할당"되기에 같은 키를 갖는 메시지에 대해 순서를 보장할 수 있다.
질문의 답이 No 이여야 하는 이유
쇼핑몰 사이트를 운영한다고 가정해보자.
특정 유저로부터 발생하는 이벤트들을 하나의 토픽에 담고자 한다. 그리고 각 유저로부터 발생하는 이벤트들은 순서를 보장할 필요가 있다. 그래서 username을 메시지의 key롤 사용하고자 한다.
해당 쇼핑몰은 1000만 명의 회원을 가지고 있고 신규 회원의 크진 않지만 기존 회원들이 열심히 서비스를 이용하고 있어 이벤트가 증가하는 추세이다.
이에 대한 대응을 위해 컨슈머 그룹 내 병렬 처리를 위해 토픽의 파티션을 늘리고자 한다.
현재 아래와 같음
아래와 같이 파티션을 늘렸음
이때 user1에 대한 이벤트가 발생했다. 의도한 바는 더 빠른 병렬 처리를 위해 파티션을 늘렸지만 "같은 키를 갖는 메시지는 같은 파티션에 할당"되기에 의도대로 동작하지 않는다.
기존 컨슈머 그룹 내 Consumer를 추가했지만 놀게 될 수 있다.
그러면 실제로 그러한지 확인해 보자.
간단히 예제로 확인해 볼 것이다.
users 토픽의 파티션을 2로 설정한 뒤 user0~user9 까지의 키를 갖는 메시지를 무한 루프를 돌려서 계속 생성할 것이다.
그러면 partition0과 partition1에 적절히 분배되어 메시지가 생성될 것이다.
이때 파티션을 3으로 늘렸을 때 partition2엔 아무런 메시지가 없는지 확인할 것이다.
아래와 같은 코드로 메시지를 계속 생성했다.
from confluent_kafka import Producer
producer = Producer({"bootstrap.servers": "localhost:9092"})
topic_name = "users"
while True:
for i in range(100):
producer.produce(
topic=topic_name,
key=f"user{i}",
value=f"user{i} event"
)
producer.flush()
이때 partition0 확인
partition1 확인
여기서 partition 2 -> 3으로 변경
아래 명령어를 통해 변경했음.
kafka-topics --bootstrap-server localhost:9092 --topic users --alter --partitions 3
이제 partition 2에 메시지가 있는지 확인하면 된다.
다행히 partition2 에도 메시지가 들어가고 있다.
사실 여기 포스팅을 확인해 보면 "If the number of partitions changes, this delivery guarantee may no longer hold"와 같이 설명되어 있다.
이 말에 대해 크게 와닿지가 않아서 확인해 본 부분이고 같은 키를 갖는 메시지에 대해 "항상" 같은 파티션에 할당된다는 것은 잘못된 표현이다.
(이를 해결하기 위해 파티션을 미리 충분히 만들어두기도 한다고 포스팅에 나와있다.)