2PC or not 2PC

Tags:

http://www.addsimplicity.com/adding_simplicity_an_engi/2006/12/2pc_or_not_2pc_.html
http://www.addsimplicity.com/adding_simplicity_an_engi/2006/12/avoiding_two_ph.html

2PC를 버리고 TX에 대한 message queue를 사용한 asynchronous recovery에 의존해야하는 이유

  • 2PC는 총 availability (TX에 참여하는 각 component의 availability의 곱)을 저하시킨다.
  • 2PC coordinator는 SPOF (Single Point of Failure)가 된다.
  • 2PC없이 두번 커밋하더라도 전체 TX의 실패가능성은 매우 낮다. 만약 실패한다고 하더라도 실패내용을 journal에 저장하고 이를 asynchronous idempotent operation으로 복구할 수 있다.

Axioms

  • 데이터 모델과 로직은 Forward에 기반한 단방향을 따르도록 한다.
  • TX에서 데이터가 변경되는 순서는 중요하다. 특히 이를 2PC없이 다룰경우 Parent – Child 테이블에서 Child를 먼저 반영하도록 한다. Child 만 반영되고 실패한다면 안전하지만, Child 없이 부모만 생성되는 경우 일관성이 깨진다.
  • 2개의 작업이 있을때 하나라도 수행하는 것이 모두다 실패해버리는 것보다 낫다.
  • Idempotent operation들을 최대한 활용한다.

활용 예로서, 카트에 물건을 1개 넣고, 구매를 수행할 때를 생각해보자. 사용자가 카트에 물건을 하나 넣으면, 구매가 수행되었지 않더라도 해당 아이템의 수가 웹페이지에서 하나 감소되게 보이게 한다고 하자. 구매가 완료되면 정말로 해당 아이템의 수가 웹 페이지에서 완전히 1 감소되어야하고, 구매가 안되면 감소되었던 값이 다시 원래되로 돌아와야한다. 그리고 판매되는 아이템의 정보가 담긴 아이템 DB와 카트 정보가 담긴 카트 DB가 별개라고 가정하자.

  • 2PC: 카트에 물건을 넣고 웹에서 물건을 하나 감소시키는 것을 하나로 묶은 TX를 수행.
  • non 2PC: reservation 테이블을 아이템 DB에 생성하고 그곳에 물건이 하나 빠졌다고 기록해둔다. reservation 테이블에는 expiration 컬럼이 있어서 해당 시간이 지난뒤에 reservation은 무효화되는 것으로 한다. 먼저, reservation을 기록한다. 그리고 TX완료. (이 단계에서 실패한다면 expiration 컬럼을 사용하여 잘못된 데이트를 수정할 수 있다. 다음으로, 카트에 물건을 넣는 TX를 수행한다. (이 단계에서 실패한다면 reservation 테이블 내용을 가져올 수 있다.) 다음, 실제로 판매가 완료되었으므로 아이템의 개수를 아이템 DB에서 1 감소시킨다. (이 단계에서 실패한다면 카드에 물건을 넣은 TX의 내용을 바탕으로 message queue를 통해 아이템 DB쪽으로 복구 메시지를 전달한다.)