본문으로 건너뛰기
뒤로

2PC와 Saga는 무엇을 약속하는가

초안아직 소유자가 검토하고 실습해 확인하지 않은 글입니다. 학습 기록이 아니라, 확인 전의 글입니다.

읽기 약 5분 · 실습 시간 별도

서로 다른 DB의 변경을 연결하는 방법을 비교할 때 2PC와 Saga를 같은 기능의 다른 구현처럼 놓으면 설명이 어긋난다. 두 방식이 다루는 실패와 보장의 범위가 다르기 때문이다. 가상 문서 처리에서 원하는 것은 예약·변환·저장·확정의 결과가 업무 규칙에 맞는 상태로 끝나는 것이다. 그 요구가 모든 참여자의 원자적인 commit을 뜻하는지, 중간 상태를 거쳐 나중에 정리해도 된다는 뜻인지부터 정해야 한다.

2PC는 two-phase commit, 즉 두 단계 commit을 뜻한다. 참여하는 트랜잭션 관리자가 변경을 commit할 준비가 됐는지 확인한 뒤 commit 또는 취소 결정을 전달하는 방식이다. 모든 참여 자원이 해당 프로토콜을 지원하고 정해진 방식으로 참여해야 한다. 임의의 HTTP API를 순서대로 호출하는 코드가 자동으로 2PC가 되지는 않는다.

commit 결정을 조정하는 것과 업무를 보상하는 것

2PC의 첫 단계에서는 참여자들이 준비 가능 여부를 알린다. 이후 조정자는 전체 결정을 전달한다. 준비한 참여자는 결정이 확정될 때까지 필요한 상태와 자원을 유지해야 한다. 장애 상황에서 처리할 복구 절차도 프로토콜의 일부다. PostgreSQL의 two-phase transaction 안내는 외부 트랜잭션 관리자의 역할과 준비 상태를 설명한다. 여기서는 구체적인 DB 제품의 가용성이나 성능을 측정하지 않았으므로 ‘무조건 느리다’ 또는 ‘항상 사용할 수 없다’는 결론을 내리지 않는다.

Saga는 각각의 로컬 트랜잭션이 순서대로 커밋되는 업무 흐름이다. 앞 단계가 이미 커밋된 뒤 다음 단계가 실패할 수 있다. 이때 앞선 효과를 업무상 상쇄하는 보상 작업을 수행하거나 실패한 단계를 재시도한다. 모든 단계가 하나의 DB 트랜잭션처럼 감춰졌다가 동시에 보이는 구조가 아니다. 그래서 다른 요청이 중간 상태를 읽을 수 있으며, 자동 격리와 자동 rollback이 따라오지 않는다.

비교 기준2PC를 검토할 때Saga를 검토할 때
조정 대상프로토콜에 참여하는 트랜잭션의 commit이미 커밋될 수 있는 로컬 업무 단계
실패 처리참여자 사이의 commit·취소 결정재시도 또는 명시적인 보상
참여 조건각 자원의 지원과 올바른 설정 필요각 단계와 보상 계약을 설계해야 함
중간 효과선택한 트랜잭션·격리 보장을 확인중간 상태 노출과 동시성 대책 필요

2PC 자체가 모든 종류의 외부 효과를 트랜잭션으로 만들어 주지는 않는다. 문서 변환 프로그램이 파일을 만들거나 외부 알림 서비스가 메시지를 보낸다면 그 자원이 실제로 참여 가능한지 확인해야 한다. 참여하지 않는 효과를 옆에 붙여 놓고 전체가 원자적이라고 부를 수 없다. 또한 원자적인 commit 조정과 구체적인 격리 수준은 같은 용어가 아니므로 각각 확인해야 한다.

예제에 Saga를 적용하면 무엇이 달라질까

예약 서비스는 RESERVED 상태를 커밋한다. 변환이 성공하면 결과 저장 서비스가 결과를 저장한다. 마지막에 예약을 CONFIRMED로 바꾼다. 변환이 실패하면 예약을 RELEASED로 바꾸는 별도 작업을 실행한다. 여기서 예약 해제는 이전 DB 스냅샷을 복원하는 명령이 아니라 현재 상태를 확인하고 업무 규칙에 따라 새 변경을 만드는 작업이다.

그 사이 다른 요청이 예약을 조회하거나 남은 허용량을 계산했을 수 있다. 단순히 과거 DB 전체를 되돌리면 다른 요청의 정상 변경도 잃을 수 있다. 보상은 해당 업무의 식별자를 기준으로 필요한 효과만 처리해야 한다. 이미 해제된 예약을 다시 해제해도 총량이 잘못 늘어나지 않는 성질도 필요하다.

Saga라는 이름을 붙이면 최종적으로 반드시 성공하거나 완전히 원상복구된다고 보장되는 것도 아니다. 해제 API가 계속 실패하거나 외부 시스템에 접근할 수 없는 경우가 남는다. 재시도 정책, 상태 조회, 수동 복구 대상 식별을 함께 설계해야 한다. 실제로 취소할 수 없는 효과가 있다면 보상 대신 어떤 업무 정책을 적용할지도 정해야 한다.

실패 하나로 두 방식의 전제를 비교한다

이번 학습에서는 2PC 엔진을 직접 구현하지 않는다. 먼저 선택한 자원이 분산 트랜잭션에 참여할 수 있는지 문서로 확인하고, 예제 각 단계를 참여 가능한 쓰기와 외부 효과로 분류한다. 지원 여부를 확인하지 않은 항목은 가능이라고 표시하지 않는다. 이후 Saga판에서는 예약 성공 후 변환 실패를 만들어 실제 상태를 확인한다.

  1. 예약 DB 쓰기, 변환 파일 생성, 결과 DB 쓰기를 각각 적는다.
  2. 각 자원이 제공하는 트랜잭션 범위와 외부 호출 여부를 표시한다.
  3. 예약을 먼저 커밋하고 변환에서 오류를 발생시킨다.
  4. 예약 해제를 실행한 뒤 이전 상태와 현재 상태를 비교한다.

예상되는 차이는 Saga에서 예약이 잠시 존재했다가 별도 해제 작업으로 변경된다는 점이다. 처음부터 예약이 없었다는 설명과 같지 않다. 이 차이가 사용자나 다른 요청에 문제가 된다면 중간 상태를 다루는 별도 설계가 필요하다. 실험 결과는 아직 없으며 이 순서는 후속 검증을 위한 계획이다.

‘MSA에서는 2PC를 쓰면 안 된다’와 ‘Saga면 분산 트랜잭션이 해결된다’는 두 문장 모두 이 검토를 생략한다. 적합한 방법은 참여 자원의 능력, 필요한 일관성, 허용 가능한 대기, 보상 가능성에 달려 있다. 다음 글부터는 Saga가 적절하다고 가정한 상태에서 누가 다음 단계를 정할지 비교한다. 그 조정 방식이 코레오그래피와 오케스트레이션이다.

이해 확인

예약 해제 API를 실행한 것은 DB rollback과 어떤 점에서 다를까? 그리고 외부 HTTP 서비스가 2PC에 참여하지 않는데 호출자 DB만 분산 트랜잭션으로 설정하면 무엇이 보장되지 않을까? 제품 이름보다 참여 범위와 중간 상태를 기준으로 답해 보자.

참고 자료



댓글