Concert Booking / Engineering case
같은 좌석을 누른 두 사람에게 다음 행동까지 돌려주기
대기열 입장, 동일 요청 재시도, 좌석 상태 변경을 한 장치에 몰지 않고 각각의 경계가 책임지게 했습니다.
GitHub에서 코드 보기 ↗User impact
한 사용자가 좌석을 먼저 확정해도 다른 사용자는 모호한 실패 화면이 아니라 최신 좌석표로 돌아가 다시 선택할 수 있어야 합니다.
01
어떻게 깨지는가
- 두 요청이 같은 AVAILABLE 상태를 읽으면 예약이 두 건 생성될 수 있습니다.
- 입장 토큰 응답을 잃은 재시도가 이미 queue에서 제거된 사용자를 복구하지 못할 수 있습니다.
- 동일 버튼의 중복 클릭이 새로운 예약 의도로 처리될 수 있습니다.
02
지켜야 할 경계
- Queue Token은 일정과 사용자에 묶여야 합니다.
- 좌석의 최종 기준은 Redis가 아니라 DB transaction입니다.
- 같은 key의 다른 payload는 정상 재시도가 아니라 conflict입니다.
- Queue Token이 일정별 입장 권한을 확인합니다.
- Idempotency-Key가 같은 예약 의도의 재시도를 찾습니다.
- 좌석 lock과 DB constraint가 최종 한 건을 확정합니다.
03
선택한 설계
- Redis Lua에서 기존 token 확인·rank 검증·token 저장·queue 제거를 한 번에 처리합니다.
- DB lock과 unique constraint가 최종 좌석 변경을 한 건으로 수렴시킵니다.
- 클라이언트는 예약 의도 하나에 같은 Idempotency-Key를 유지합니다.
04
감수한 trade-off
- DB를 최종 기준으로 두어 write 경합 비용을 감수하지만 복구 기준이 명확합니다.
- Queue와 reservation의 책임이 나뉘어 상태가 늘지만 실패 원인을 구분할 수 있습니다.
05
어떻게 확인했는가
- 동일 좌석 10개 동시 요청이 성공 1건으로 수렴합니다.
- 동일 key replay는 기존 예약을 반환하고 Queue Token 응답 유실 뒤 재발급도 같은 token으로 복구됩니다.
- 브라우저 E2E에서 패자는 최신 좌석표로 돌아갑니다.
Replay the boundary
글 대신 단계별로 따라보기
Sources
코드와 테스트로 돌아가기
한계와 다음 확인
- 공개 검증은 로컬 Testcontainers와 브라우저 E2E 범위입니다.
- 세 가지 lock 전략의 production 처리량 비교를 주장하지 않습니다.