요약
- Transactional Outbox Polling 방식에서 retry는 두 단계로 나뉜다: 발행(Outbox → Queue)과 처리(Queue → Consumer)
- 각 계층이 자신의 실패만 책임지는 것이 핵심
본문
두 단계의 retry 책임
[Outbox] ---(1)발행---> [Message Queue] ---(2)처리---> [Consumer]
- 발행 실패 (Outbox → Queue): Outbox poller가 책임
- 처리 실패 (Queue → Consumer): RQ, Celery 등 큐 프레임워크가 책임
왜 분리해야 하는가?
RQ는 자신이 받지 못한 메시지를 retry할 수 없다.
- Outbox → RQ 단계에서 실패하면 메시지가 RQ에 도달하지 않음
- RQ 입장에서는 그 메시지의 존재 자체를 모름
- 따라서 Outbox에서 retry를 관리하지 않으면 메시지는 영원히 pending 상태로 남음
Outbox Poller의 역할
Outbox poller는 pending 메시지를 큐에 넣는 것까지만 책임진다.
for msg in get_pending_messages():
try:
queue.enqueue(task, msg.payload)
msg.mark_sent()
except:
msg.retry_count += 1
msg.next_retry_at = now() + exponential_backoff(msg.retry_count)
if msg.retry_count >= MAX_RETRY:
msg.status = 'failed'
alert("Outbox 발행 실패", msg)
발행 실패 시 처리 전략
- 기본: pending 상태로 남아있으므로 다음 polling 사이클에서 자동 재시도
- 보완: exponential backoff, max retry 후 failed 상태 분리, 알림