결제가 완료되면 주문 상태, 재고, 포인트, 알림을 전부 바꿔야 합니다. 이걸 다 한 트랜잭션으로 묶을지, 돈과 상태만 묶고 나머지는 이벤트로 몇 초 뒤에 맞출지 정해야 하는 상황입니다.
언제: 돈, 재고, 주문 상태처럼 틀리면 바로 사고가 나는 데이터일 때. 그리고 쓰기 트래픽이 DB 한 대로 충분히 감당될 때. 대부분의 서비스는 여기서 끝납니다.
비용: 쓰기 처리량이 DB 한 대의 한계에 묶입니다. 트랜잭션 안에 일을 많이 넣을수록 락을 오래 잡아서, 트래픽이 늘면 대기 시간이 같이 늘어납니다.
언제: 알림, 집계, 검색 인덱스처럼 몇 초 늦어도 사용자가 못 느끼는 데이터일 때. 또는 여러 서비스가 같은 데이터에 동기로 묶여서 서로 발목을 잡기 시작할 때.
비용: 중복·순서 꼬임·유실을 직접 처리해야 합니다(멱등 키, 재시도, DLQ). 장애 추적이 어려워지고, "잠깐 안 맞는 화면"을 CS팀과 비즈니스가 감수해야 합니다.
기본은 단일 DB에 한 트랜잭션입니다. 돈과 상태 전이는 끝까지 트랜잭션 안에 둡니다. 쓰기 p99가 락 대기 때문에 실제로 무너지거나, 트랜잭션 안에서 다른 서비스를 동기 호출하고 있는 걸 발견하는 순간 — 그때 부가 데이터부터 outbox 이벤트로 빼서 최종 일관성으로 갑니다.
- 쓰기 p99가 500ms를 넘고, 원인을 따라가 보니 락 대기(lock wait)일 때
- 트랜잭션 안에서 외부 서비스 API를 동기 호출하고 있을 때 — 남의 장애가 내 DB 락을 잡고 있는 구조
- 기능 하나 배포하는데 두 팀이 같이 묶여서 매번 릴리즈 조율 회의가 필요할 때
- 읽기:쓰기가 10:1을 넘는데 읽기 부하 때문에 주 DB CPU가 70%를 넘나들 때
🔧Postgres transactions (single primary)Transactional outboxKafka/SQS + idempotent consumerRead replicaDLQ + replay
“I start with one transaction in one database, and only go eventual when lock waits or team coupling actually bite.”
checking microphone…