Zettelkasten

Outbox Polling에서 retry 책임은 발행과 처리로 분리된다

·수정 2026.04.23·수정 2

요약

  • Transactional Outbox Polling 방식에서 retry는 두 단계로 나뉜다: 발행(Outbox → Queue)과 처리(Queue → Consumer)
  • 각 계층이 자신의 실패만 책임지는 것이 핵심

본문

두 단계의 retry 책임

[Outbox] ---(1)발행---> [Message Queue] ---(2)처리---> [Consumer]
  1. 발행 실패 (Outbox → Queue): Outbox poller가 책임
  2. 처리 실패 (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 상태 분리, 알림

참고