피드로 돌아가기
카카오 기술블로그Backend
원문 읽기
잃어버린 리포트를 찾아서: 카카오 메시징 시스템의 경쟁 조건 문제와 안티 패턴 제거 과정
카카오 메시징 시스템이 경쟁 조건으로 인한 리포트 누락(0.02%)을 Single Writer Principle과 Outbox 패턴 도입으로 구조적 제거
AI 요약
Context
KIMS(Kakao Integrated Messaging Service)는 하루 100만 건의 메시지를 처리하는 분산 MSA 시스템으로, API Server가 벤더사 호출 후 DB 영속화를 완료하기 전에 빠른 응답 벤더사(약 8ms)의 리포트가 먼저 도착하는 경쟁 조건이 발생했다. Report Server가 미존재 메시지에 대한 리포트를 Drop하면서 리포트 누락이 발생했으며, 과금 메시지에서만 선택적으로 나타났다.
Technical Solution
- 트랜잭션 범위 최소화: @Async와 @TransactionalEventListener를 사용해 Kafka 이벤트 발행을 DB 커밋 이후 별도 스레드에서 비동기 실행으로 분리
- Transactional Outbox 패턴 도입: Report Server가 수신한 리포트를 Outbox 테이블에 먼저 저장한 후 Report Replayer가 배치 주기로 처리
- Single Writer Principle 적용: Report Replayer를 단일 쓰기 경로로 지정해 메시지 상태 업데이트와 과금 이벤트 발행을 직렬화
- 배치 기반 처리 전환: Report Replayer가 주기적으로 Outbox 테이블을 순회하며 리포트를 메시지 테이블에 반영
- 재시도 메커니즘 통합: Outbox 테이블이 리포트 누락에 대비한 재시도를 담당해 Exactly-once 처리 보장
Impact
- 트랜잭션 평균 커밋 시점 약 10ms 앞당김
- 리포트 누락 건수 눈에 띄게 감소
- Report Server ↔ Report Replayer 간 경쟁 조건 구조적 제거
Key Takeaway
동시성 문제는 코드의 미세한 조정만으로 해결될 수 없으며, 경쟁 자체가 발생하지 않는 아키텍처로 전환할 때에만 근본적으로 제거 가능하다. 과금·정산 도메인에서는 실시간성보다 최종 일관성(Eventual Consistency)을 우선으로 설계해야 한다.
실천 포인트
분산 메시징 시스템에서 비동기 처리로 인한 경쟁 조건이 의심될 때, @TransactionalEventListener와 Outbox 패턴을 조합해 트랜잭션 범위를 최소화하고 단일 쓰기 경로를 설정하면 예측 불가능한 누락 현상을 구조적으로 제거할 수 있다.