[시스템 설계] Transactional Outbox로 DB 변경과 이벤트 발행 다루기
DB 변경과 이벤트 발행 사이에 실패가 발생하는 경우를 살펴보고, Transactional Outbox의 동작 과정과 주의할 점을 정리합니다.
개요
DB에 데이터를 저장한 이후 다른 시스템에 이벤트를 전달해야 하는 상황을 가정해보겠습니다.
가장 단순한 방식은 DB 변경을 커밋한 이후 이벤트를 발행하는 것입니다.
1. DB 변경을 커밋
2. 이벤트 발행
Q) DB 변경이 성공했다면 이벤트도 정상적으로 전달될까요?
A) DB 커밋 이후 이벤트 발행 전에 프로세스가 종료된다면, DB에는 변경이 반영되지만 이벤트는 전달되지 않을 수 있습니다.
이벤트를 먼저 발행하는 경우
순서를 바꿔서 이벤트를 먼저 발행할 수도 있습니다.
하지만 이벤트를 발행한 이후 DB 변경이 실패하면, 다른 시스템은 반영되지 않은 변경에 대한 이벤트를 처리할 수 있습니다.
| 실행 순서 | 중간에 실패하는 경우 |
|---|---|
| DB 커밋 → 이벤트 발행 | DB 변경은 반영되었지만 이벤트 전달 누락 |
| 이벤트 발행 → DB 커밋 | 이벤트는 전달되었지만 DB 변경 실패 |
순서를 바꾸는 것만으로는 문제를 해결할 수 없습니다.
일반적인 로컬 DB 트랜잭션은 외부 메시지 브로커의 작업까지 하나의 커밋으로 묶지 못하기 때문입니다.
Transactional Outbox 동작 과정
Q) DB 변경이 커밋되었을 때 발행할 이벤트도 함께 남길 수 있는 방법은 무엇일까요?
A) 업무 데이터와 Outbox 이벤트를 같은 DB 트랜잭션에 저장할 수 있습니다.
하나의 DB 트랜잭션
├─ 업무 데이터 변경
└─ Outbox 이벤트 저장
↓ 커밋 후
별도 발행기 → 메시지 브로커 → 소비자
- 업무 데이터를 변경하면서 Outbox에 발행할 이벤트를 저장합니다.
- 같은 트랜잭션을 커밋합니다.
- 별도 발행기가 커밋된 Outbox 레코드를 읽어 메시지 브로커로 전달합니다.
- 소비자는 전달받은 이벤트를 처리합니다.
트랜잭션이 롤백되면 업무 데이터와 Outbox 이벤트도 함께 롤백됩니다.
커밋 이후 발행이 실패하더라도 Outbox에 남아 있는 이벤트를 재시도 대상으로 사용할 수 있습니다.
이때 같은 트랜잭션으로 저장하는 것은 업무 데이터와 발행할 이벤트입니다. 소비자의 처리 완료까지 하나의 트랜잭션으로 묶는 것은 아닙니다.
이벤트 중복 처리
Q) Outbox에 저장했다면 이벤트는 한 번만 전달될까요?
A) 발행기는 같은 이벤트를 다시 전달할 수 있습니다.
예를 들어 다음과 같은 상황이 발생할 수 있습니다.
- 발행기가 메시지 브로커에 이벤트를 전달합니다.
- 브로커가 이벤트를 받습니다.
- 발행기가 전송 완료 상태를 기록하기 전에 종료됩니다.
- 발행기가 다시 실행되면서 같은 이벤트를 재전송합니다.
따라서 소비자는 동일한 이벤트가 다시 전달되어도 결과가 중복 반영되지 않도록 멱등성을 갖춰야 합니다.
운영 시 확인할 점
이벤트를 저장하는 것 외에도 다음 항목을 함께 확인해야 합니다.
- 발행에 실패한 이벤트를 재시도하는 방식
- 발행되지 않고 남아 있는 이벤트와 발행 지연
- 동일한 이벤트가 다시 전달되었을 때 소비자의 처리 방식
- 순서가 중요한 경우 발행 순서와 소비 순서를 유지하는 방식
Outbox에 이벤트가 저장되어 있어도 발행기가 동작하지 않으면 전달은 지연됩니다. DB 변경 직후 다른 시스템에도 같은 상태가 반영된다고 가정하면 안 됩니다.
Learning Point
- DB 변경과 이벤트 발행은 순서만 바꿔서는 함께 성공하도록 보장할 수 없다는 점
- 업무 데이터와 발행할 이벤트를 같은 DB 트랜잭션에 저장할 수 있다는 점
- 재전송에 대비한 멱등성과 발행 지연에 대한 모니터링이 필요하다는 점