SJH

문제 해결 포트폴리오 / 콘서트 예매 / 이벤트 정합성

시나리오 검증Concert Booking · Deep Dive 2/2

Outbox/DLT 이벤트 복구 설계

DB commit 이후 Kafka 발행 실패를 Outbox·DLT·수동 재처리로 복구 가능한 상태로 설계했습니다.

해결 키워드OutboxDLTManual ReplayConsumer Idempotency
프로젝트
Concert Booking
참여
개인 / BE 1
기술
Java · Spring Boot · PostgreSQL · Redis · Kafka

문제 구간 아키텍처

DB commit 이후 Kafka 발행 실패를 Outbox relay, DLT, DEAD, manual replay로 분리한 복구 아키텍처
DB commit과 Kafka publish 사이의 실패 구간을 Outbox 상태, DLT, manual replay로 분리한 구조입니다.

핵심 설계 판단

  • 도메인 변경과 Outbox Insert를 같은 DB transaction에 기록합니다.
  • Outbox는 exactly-once 보장이 아니라, 발행 의도를 복구 가능하게 남기는 장치입니다.
  • 중복 소비는 consumer idempotency로 흡수하고, 자동 복구 실패는 DEAD/manual replay로 분리합니다.

문제

  1. DB commit 이후 Kafka publish가 실패하면 예약 상태와 consumer 처리 상태가 어긋날 수 있었습니다.
  2. consumer 실패를 단순 재시도로만 처리하면 중복 처리와 무한 재시도 위험이 있었습니다.
  3. 결제 승인, 취소, 만료 race가 섞일 때 어떤 이벤트를 다시 처리해야 하는지 추적 가능해야 했습니다.

해결

  1. 도메인 변경과 발행할 이벤트 의도를 같은 transaction에서 Outbox에 저장했습니다.
  2. Outbox relay, DEAD 상태, DLT, manual replay 경로를 분리해 실패 이벤트를 다시 설명할 수 있게 했습니다.
  3. consumer idempotency와 Redis stock reconciliation으로 중복 소비와 조회 상태 불일치를 흡수했습니다.

결과

  1. 예약/결제 idempotency, race condition, DLT replay, stock reconciliation을 Testcontainers로 검증했습니다.
  2. D/E/F local repeat에서 결제/만료 race, idempotency replay/conflict, 대기열 token abuse checks를 통과했습니다.
  3. Kafka publish 실패와 consumer 실패를 정상 처리 흐름 밖으로 격리할 수 있게 구조화했습니다.

검증 근거

Testcontainers 검증 시나리오

시나리오 검증

reservation/payment idempotency, race condition, DLT replay, stock reconciliation 검증

결제/만료 race·중복 요청·대기열 abuse 검증

시나리오 검증

D/E/F local repeat: 결제/만료 race, idempotency replay/conflict, 대기열 token abuse checks passed

구현 포인트

  1. Outbox Table은 이벤트 발행 성공 여부와 재처리 상태를 추적하는 복구 기준으로 둡니다.
  2. DLT와 DEAD 상태는 자동 재시도로 해결되지 않는 이벤트를 운영자가 확인 가능한 대상으로 분리합니다.
  3. Redis stock은 빠른 조회를 위한 보조 상태이며, reconciliation은 PostgreSQL 기준으로 수행합니다.

Outbox 상태 전이

FromTo설명
PENDINGPUBLISHEDOutbox relay가 Kafka 발행에 성공한 상태
PUBLISHEDCONSUMEDconsumer idempotency를 통과해 처리 완료된 상태
PENDINGRETRYING일시적 발행 실패 후 재시도 대상으로 남긴 상태
RETRYINGDEAD자동 재시도로 복구하지 못해 격리한 상태
DEADMANUAL_REPLAY운영자 확인 후 수동 재처리 대상으로 올린 상태
MANUAL_REPLAYPUBLISHED수동 재처리 이벤트가 다시 발행된 상태

세부 테스트, guard, raw artifact는 GitHub README와 docs에 정리했습니다.

GitHub 근거 보기