문제 해결 포트폴리오 / 콘서트 예매 / 이벤트 정합성
시나리오 검증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
문제 구간 아키텍처
핵심 설계 판단
- 도메인 변경과 Outbox Insert를 같은 DB transaction에 기록합니다.
- Outbox는 exactly-once 보장이 아니라, 발행 의도를 복구 가능하게 남기는 장치입니다.
- 중복 소비는 consumer idempotency로 흡수하고, 자동 복구 실패는 DEAD/manual replay로 분리합니다.
문제
- DB commit 이후 Kafka publish가 실패하면 예약 상태와 consumer 처리 상태가 어긋날 수 있었습니다.
- consumer 실패를 단순 재시도로만 처리하면 중복 처리와 무한 재시도 위험이 있었습니다.
- 결제 승인, 취소, 만료 race가 섞일 때 어떤 이벤트를 다시 처리해야 하는지 추적 가능해야 했습니다.
해결
- 도메인 변경과 발행할 이벤트 의도를 같은 transaction에서 Outbox에 저장했습니다.
- Outbox relay, DEAD 상태, DLT, manual replay 경로를 분리해 실패 이벤트를 다시 설명할 수 있게 했습니다.
- consumer idempotency와 Redis stock reconciliation으로 중복 소비와 조회 상태 불일치를 흡수했습니다.
결과
- 예약/결제 idempotency, race condition, DLT replay, stock reconciliation을 Testcontainers로 검증했습니다.
- D/E/F local repeat에서 결제/만료 race, idempotency replay/conflict, 대기열 token abuse checks를 통과했습니다.
- 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
구현 포인트
- Outbox Table은 이벤트 발행 성공 여부와 재처리 상태를 추적하는 복구 기준으로 둡니다.
- DLT와 DEAD 상태는 자동 재시도로 해결되지 않는 이벤트를 운영자가 확인 가능한 대상으로 분리합니다.
- Redis stock은 빠른 조회를 위한 보조 상태이며, reconciliation은 PostgreSQL 기준으로 수행합니다.
Outbox 상태 전이
| From | To | 설명 |
|---|---|---|
| PENDING | PUBLISHED | Outbox relay가 Kafka 발행에 성공한 상태 |
| PUBLISHED | CONSUMED | consumer idempotency를 통과해 처리 완료된 상태 |
| PENDING | RETRYING | 일시적 발행 실패 후 재시도 대상으로 남긴 상태 |
| RETRYING | DEAD | 자동 재시도로 복구하지 못해 격리한 상태 |
| DEAD | MANUAL_REPLAY | 운영자 확인 후 수동 재처리 대상으로 올린 상태 |
| MANUAL_REPLAY | PUBLISHED | 수동 재처리 이벤트가 다시 발행된 상태 |
세부 테스트, guard, raw artifact는 GitHub README와 docs에 정리했습니다.
GitHub 근거 보기