Transactional Outbox 생성 후 즉시 발송하는 로직은 Polling을 최적화한 방법이다.
·수정 2026.04.23·수정 1회
요약
- 즉시 전송(Immediate dispatch)은 독립적인 방식이 아니라 Polling의 최적화 레이어다
- 즉시 전송을 사용하더라도 실패 fallback을 위해 Polling 워커는 필수
본문
두 가지 처리 방식
| 방식 | 설명 |
|---|---|
| 즉시 전송 | Outbox에 추가 후 바로 외부 시스템 호출 |
| Polling | 워커가 일정 주기로 Outbox를 확인해 처리 |
선택 기준
| 기준 | 즉시 전송 | Polling |
|---|---|---|
| 지연 허용 | ❌ 즉시 필요 | ✅ 수 초~분 OK |
| 처리량 | 낮음 (수십 TPS) | 높음 (수백+ TPS) |
| API 응답 속도 | 느려도 됨 | 빨라야 함 |
| 외부 시스템 안정성 | 안정적 | 불안정/Rate limit |
왜 즉시 전송에도 Polling이 필요한가?
Outbox 저장과 외부 시스템 호출은 트랜잭션으로 묶이지 않는다:
BEGIN → Outbox INSERT → COMMIT (성공) → 외부 API 호출 (실패 가능)
실패 시나리오:
- Outbox 저장 성공 (DB 트랜잭션 커밋)
- 외부 시스템 호출 실패 (네트워크 오류, 타임아웃, Rate limit 등)
- → Outbox에 미처리 레코드로 남음
- → Polling 워커가 이를 재처리
하이브리드 패턴 (권장)
def process(event):
with transaction():
save(event)
outbox.insert(event, status='PENDING')
# 즉시 시도 (best-effort)
try:
send_to_external(event)
outbox.delete(event)
except:
pass # 실패해도 OK → Polling이 처리
# Polling 워커는 항상 돌면서 미처리 건 수습
실무 적용
- 처리량 높을 예정: 처음부터 Polling 전용으로 설계
- 즉시 전송 추가: 성능 최적화 단계에서 고려
- 핵심: 즉시 전송은 "있으면 좋은 것", Polling은 "필수"