피드로 돌아가기
Transactional Outbox 패턴으로 메시지 발행 보장하기
리디 기술블로그리디 기술블로그
Backend

Transactional Outbox 패턴으로 메시지 발행 보장하기

리디가 Transactional Outbox 패턴을 도입해 DB 트랜잭션과 Kafka 메시지 발행을 원자적으로 처리하고 메시지 순서 보장

Gyu Kang2024년 1월 17일11intermediate

Context

MySQL과 Kafka를 사용하는 리디 서비스에서 DB 트랜잭션 완료 후 Kafka에 메시지를 발행했을 때 발행 실패로 인해 메시지 손실 위험이 발생했다. DB 트랜잭션이 완료되어도 메시지 발행을 보장할 수 없었으며, dead letter queue를 통한 배치 retry는 신속하지 않았고 메시지 간 발행 순서를 보장하지 못했다.

Technical Solution

  • Transactional Outbox 패턴을 Polling Publisher 방식으로 구현: DB 트랜잭션 실행 시점에 발행할 메시지 정보를 message 테이블에 저장해 원자성 보장
  • message 테이블(id, topic, type, key, payload, source, created_at, updated_at 컬럼)과 processed_message 테이블(id 컬럼)을 분리: DB Lock 경합을 완화하기 위해 처리 완료된 메시지 ID를 임시 저장 후 삭제
  • Message Relay loop 구현: 일정 간격으로 message 테이블을 polling해 미발행 메시지를 조회하고 Kafka에 batch로 발행
  • 메시지를 CloudEvent 형식으로 변환: Kafka 발행 시 모든 메시지를 CloudEvent 표준으로 인코딩
  • Graceful shutdown 구현: SIGTERM/SIGINT 신호 수신 시 현재 실행 중인 loop가 완료될 때까지 대기
  • Datadog APM 모니터링: Message Relay loop를 span으로 기록해 처리량, 에러 빈도, latency 추적
  • 임계치 기반 alert 설정: 현재 시각과 메시지 입력 시각의 차이가 60초 이상이면 에러 추적 서비스에 전파, processed_message 테이블 row 개수 초과 시 Slack alert

Impact

아티클에서 정량적 성능 수치를 명시하지 않음.

Key Takeaway

분산 트랜잭션 문제가 발생하는 다중 기종 시스템에서는 Transactional Outbox 패턴을 통해 메시지 발행을 DB 트랜잭션에 포함시킴으로써 메시지 손실을 근본적으로 방지할 수 있다. Polling Publisher 방식은 구현이 단순하지만 DB Lock 경합, deadlock, 지연 시간 누적 등의 운영 이슈를 지속적으로 모니터링하고 튜닝해야 한다.


MySQL과 Kafka를 함께 사용하는 Event Driven Architecture에서 중요한 비즈니스 이벤트를 발행할 때, Transactional Outbox 패턴의 Polling Publisher 방식을 도입하면 DB 트랜잭션과 메시지 발행의 원자성을 보장해 메시지 손실과 순서 뒤바뀜을 방지할 수 있다. 다만 복수의 relay node 운영 시 processed_message 테이블을 별도로 분리해 DB Lock을 완화하고, Datadog 같은 모니터링 도구를 통해 message 지연 시간과 processed_message 누적을 실시간으로 추적해야 한다.

원문 읽기