데이터베이스 용어 사전
분산2단계 커밋 · Two-Phase Commit · 보상 트랜잭션

2PC·Saga

여러 DB에 걸친 트랜잭션을 다루는 두 방식. 2PC는 원자성을 지키되 블로킹하고, Saga는 보상으로 되돌린다.

여러 노드·서비스에 걸친 변경을 어떻게 일관되게 만들 것인가에 대한 두 답.

2PC — 조정자가 전부의 동의를 받는다

[1단계 준비(prepare)]

  • 조정자 → 각 참여자: "커밋할 수 있나?"
  • 참여자 → 준비 완료하고 락을 쥔 채 "예" 라고 답한다

[2단계 커밋]

  • 전원이 "예" → 조정자가 커밋 명령
  • 하나라도 "아니오" → 전원 롤백

원자성이 지켜진다. 그런데 결정적 약점이 있다.

조정자가 1단계와 2단계 사이에 죽으면?

  • 참여자들은 락을 쥔 채 무한정 기다린다 (블로킹)
  • 그동안 그 행들에 접근하는 모든 트랜잭션이 멈춘다

그래서 MSA에서는 2PC를 피하는 것이 기본이다. 한 서비스의 장애가 다른 서비스를 얼려 버리기 때문이다.

-- PostgreSQL 도 2PC 를 지원하기는 한다
BEGIN;
  UPDATE account SET balance = balance - 100 WHERE id = 'A';
PREPARE TRANSACTION 'tx1';       -- 1단계. 이 상태로 남으면 락이 유지된다
COMMIT PREPARED 'tx1';           -- 2단계

-- 미결 트랜잭션 감시 (방치하면 VACUUM 까지 막는다)
SELECT * FROM pg_prepared_xacts;

max_prepared_transactions가 기본 0이라 PostgreSQL은 2PC가 기본 비활성이다 — 그만큼 권장되지 않는다는 신호다.

Saga — 되돌리기로 대신한다

주문 생성 → 결제 승인 → 재고 차감 → 배송 요청
                              ↓ 실패
         결제 취소  ←  주문 취소        (보상 트랜잭션을 역순으로)

각 단계가 독립된 로컬 트랜잭션이고, 실패하면 앞 단계를 의미적으로 되돌리는 보상 작업을 실행한다.

  • 2PC — 원자성 O · 락 유지 · 블로킹 위험
  • Saga — 원자성 X · 락 없음 · 중간 상태가 외부에 보인다 (최종 일관성)

Saga의 대가는 중간 상태가 노출된다는 것이다. "결제는 됐는데 재고가 없어 곧 취소됨" 같은 상태를 사용자가 볼 수 있고, 그걸 UX로 설계해야 한다.

보상은 취소가 아니다

계좌에서 100원을 뺐다 ❌ 롤백 — 그런 건 없다. 이미 커밋됐다 ✅ 보상 — 100원을 다시 넣는 새 트랜잭션을 실행한다

메일을 보냈다

  • 보상이 불가능하다. 이런 작업은 Saga 의 맨 뒤에 둔다

되돌릴 수 없는 작업은 순서상 마지막에 배치하는 것이 Saga 설계의 기본 원칙이다.

면접 함정

  • "Saga는 ACID를 보장한다" → 원자성을 포기하고 최종 일관성을 택한 것이다.
  • "2PC는 안전하니 쓰면 된다" → 조정자 장애 시 블로킹이 서비스 전체로 번진다.

두 가지 조율 방식

오케스트레이션(중앙 조율)

  • 조율자가 각 단계를 순서대로 호출하고 실패 시 보상을 부른다
  • ✅ 흐름이 한곳에 보인다 · 디버깅이 쉽다
  • ❌ 조율자가 모든 서비스를 알아야 한다

코레오그래피(이벤트 연쇄)

  • 각 서비스가 이벤트를 듣고 다음 이벤트를 낸다
  • ✅ 서비스 간 결합이 낮다
  • ❌ 전체 흐름이 어디에도 안 적혀 있다 · 추적이 어렵다

단계가 4개를 넘으면 오케스트레이션이 유지보수에 유리하다는 것이 일반적인 경험칙이다.

보상이 실패하면

  • 보상 트랜잭션도 실패할 수 있다

  • ① 보상은 반드시 멱등하게 만든다 (여러 번 실행돼도 결과가 같게)

  • ② 재시도한다

  • ③ 그래도 실패하면 사람이 개입할 큐(dead letter)로 보낸다

  • "자동으로 완벽하게 되돌린다" 는 환상이다 — 개입 경로를 반드시 설계한다

함께 보면 좋은 용어

노트에서 맥락과 함께 보기 — 분산 DB — 복제·샤딩·CAP·일관성·2PC/Saga