안녕세계
[MSA] Two-Phase Commit (2PC) 본문
[MSA] Two-Phase Commit (2PC)
Junhong Kim 2025. 9. 28. 17:19Two-Phase Commit이란?
서비스를 개발하다 보면 하나의 사용자 액션이 여러 시스템에 영향을 주는 순간이 옵니다. 예를 들어 사용자가 결제 버튼을 누르면, 주문을 완료하기 위해 결제 승인, 주문 상태 변경, 재고 차감 같은 작업이 연쇄로 일어납니다. 초기에는 한 시스템에서 처리하던 것들도, 서비스가 커지면 역할이 분리되면서 서로 다른 시스템이나 DB에서 처리되기 시작합니다. 이때 요구사항은 단순합니다. 모든 단계가 성공했을 때만 주문을 최종적으로 ‘결제 완료’로 만들고, 중간에 하나라도 실패하면 전체가 완료되지 않게 해야 합니다. Two-Phase Commit(2PC)은 이런 요구를 분산 환경에서 다루기 위한 방식입니다.
커머스 서비스에서 자주 발생하는 문제
사용자가 쇼핑몰에서 주문을 하면 보통 다음 작업이 필요합니다.
- 결제 시스템에서 결제 승인
- 주문 시스템에서 주문 상태를 ‘결제 완료’로 변경
- 재고 시스템에서 재고 수량 차감
실제로는 결제, 주문, 재고가 각각 다른 시스템으로 구성된 경우가 많습니다. 그래서 일부는 성공하고 일부는 실패하는 상황이 쉽게 나옵니다. 예를 들어 결제 승인과 주문 상태 변경은 성공했는데, 재고가 부족해서 재고 차감이 실패하면 문제가 됩니다. 사용자는 결제 완료로 보이는데 실제로는 배송이 불가능한 주문이 되어버립니다. 이런 케이스가 반복되면 서비스 신뢰도는 빠르게 떨어집니다.
Two-Phase Commit이 처리하는 방식
Two-Phase Commit은 한 번에 커밋하지 않고, 먼저 “전체가 커밋 가능한 상태인지”를 확인한 뒤 최종 결정을 내립니다. 다만 Prepare 단계에서 OK를 받았다고 해서, 커밋이 항상 성공하는 것은 아닙니다. 네트워크 단절이나 프로세스 장애처럼 커밋 시점에 예외는 언제든 발생할 수 있습니다.

1단계: Prepare 단계
Prepare 단계에서는 아직 어떤 시스템도 커밋하지 않습니다. 주문 처리를 시작한 쪽에서 결제, 주문, 재고 시스템에 “커밋 가능 여부”를 묻습니다. 결제 시스템은 실제 승인 대신 승인 가능 여부만 확인하고 임시 홀드합니다. 주문 시스템은 상태 변경이 가능한지 점검만 합니다. 재고 시스템도 실제 차감은 하지 않고, 수량 확보가 가능한지 확인한 뒤 잠시 접근을 막아 둡니다. 이 단계에서 각 시스템은 가능/불가능만 응답합니다. 이 시점까지는 데이터가 확정되지 않습니다. Prepare는 “커밋 가능성 확인”이지 “커밋 성공 보장”이 아닙니다.
2단계: Commit 또는 Rollback 단계
Prepare에서 모두 OK가 나오면 커밋을 진행합니다. 이 시점에 결제는 실제 승인되고, 주문 상태는 결제 완료로 바뀌며, 재고는 최종 차감됩니다. 다만 커밋 단계에서도 실패는 발생할 수 있습니다. 커밋 직전에 프로세스가 내려가거나, 디스크 I/O 오류가 나거나, 네트워크가 끊겨 결과 전달이 안 되는 상황이 생길 수 있습니다. 그러면 일부 시스템은 커밋에 성공했고, 일부는 실패한 상태가 일시적으로라도 만들어질 수 있습니다.
그래서 2PC 구현은 “커밋하기로 결정했다”는 사실을 로그로 남기고, 재시도나 복구로 결국 같은 결론에 도달하도록 설계합니다. Commit 단계 이후에는 이미 커밋을 하기로 결정되었다는 사실이 중요해지고, 각 시스템은 그 결정을 따라가도록 되어 있습니다.
반대로 Prepare에서 하나라도 실패하면 커밋으로 넘어가지 않고 롤백 흐름으로 들어갑니다. 이 경우 결제는 홀드를 해제하고, 주문과 재고는 아무 변경 없이 종료됩니다.
Two-Phase Commit의 한계
Prepare 단계에서 OK를 받았다고 해서, 그 다음 단계가 항상 안전하다고 보기는 어렵습니다. Prepare는 “지금 시점에서는 가능해 보인다”는 확인일 뿐이고, 실제 커밋 시점에는 네트워크 장애나 프로세스 종료, 디스크 오류 같은 문제가 언제든 발생할 수 있습니다. 그래서 Two-Phase Commit은 실패를 없애는 방식이라기보다는, 실패가 발생하더라도 최종 상태를 하나로 맞추기 위한 규칙에 가깝습니다. 이 구조에서는 단순히 단계를 둘로 나누는 것만으로 끝나지 않습니다. 커밋 결정을 기록하는 로그가 필요하고, 중간에 끊기면 다시 시도해야 하며, 일정 시간 이후에는 복구 절차로 넘어가야 합니다. 정합성을 얻는 대신, 복구와 운영 복잡도를 함께 가져가게 됩니다.
또 다른 한계는 성능입니다. Prepare 단계 동안 참여한 시스템들은 보통 락을 잡고 대기합니다. 하나의 시스템이 느려지면 전체 트랜잭션이 같이 지연되고, 트래픽이 몰리는 구간에서는 처리량 저하가 눈에 띄게 나타납니다.
장애 전파 역시 피하기 어렵습니다. 트랜잭션을 진행하던 중 특정 컴포넌트에 문제가 생기면, 다른 시스템들이 락을 잡은 채 애매한 상태로 대기하게 될 수 있습니다. 이 대기 상태가 길어질수록 영향 범위는 점점 커집니다.
결국 Two-Phase Commit은 정합성을 강하게 보장하는 대신, 성능과 가용성, 운영 난이도 측면에서 높은 비용을 요구하는 방식입니다. 이런 이유로 트래픽이 많고 장애를 완전히 피할 수 없는 환경에서는, 한 번에 모두를 묶기보다는 각 단계를 빠르게 확정하고 실패를 보상으로 처리하는 Saga 같은 접근이 더 현실적인 선택으로 이어지게 됩니다.
정리
Two-Phase Commit(2PC)은 여러 시스템이 함께 처리되는 작업을 “모두 성공하거나 모두 실패”하도록 맞추기 위한 분산 트랜잭션 방식입니다. Prepare 단계에서 커밋 가능 여부를 확인하고, 모두 OK일 때만 Commit 단계에서 실제 변경을 확정합니다. 다만 커밋 시점의 장애 가능성, 락으로 인한 성능 저하, 복구와 운영 복잡도라는 비용이 따릅니다. 이런 이유로 트래픽과 장애를 고려해야 하는 환경에서는 2PC 대신 Saga 같은 보상 기반 접근이 더 현실적인 선택이 됩니다.
참고
'Server > Architecture' 카테고리의 다른 글
| [EDA] Transactional Outbox와 Inbox (1) | 2025.08.23 |
|---|---|
| [헥사고날 아키텍처] 포트와 어댑터(Ports and Adapters) (1) | 2024.11.23 |
| [아키텍처 테스트] ArchUnit (4) | 2024.10.31 |