ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Two-Phase Commit이란? (2PC)
    SE Concepts 2021. 10. 7. 07:17
    반응형

    Two-phase 커밋은 여러 노드들 상에서의 원자적 트랜잭션 커밋을 이루기 위한 알고리즘(또는 프로토콜)입니다. 하나의 트랜잭션에 대해 모든 노드가 동일하게 커밋하거나 아니면 실패처리하는 것을 보장하기 위해 분산 데이터베이스에서 사용되는데요. 2PC는 몇몇의 데이터베이스 내부에 사용되거나 XA transaction(Java transaction API에서 지원), WS-AtomicTransaction for SOAP과 같은 애플리케이션 형태로도 제공되고 있습니다. 

     

    이 글에서는 2PC와 관련해 아래와 같은 사항들을 알아보겠습니다.

     

    • 2PC의 탄생배경
    • 2PC란?
    • 2PC의 취약점

    2PC의 탄생배경

    데이터베이스는 Multi-object 트랜잭션에서 일부만 데이터베이스에 반영이 되고, 일부는 실패하는 상황을 막기 위해 원자적(atomic) 트랜잭션을 제공합니다.

     

    싱글 노드에서 실행되는 데이터베이스에서 이러한 원자성(atomicity)은 보통 스토리지 엔진에 의해 구현되는데요. 클라이언트가 데이터베이스 노드에 커밋을 요청하면, 데이터베이스는 트랜잭션이 (보통 write-ahead log를 통해) durable하게 write될 수 있도록 합니다. 이러한 과정 중 실패한다면, 노드가 재시작할 때 로그로부터 트랜잭션을 회복하고 커밋 레코드가 실패 전에 디스크에 잘 write되었다면 커밋 성공으로, 아니면 커밋 실패로 간주해 롤백을 진행합니다. 

     

    그렇기에 싱글 노드 상에서, 트랜잭션 커밋은 데이터가 디스크에 쓰여진 순서에 의존하게 됩니다. 트랜잭션의 성공여부는 커밋 레코드가 디스크에 write되는 순간에 결정되게 됩니다. 

     

    그렇다면 단일 노드가 아닌, 분산 데이터 저장소에서는 이러한 원자적 트랜잭션이 어떻게 지원될까요? 대부분의 NoSQL은 그러한 분산 트랜잭션을 지원하지 않지만, 다양한 클러스터 시스템은 지원하고 있습니다. 이러한 클러스터 시스템에서 싱글 노드와 같은 방식만으로 분산 트랜잭션을 지원한다면 아래와 같은 문제가 발생할 수 있습니다:

     

    • 일부 노드에서는 제약위반 발생을 감지하여 커밋 실패가 발생하나, 일부 노드에서는 성공적으로 커밋됨
    • 커밋 요청이 네트워크 상에서 손실되고, 결국 타임아웃으로 커밋 실패가 발생하나, 일부 노드에는 성공적으로 전달되어 성공적으로 커밋됨
    • 일부 노드에는 커밋 레코드가 완전히 write되기 전에 다운되나, 일부 노드에는 성공적으로 커밋됨

     

     

    이렇게 일부만 트랜잭션을 커밋하고 일부는 하지 않는다면, 노드들은 서로 일관되지 않게 됩니다. 트랜잭션은 일단 커밋이 발생한 이후 추가적으로 해당 커밋과 관련해 변경을 가할 수 없습니다. 즉, 하나의 노드는 트랜잭션의 모든 노드들이 커밋하는게 확실한 순간에 한 번만 커밋해야 합니다. 그렇지 않다면, 이후에 발생하는 다른 트랜잭션들이 'read committed' isolation level이기에, 순간적으로 잘 못 write된 데이터를 읽는 일이 발생할 수 있습니다. 

     

    2PC란?

    2PC는 기본적으로 아래와 같이 2가지 phase로 나뉩니다:

    Image from Author inspired by [1]

     

    2PC는 일반적인 싱글 노드 트랜잭션에 존재하지 않는 새로운 컴포넌트인 코디네이터(또는 트랜잭션 매니저)를 사용합니다. 코디네이터는 트랜잭션을 요청하는 같은 애플리케이션 프로세스 내 라이브러리에 구현되어 있으나, 분리된 서비스나 프로세스일수도 있습니다. 

     

    2PC 트랜잭션은 애플리케이션이 여러 데이터베이스 노드들에 읽고 쓰면서 시작하게 됩니다. 애플리케이션이 커밋할 준비가 되면, 코디네이터는 phase 1을 시작하며 각 노드에 prepare 요청을 보내어 커밋할 수 있는지 질의합니다. 코디네이터는 이후 각 노드(참여자, participant라고 부름)의 응답을 추적합니다:

     

    • 모든 참여자가 yes라고 응답하면, 코디네이터는 phase 2로 넘어가 commit 요청을 보내어 커밋이 수행되도록 함
    • 어느 하나라도 no를 응답하면, 코디네이터는 phase 2로 넘어가 모든 노드들에 abort 요청을 보냄

     

    좀 더 상세하게, 어떻게 2PC가 동작하는지 살펴보면,

     

    1. 애플리케이션이 분산 트랜잭션은 시작하길 원하면, 코디네이터에게 트랜잭션 ID를 요청합니다. 이 트랜잭션 ID는 글로벌하게 유니크합니다.
    2. 애플리케이션이 각 참여자와 싱글-노드 트랜잭션을 시작하고 위의 글로벌하게 유니크한 ID를 전달합니다. 모든 읽기와 쓰기는 이러한 싱글-노드 트랜잭션 중 하나에서 처리되게 됩니다. 이 단계에서 일부 작업이 잘못되면 코디네이터 또는 참여자 중 누구도 abort할 수 있습니다.
    3. 애플리케이션이 커밋할 준비가 되면, 코디네이터는 모든 참여자들에게 글로벌 트랜잭션 ID가 포함된 prepare 요청을 보냅니다. 이러한 요청 중 어느 하나라도 실패하거나 타임아웃되면, 코디네이터는 모든 참여자들에게 해당 트랜잭션 ID의 트랜잭션 abort 요청을 보냅니다.
    4. 참여자가 prepare 요청을 받으면, 참여자는 어떠한 상황에서는 트랜잭션을 커밋할 수 있도록 합니다. 이것은 모든 트랜잭션 데이터를 디스크에 쓰고, 제약위반 등을 검증하는 것을 포함합니다. 코디네이터에게 'yes'를 응답하면서, 노드는 트랜잭션이 커밋된다면 어떠한 에러도 없이 커밋될 수 있다는 것을 약속합니다. 
    5. 코디네이터가 모든 prepare 요청들에 대한 응답들을 받으면, 트랜잭션을 커밋할지 abort할지 결정합니다. 코디네이터는 그러한 결정을 디스크에 존재하는 코디네이터 트랜잭션 로그에 기록하여 혹시나 발생하는 crash에 대비합니다 (commit point). 
    6. 코디네이터의 결정이 디스크에 write되고 커밋 또는 abort 요청이 모든 참여자들에게 전달됩니다. 요청이 실패하거나 타임아웃되면 코디네이터는 성공할 때까지 retry해야 합니다. 이후의 취소, 반려는 없으며 결정이 커밋하는 것이었다면 얼마의 retry가 필요하든지와 상관없이 그 결정이 강제되어야 합니다. 참여자가 그 사이에 다운되면, 참여자가 회복했을 때 그 트랜잭션은 커밋됩니다. 

     

    그러므로 2PC 프로토콜은 2가지 중요한 "번복이 없는 포인트"를 가집니다. 하나는 참여자가 yes를 응답했을 때 이후에 반드시 커밋해야만하는 부분이고, 다른 하나는 코디네이터가 결정을 하면 반드시 그 결정을 강제하여야 합니다. 이러한 약속이 2PC의 원자성을 보장합니다. 

     

     

    2PC의 취약점

    앞서 참여자의 다운, 네트워크 실패는 코디네이터가 결정 이후 결정의 강제를 통해 극복한다고 전달드렸습니다. 그러나 코디네이터가 다운되는 경우에는 어떤일이 발생할까요? prepare 요청을 보내기 전에는 참여자는 트랜잭션을 안전하게 abort할 수 있습니다. 그러나 참여자가 prepare 요청을 받고 yes를 응답한 이후에 코디네이터가 다운된다면 참여자는 더 이상 혼자서 abort를 수행할 수 없습니다. 

    Image from Author inspired by [1]

    코디네이터로부터 추가적인 도움 없이는 참여자는 커밋되었는지 abort되었는지 알 수 있는 방법이 없습니다. 원칙 상으로는, 참여자들끼리 서로 커뮤니케이션을 통해 어떻게 각 참여자가 prepare에 응답하였는지 찾고 합의에 도달해야하나 이 부분은 2PC 프로토콜에 포함되지 않습니다. 

     

    2PC가 완결되는 방법은 오직 코디네이터가 회복되기를 기다리는 것뿐입니다. 코디네이터는 커밋이나 abort 결정을 디스크에 존재하는 트랜잭션 로그에 기록해두었는데, 코디네이터가 회복할 때 이러한 상황에서 트랜잭션 로그를 읽어들여서 상태를 회복합니다. 

     

     

    Reference

    [1] Designing Data-Intensive Applications

    [2] https://en.wikipedia.org/wiki/Two-phase_commit_protocol

    반응형

    댓글 0

Kaden Sungbin Cho.