https://microservices.io/patterns/data/saga.html 을 번역한 글입니다.
Context
당신은 Database per Service 패턴을 적용했다. 각각 의 서비스는 자신만의 데이터베이스를 가지고 있다. 그런데, 한 비즈니스 트랜잭션이 여러 서비스에 걸쳐 존재하고 당신은 이를 구현하기 위한 메커니즘이 필요하다. 예를 들어, 구매자에게 신용한도가 있는 e-커머스 스토어를 만든다고 해보자. Order 서비스와 Customer 서비스는 각각의 데이터베이스를 가지고 있어 로컬 ACID 트랜잭션을 쓸 수 없는 상태이다.
Problem
어떻게 하면 여러 서비스에 걸쳐 트랜잭션을 구현할 수 있을까?
Forces
- 2PC (2 Phase Commit)은 사용하지 않는다.
Solution
여러 서비스에 걸친 비즈니스 트랜잭션은 saga로 구현한다. Saga는 연속된 로컬 트랜잭션이다. 각각의 로컬 트랜잭션은 데이터베이스를 업데이트하고 메시지나 이벤트를 발행해 saga 다음 트랜잭션을 트리거한다. 만약 비즈니스 규칙을 위반하여 로컬 트랜잭션이 실패하면 saga는 이전 트랜잭션으로 인한 변화를 되돌리기 위해 보상 트랜잭션들을 실행한다.
여기에 saga를 조정(coordination)하는 두 방식이 있다.
- Choreography - 각각의 로컬 트랜잭션이 다른 서비스의 로컬 트랜잭션을 트리거 하는 도메인 이벤트를 발행한다.
- Orchestration - 오케스트레이터가 다른 서비스들에게 로컬 트랜잭션을 실행하도록 지시한다.
Example: Choreography-based saga
e-커머스 어플리케이션이 choreography 기반의 saga를 사용하여 ‘주문’을 생성하려고 한다면, 아래 단계를 따른다.
Order
서비스는POST /orders
요청을 받고Order
를PENDING
상태로 생성한다.Order Created
이벤트를 발행한다.Customer
서비스의 이벤트 핸들러가 이를 받아 ‘신용’을 확보한다.- 위 결과를 의미하는 이벤트를 발행한다.
Order
서비스의 이벤트 핸들러가Order
를 승인하거나 거절한다.
Example: Orchestration-based saga
e-커머스 어플리케이션이 orchestration 기반의 saga를 사용하여 ‘주문’을 생성하려고 한다면, 아래 단계를 따른다.
Order
서비스가POST /orders
요청을 받고Create Order
saga 오케스트레이터를 생성한다.- saga 오케스트레이터는
Order
를PENDING
상태로 생성한다. - 그리고
Reserve Credit
명령(command)를Customer
서비스로 보낸다. Customer
서비스는 ‘신용’을 확보한다.Customer
서비스는 결과를 saga 오케스트레이터에게 보낸다.- saga 오케스트레이터는
Order
를 승인하거나 거절한다.
Resulting context
이 패턴은 다음과 같은 장점이 있다.
- 분산 트랜잭션 없이 여러 서비스에 걸쳐 데이터 일관성을 유지할 수 있도록 한다.
이 패턴은 다음과 같은 단점이 있다.
- 프로그래밍 모델이 복잡하다. 예를 들어, 개발자는 앞선 변화를 되돌릴 수 있도록 반드시 보상 트랜잭션을 설계해야 한다.
이 패턴은 또한 아래 대처해야 할 이슈가 있다.
- 신뢰성을 위해 서비스는 반드시 원자적으로 데이터베이스를 업데이트 한 후 메시지/이벤트를 발행해야 한다. 이때 데이터베이스와 메시지 브로커에 걸친 분산 트랜잭션 메커니즘은 사용할 수 없다. 대신, 다음에 소개 될 아래 패턴 중 하나를 사용해야 한다.
- 클라이언트는 동기적인 요청(e.g. HTTP
POST /orders
)을 통해 saga를 동작 시키고, 그 결과를 비동기적으로 얻어야 한다. 여기에는 트레이드오프가 있는 몇가지 옵션이 있다.- 서비스는 saga가 완료되면 그 결과를 응답한다. (e.g.
OrderApproved
또는OrderRejected
이벤트) - 서비스는 saga를 동작시킨 뒤
orderID
를 포함하여 응답한다. 클라이언트는 주기적으로GET /orders/{orderID}
를 요청해 polling 하여 결과를 얻는다. - 서비스는 saga를 동작시킨 뒤
orderID
를 포함하여 응답한다. 그리고 saga가 완료되면 웹 소캣 또는 웹 훅 등을 이용해 이벤트를 발행한다.
- 서비스는 saga가 완료되면 그 결과를 응답한다. (e.g.
Related patterns
- 이 패턴은 Database per Service pattern의 필요로 인해 고안됐다.
- 아래 패턴 들은 원자적으로 업데이트와 메시지/이벤트를 발행하는 방법이다.
- Choreography 기반 saga는 Aggregates와 Domain Events를 사용해 이벤트를 발행한다.
Learn more
- 책 Microservices patterns는 이 패턴에 대해 상세히 설명한다. 이 책의 example application은 Eventuate Tram Sagas framework를 사용한 orchestration 기반의 saga로 구현되어있다.
- self-paced, online bootcamp에서 여러 서비스에 걸쳐 saga를 사용하는 방법, API Composition, CQRS 패턴을 배울 수 있다.
- Saga 패턴에 관한 블로그 포스트
- Saga와 비동기 마이크로서비스에 대한 발표들
Example code
아래 예제는 Customer, Order 예제를 각각 다른 방식으로 구현한다.