트랜잭션은 애플리케이션의 필수적인 부분입니다. 트랜잭션이 없으면 데이터 일관성을 유지하는 것이 불가능합니다.

가장 강력한 트랜잭션 유형 중 하나는 2단계 커밋이라고 하며, 요약하면 다음과 같습니다. 첫 번째 트랜잭션의 커밋은 두 번째 트랜잭션의 완료에 따라 달라집니다. 주문 확인과 재고 업데이트 등 여러 개 엔티티를 동시에 업데이트해야 할 때 특히 유용합니다.

그러나 예를 들어 마이크로서비스 오케스트레이션을 수행하면 상황이 더 복잡해집니다. 각 서비스는 자체 데이터베이스가 있는 별도의 시스템이며, 더 이상 전체 시스템의 일관성을 유지하기 위해 로컬 2단계 커밋의 단순성을 활용할 수 없습니다.

이 기능을 잃게 되면, 동일한 '단일 엔티티 원자 트랜잭션'을 수행하지만 수십 배 더 빠르게 수행할 수 있는 NoSQL 데이터베이스 Couchbase처럼요. 그렇기 때문에 마이크로서비스를 사용하는 대부분의 회사에서도 NoSQL을 사용하고 있습니다.

이 문제를 예시하기 위해 전자상거래 시스템의 다음과 같은 상위 수준의 마이크로서비스 아키텍처를 고려해 보세요:

위의 예에서는 주문, 고객 청구, 재고 업데이트, 배송 전송을 모두 단일 ACID 트랜잭션으로 처리할 수 없습니다. 이 전체 흐름을 일관되게 실행하려면 분산 트랜잭션을 생성해야 합니다.

분산된 모든 것을 구현하는 것이 얼마나 어려운지 우리 모두는 잘 알고 있으며, 안타깝게도 트랜잭션도 예외는 아닙니다. 일시적인 상태 처리, 서비스 간의 최종 일관성, 격리 및 롤백은 설계 단계에서 고려해야 할 시나리오입니다.

다행히도 저희는 이미 는 20년 넘게 분산 트랜잭션을 구현해오면서 몇 가지 좋은 패턴을 생각해냈습니다. 오늘 말씀드리고자 하는 패턴은 사가 패턴입니다.

SAGA 패턴

A 사가 패턴 는 각 트랜잭션이 단일 서비스 내에서 데이터를 업데이트하는 로컬 트랜잭션의 시퀀스입니다. 사가의 첫 번째 트랜잭션은 시스템 작동에 해당하는 외부 요청에 의해 시작되며, 이후 각 단계는 이전 단계가 완료되면 트리거됩니다.

분산 트랜잭션의 가장 잘 알려진 패턴 중 하나는 사가입니다. 이에 관한 첫 번째 논문 는 1987년에 출판되었습니다. 그리고 사가들은 인기 있는 솔루션 그 이후로

앞의 이커머스 예시를 사용하면 매우 높은 수준의 디자인에서 사가 패턴 구현은 다음과 같이 보일 것입니다:

saga implementation

사가 트랜잭션을 구현하는 방법에는 몇 가지가 있지만 가장 많이 사용되는 두 가지 방법이 있습니다:

  • 이벤트/안무: 중앙 조정이 없는 경우, 각 서비스는 다른 서비스의 이벤트를 생성하고 청취하여 조치를 취할지 여부를 결정합니다.
  • 명령/오케스트레이션코디네이터 서비스가 사가의 의사 결정과 비즈니스 로직의 순서를 중앙 집중화할 책임이 있는 경우

각 구현을 조금 더 자세히 살펴보고 사가의 작동 방식을 이해해 보겠습니다.

 

이벤트/안무

이벤트/안무 접근 방식에서는 첫 번째 서비스가 트랜잭션을 실행한 다음 이벤트를 게시합니다. 이 이벤트는 로컬 트랜잭션을 실행하고 새 이벤트를 게시(또는 게시하지 않음)하는 하나 이상의 서비스에서 수신 대기합니다.

분산 트랜잭션은 마지막 서비스가 로컬 트랜잭션을 실행하고 이벤트를 게시하지 않거나 게시된 이벤트가 사가의 참여자에게 들리지 않으면 종료됩니다.

이커머스 예시에서 사가 패턴이 어떻게 보이는지 살펴보겠습니다:

  1. 주문 서비스 은 새 주문을 저장하고 상태를 보류 중 라는 이벤트를 게시하고 주문_생성_이벤트.
  2. 그리고 결제 서비스 청취 주문_생성_이벤트를 클릭하고 클라이언트에 요금을 청구하고 이벤트를 게시합니다. 청구된_주문_이벤트.
  3. 그리고 재고 서비스 청취 청구된_주문_이벤트재고를 업데이트하고 주문에서 구매한 제품을 준비하여 게시합니다. 주문_준비된_이벤트.
  4. 배달 서비스 청취 주문_준비된_이벤트 를 클릭한 다음 제품을 픽업하여 배송합니다. 마지막에는 주문_배송_이벤트
  5.  마지막으로, 주문 서비스 청취 주문_배송_이벤트 를 클릭하고 주문 상태를 완료로 설정합니다.

위의 경우 주문 상태를 추적해야 하는 경우 주문 서비스는 모든 이벤트를 수신하고 상태를 업데이트하기만 하면 됩니다.

 

분산 트랜잭션의 롤백

분산 트랜잭션 롤백은 무료로 제공되지 않습니다. 일반적으로 이전에 수행한 작업에 대해 또 다른 보상 트랜잭션을 구현해야 합니다.

거래 중에 주식 서비스가 실패했다고 가정해 보겠습니다. 롤백이 어떻게 진행되는지 살펴봅시다:

  1. 재고 서비스 생산 제품_품절_이벤트;
  2. 둘 다 주문 서비스 그리고 결제 서비스이전 메시지를 듣습니다:
    1. Payment 서비스 고객에게 환불
    2. 주문 서비스 주문 상태를 실패로 설정     

각 트랜잭션에 대해 공통 공유 ID를 정의하는 것이 중요하므로 이벤트를 던질 때마다 모든 리스너가 어떤 트랜잭션을 가리키는지 바로 알 수 있습니다.

 

사가의 이벤트/안무 디자인 패턴 사용의 장점과 단점

이벤트/안무는 사가 오케스트레이션 패턴을 자연스럽게 구현할 수 있는 방법입니다. 간단하고 이해하기 쉬우며 구축하는 데 많은 노력이 필요하지 않습니다. 참가자들은 서로에 대한 직접적인 지식이 없기 때문에 느슨하게 결합되어 있습니다.. 거래에 2~4단계가 포함되는 경우 매우 적합할 수 있습니다.

그러나 이 접근 방식은 어떤 서비스가 어떤 이벤트를 수신하는지 추적하기 어렵기 때문에 트랜잭션에 추가 단계를 계속 추가할 경우 빠르게 혼란스러워질 수 있습니다. 또한 서비스 간에 주기적인 종속성이 추가될 수도 있습니다. 서로의 구독을 구독해야 하므로 이벤트.

마지막으로, 모든 서비스를 실행해야 하는 트랜잭션 패턴을 시뮬레이션하려면 이 디자인을 사용하여 테스트를 구현하는 것이 까다로울 수 있습니다.

 

다음 게시물에서의 대부분의 문제를 해결하는 방법을 설명합니다. 다른 사가 구현을 사용한 이벤트/안무 접근법 명령/오케스트레이션.

그동안 마이크로서비스, 사가 아키텍처 또는 사가 애플리케이션에 대한 사가 디자인 패턴에 대한 질문이 있으시면 언제든지 다음 주소로 문의해 주세요. @deniswsrosa

작성자

게시자 데니스 로사, 개발자 옹호자, 카우치베이스

데니스 로사는 독일 뮌헨에 거주하고 있는 카우치베이스의 개발자 옹호자입니다. 그는 소프트웨어 엔지니어로서 탄탄한 경력을 쌓았으며 Java, Python, Scala, Javascript를 유창하게 구사합니다. Denis는 검색, 빅 데이터, AI, 마이크로서비스 및 개발자가 아름답고 빠르고 안정적이며 확장 가능한 앱을 만드는 데 도움이 되는 모든 것에 대해 글을 쓰는 것을 좋아합니다.

댓글 하나

  1. Couchbase에서 가장 잘 작동하는 방법에 대한 두 가지 질문이 있습니다.

    첫째, 다중 문서 ACID 트랜잭션이 없는 스토리지의 경우... 제가 알기로는 사가 패턴은 각 서비스 호출이 커밋 또는 롤백할 수 있는 단일 데이터베이스 트랜잭션에 해당한다고 가정합니다. 하지만 여러 개의 Couchbase 문서를 작성하는 서비스 호출이 있다면 어떻게 해야 할까요? 이것은 각 문서 쓰기를 별개의 이벤트로 모델링한다는 것을 의미할까요?

    둘째, 카우치베이스와 관련하여 카우치베이스 라이트도 사용한다면 어떨까요? 모바일 클라이언트는 사가 트랜잭션 중간에 다운스트림 동기화를 수행한 다음 롤백할 수 있으며, 클라이언트는 이벤트 스트림에 참여하지 않을 수 있습니다. 결국 클라이언트가 카우치베이스 서버의 상태를 따라잡고 발생하는 충돌을 처리해야 한다고 가정할 수 있을 것 같습니다. 그렇다면 업스트림 동기화는 어떨까요?

  2. [...] 사가 패턴은 각 트랜잭션이 단일 서비스 내에서 데이터를 업데이트하는 로컬 트랜잭션의 시퀀스입니다. [...]

댓글 남기기