본문 바로가기
Spring

[Kafka & Zookeeper 일지] Subscriber 에서 생기는 하나의 테이블에 같은 객체 쓰기(write)에 대한 동시 접근 제어

by onejunu 2021. 3. 16.

 

만약 FOO라는 객체에 속성이 A부터 D까지 4가지가 있다. 빨간 박스인 A가 primaryKey 이다.

 

 

먼저 카프카 클러스터에는 2개의 브로커가 떠있고 Subscriber가 2개의 토픽을 구독하고 있는 상황이다.

 

 

 

topic1 에서 읽은 메세지인 Foo의 pk 인 A와 topic2 에서 읽은 메세지인 Foo의 pk인 A 가 다르다면 문제 되지 않는다.

하지만 pk가 같은 객체에 똑같이 쓰기를 한다면 다음과 같이 생각할 것이다.

 


1. topic1에서 읽어 드린 메세지에서 똑같은 pk를 가진 객체가 DB에 있는지 검사후 없다면 DB에 생성한다.

1-1. 똑같은 pk를 가진 객체가 DB에 있다면 B,C 속성을 업데이트한다.

2. topic2에서 읽어 드린 메세지에서 똑같은 pk를 가진 객체가 DB에 있는지 검사후 없다면 DB에 생성한다.

2-1. 똑같은 pk를 가진 객체가 DB에 있다면 D 속성을 업데이트한다.


위와 같은 접근은 문제가 있다.

카프카 브로커를 통해 Topic을 소모하는 과정은 비동기적으로 일어난다는 것이다. 위 순서에서 우리가 일어나길 기대하는 상황은 1번과 2-1번이 일어나는것과 1-1번과 2번이 일어나는 상황이다. 하지만 1번과 2번이 동시에 일어날 수 있다는 것이다. 

 

그렇다면 동기화를 해야하는가?? 그보다 더 쉬운 아이디어가 있다.

 

Foo객체를 생성하는 로직은 하나의 토픽에서만 사용하고 나머지 토픽은 업데이트만 사용하는 것이다.

 

만약 업데이트하는 토픽을 먼저 Consume하면 Foo객체가 다른 토픽에서 생성되길 기대하며 기다리는 것이다. 여기서 필요한 전제조건이있다. Foo객체를 업데이트하는 토픽은 반드시 Foo객체를 생성하는 토픽에 의존해야한다.  생성됨을 반드시 보장해야하는 것이다. 

 

그림으로 보면 아래와 같다.

 

 

1.  2개의 메세지가 동시에 도착한다.

2.  "Foo생성"은 Foo를 생성하고 "Foo업데이트"는 해당 pk가 없으므로 기다린다.

화살표의 순서는 일어나는 일의 순서이다. 1번은 동시에 일어나는 일이다.

3. pk로 Foo를 찾았더니 있으면 업데이트하고 마무리한다.

데이터베이스 레벨에서 동시성을 제어하는 것이 가장 이상적이지만 더욱 좋은 아이디어가 생기면 다시 업그레이드를 하겠다.

 

 

댓글