마이크로서비스 - 트랜잭션
Transaction은 다수 연산의 전체 성공 또는 전체 실패를 보장하는 방법입니다. 마이크로서비스에서의 트랜잭션 처리에 대해 알아봅시다.
ACID 중 타협이 필요한 트랜잭션
원자성(Atomicity)은 트랜잭션과 관련된 작업들이 부분적으로 실행되다가 중단되지 않는 것을 보장하는 능력이다. 예를 들어, 자금 이체는 성공할 수도 실패할 수도 있지만 보내는 쪽에서 돈을 빼 오는 작업만 성공하고 받는 쪽에 돈을 넣는 작업을 실패해서는 안된다. 원자성은 이와 같이 중간 단계까지 실행되고 실패하는 일이 없도록 하는 것이다.
일관성(Consistency)은 트랜잭션이 실행을 성공적으로 완료하면 언제나 일관성 있는 데이터베이스 상태로 유지하는 것을 의미한다. 무결성 제약이 모든 계좌는 잔고가 있어야 한다면 이를 위반하는 트랜잭션은 중단된다.
고립성(Isolation)은 트랜잭션을 수행 시 다른 트랜잭션의 연산 작업이 끼어들지 못하도록 보장하는 것을 의미한다. 이것은 트랜잭션 밖에 있는 어떤 연산도 중간 단계의 데이터를 볼 수 없음을 의미한다. 은행 관리자는 이체 작업을 하는 도중에 쿼리를 실행하더라도 특정 계좌간 이체하는 양 쪽을 볼 수 없다. 공식적으로 고립성은 트랜잭션 실행내역은 연속적이어야 함을 의미한다. 성능관련 이유로 인해 이 특성은 가장 유연성 있는 제약 조건이다. 자세한 내용은 관련 문서를 참조해야 한다.
지속성(Durability)은 성공적으로 수행된 트랜잭션은 영원히 반영되어야 함을 의미한다. 시스템 문제, DB 일관성 체크 등을 하더라도 유지되어야 함을 의미한다. 전형적으로 모든 트랜잭션은 로그로 남고 시스템 장애 발생 전 상태로 되돌릴 수 있다. 트랜잭션은 로그에 모든 것이 저장된 후에만 commit 상태로 간주될 수 있다.
https://ko.wikipedia.org/wiki/ACID
마이크로서비스와 같은 분산환경에서 트랜잭션은 굉장히 비싼 연산(시간, 네트워크…)입니다. 업데이트를 막는 Lock을 걸고 여러 데이터들과 Sync를 맞춰야하기 때문입니다. 하지만 단일DB에서 @Transaction 또는 3~4줄 정도의 추가 코드를 통해 쉽게 트랜잭션을 처리했습니다. 이 쉬움은 과도하게 많은 트랜잭션남발을 불러왔습니다. 마이크로서비스에서 우리에게는 다음 선택지가 있습니다.
- 단일 DB 수준의 트랜잭션
- ACID 중 일부를 타협한 트랜잭션
분산환경에서 단일 DB 수준의 트랜잭션을 처리하려면 2PC라는 두 단계 커밋이 필요합니다. 하지만 짧은 시간 동안의 ACID중 일부 요소를 타협한다면 마이크로서비스와 같은 분산환경에서도 고성능 트랜잭션 처리가 가능합니다.
Saga Pattern
여기서 소개하는 패턴은 우리가 단일DB에서 쓰던 트랜잭션이 아닙니다.
START TRANSACTION
INSERT INTO TABLE A
INSERT INTO TABLE B
COMMIT
END TRANSACTION 이와 같이 테이블 A와 테이블 B에 인서트할 경우 짧은 시간동안 테이블 A에는 인서트가됐으나 테이블 B에는 인서트가 안된 고립성이 깨지는 상황이 발생합니다. 물론 A 인서트가 성공하고 B가 실패하면 A는 롤백되는 원자성을 보장하지만 역시 고립성이 깨집니다. 하지만 짧은 시간동안 A에는 인서트된 데이터가 존재합니다. 따라서 반드시 이전 파트 부분을 이해하고 나의 서비스 시나리오가 정말 단일 DB에서 쓰던 트랜잭션이 필요한지 판단해야합니다.
Saga Pattern은 다음 두 가지 방식이 있습니다.
- Events/Choreography
- Command/Orchestration
마이크로서비스에서는 1번 방식이 선호되나 이벤트 드리븐으로 전환이 완료되야 쓸 수 있는 단점이 있습니다. 이벤트 방식은 트랜잭션을 주도하는 서비스 없이 각자 서비스들이 이벤트에 따라서 자신의 역할만 수행하는 방식이고 컴맨드 방식은 트랜잭션을 주도하는 마스터같은 서비스가 존재합니다. 이벤트 방식은 락을 걸기 어렵지만 컴맨드 방식은 트랜잭션을 주관하는 마스터에 의해 락을 관리하여 2PC를 이용 단일DB수준의 트랜잭션을 분산환경에서 구현할 수 있습니다. 이 포스트에서는 이벤트 방식만 설명합니다.

다이아그램의 1번과 2번 과정을 풀어서 쓰면 아래와 같습니다.
- Post /Orders
- Store Order DB
- Fire Order_Created
- Listen Order_Created
- Store Payment DB
- Fire Payment_Created(BILLED_ORDER_EVENT)
다이아그램 처럼 이벤트에 의미(BILLED_ORDER_EVENT)를 부여하는것 보다는 CRUD+Event로 네이밍(Payment_Created)하는것이 좋습니다. 비지니스가 복잡할수록 이벤트 네이밍이 어렵기 때문입니다. 이벤트 방식에서는 이와 같이 각각의 서비스에서 이벤트를 발생하고 이 이벤트를 다른 서비스에서 처리하고 다시 이벤트를 발생합니다. 따라서 트랜잭션을 처리하는 마스터와 같은 존재가 필요 없습니다. 이벤트 방식은 Persistence를 처리하고 이벤트를 만드는 방식과 이벤트를 만들고 Persistence 처리하는 방식이 있으나 대부분의 이벤트 드리븐 방식이 비동기이므로 둘의 차이는 없습니다.
Event - Rollback
이벤트 방식에서 롤백은 이벤트가 역으로 전파된다고 생각하면 이해하기 쉽습니다.

위 다이아그램에서는 Stock 서비스에서 재고가 없을 경우 Order와 Parment에 이벤트를 보내지만 실제 시스템 설계에서는 다음과 같이 하는것이 좋습니다. 이벤트가 한향으로 흐르지 않고 여러방향으로 전파되면 시스템이 불필요하게 복잡해집니다. 이벤트가 계속 루프로 도는 치명적인 버그를 만들수도 있기 때문에 아래와 같은 처리가 좋습니다.

- 페이먼트 롤백 이벤트 발생
- 페이먼트에서 롤백 처리
- 주문 롤백 이벤트 발생
- 주문에서 롤백 처리
Log
분산환경에서는 여러가지 원인 때문에 트랜잭션이 실패하고 Failover등의 상황으로 동일 이벤트가 2번 이상 처리될 수 있으므로 로그가 반드시 필요합니다. 이 로그에는 다음 3종류의 내용이 있어야합니다.
- Event Catch
- Event Fail
- Event Fire&Complete
| TransactionId | Timestamp | ServiceId | Event | Reasons |
|---|---|---|---|---|
| a38d10 | 1538362942842 | User | CreateOrder | |
| a38d10 | 1538362943631 | Order | CreatePayment | |
| a38d10 | 1538362944879 | Payment | FailCreatePayment | Out of Credit |
| a38d10 | 1538362945143 | Order | RollbackCreateOrder |
트랜잭션 아이디는 요청을 하는 UI나 다른 서비스가 만드는 UUID입니다. 이를 통해 동일한 중복 요청을 선별(사용자가 주문 버튼을 두번 누르거나 At least once와 같은 이벤트 전파 방식에서 두번 이상의 메세지 도달 등…)합니다. 위 로그 예제를 보면 첫번째 로그의 서비스 아이디는 User입니다. 이것은 이 트랜잭션이 사용자 요청(앱)에 의해서 발생한 트랜잭션이라는것을 알 수 있습니다. 서비스의 초반에는 주로 사용자에 의한 트랜잭션만 생기지만 데이터와 AI기술이 축적되면 사용자가 아닌 머신에의한 트랜잭션이 더 많아집니다. 주기적으로 기저귀나 우유를 보내주는 서비스의 경우 AutoSupply등의 다른 서비스가 됩니다.
로그를 하나씩 읽어보면
- 사용자에 의해 주문생성
- 주문에 의해 결재 생성
- 결재가 잔고 부족의 의유로 실패
- 주문생성을 롤백
이 로그를 통해 트랜잭션의 정상/비정상 처리 상태와 성공을 확인할 수 있습니다.
참고자료
이미지 출처 https://blog.couchbase.com/saga-pattern-implement-business-transactions-using-microservices-part/
https://www.youtube.com/watch?v=E8-e-3fRHBw
Data consistency in microservices architecture in ebay https://ebaytech.berlin/data-consistency-in-microservices-architecture-bf99ba31636f
마이크로서비스 아키텍처에 대해 궁금한점이 있다면 포스트 하단의 링크(페이스북, 이메일)를 통해 문의주세요.