들어가기 전
하나의 서비스가 특정 비즈니스 로직을 수행할 때 종종 다른 서비스와 연계되어 함께 작업을 진행해야 할 때가 많습니다. 이때 두 가지 이상의 서비스에서 트랜잭션이 실행되면 "모두 실패하거나" 또는 "모두 성공하거나" 하는 상황이 자연스럽게 요구됩니다. 일부 서비스는 작업이 절반만 완료되고 다른 서비스는 작업이 실패하는 경우를 상정하고 시스템을 설계하지 않을뿐더러 그런 일은 발생해서는 안되기 때문입니다.
또 다른 예로 사용자 경험을 개선하기 위해 빠른 응답을 제공하기 위해서 서비스 로직이 비동기로 처리되는 경우도 있습니다. 예를 들어 회원 가입 후 안내 메일을 발송하는 로직을 있을 때 이 메일 발송 기능이 특정 이유로 오래 걸린다면 사용자는 가입이 완료되었다는 응답을 지연받게 됩니다. 이를 방지하기 위해 먼저 회원 가입 완료를 사용자에게 알리고 메일 발송 로직을 비동기로 분리해 처리할 수 있습니다.
위에서 설명한 예와 같이 2개 이상의 네트워크 상의 시스템 간에도 데이터의 일관성이 중요하게 지켜져야 하는 경우가 있는 반면 하나의 시스템 내에서 비동기적으로 수행되는 작업에서도 데이터의 최종 일관성이 요구될 수 있습니다. 별도의 스레드에서 처리되는 작업이 최종적으로 데이터의 일관성을 보장해야 할 수도 있습니다.
일반적인 동기식 시스템에서는 데이터의 일관성을 쉽게 보장할 수 있지만 비동기 환경에서는 몇몇 사항들을 고려하지 않고 조치하게 되면 최종적 데이터 일관성을 유지하기 어렵게 될 수 있습니다.
분산 트랜잭션
위키백과에 따르면 분산 트랜잭션(Distributed Transaction)은 두 개 이상의 네트워크 상의 시스템에서 트랜잭션이 실행되는 경우를 의미합니다. 일반적으로 시스템은 트랜잭션 매니저(Transaction Manager) 역할을하며 트랜잭션 리소스와 관련된 모든 작업을 관리하고 트랜잭션의 생성, 커밋, 롤백 등을 담당하게 됩니다.
애플리케이션이 고도화되고 비즈니스 로직이 점차 복잡해지면서 여러 시스템으로 분산된 네트워크 환경에서 데이터 일관성을 유지하는 것이 매우 중요해졌습니다. 특히 데이터베이스 트랜잭션에서 지켜야 할 ACID 원칙(Atomicity, Consistency, Isolation, Durability)을 분산된 환경에서도 동일하게 적용해야 하는 필요성이 대두되었습니다.
예를 들어 비즈니스 로직에서 데이터의 수정, 삽입, 삭제 등 여러 작업이 동시에 이루어질 때 각 시스템에서 데이터의 무결성을 보장해야만 전체 시스템이 신뢰성을 가질 수 있습니다. 이를 위해 데이터베이스의 트랜잭션 개념을 넘어 애플리케이션의 비즈니스 계층에도 ACID 원칙을 적용하게 된 것입니다.
위와 같이 애플리케이션 비즈니스 계층에서 분산 트랜잭션을 관리하는 대표적인 방식인 2-Phase Commit 과 Saga 패턴을 간략하게 소개하겠습니다.
2 Phase Commit
위 그림에서 확인할 수 있듯이 2 Phase Commit(2PC) 는 단일 노드 트랜잭션에서는 나타나지 않는 Coordinator 라는 트랜잭션 매니저가 등장하게 됩니다. 해당 Coordinator 는 가장 첫 단계에서 데이터를 쓰기 위한 작업을 수행합니다. 이때 유니크한 트랜잭션 아이디를 생성하고 각 데이터베이스에 전달합니다. 각각의 데이터베이스는 락을 획득하며 쓰기 작업을 진행한 후 첫번째 phase 에 진입합니다.
첫번째 phase 에서 각 Coordinator 는 각각의 노드에게 커밋 가능 여부를 묻게되며 각각의 노드의 응답에 따라서 커밋 또는 롤백을 할지 결정하게 됩니다. 그 후 두번쨰 phase 에서 각 노드의 응답 값에 따라 Coordinator 는 커밋 또는 롤백을 실제로 수행하게 됩니다.
여기서 중요한 것은 각각의 노드의 응답을 번복할 수 있는 포인트가 없다는 점입니다. Coordinator 가 한 번 결정하게 되면 그 결정을 반드시 강제하여 실행됩니다. 그리고 2PC의 가장 큰 단점은 블록킹으로 동작하는 것 입니다.
SAGA Pattern
SAGA 패턴 또한 분산 트랜잭션을 관리하기 위한 시스템 디자인 패턴 중 하나로 각각의 서비스들끼리 이벤트를 주고 받으면서 분산 환경에서의 데이터 일관성을 보장합니다. 각 서비스들은 로컬 트랜잭션을 가지고 있으며 해당 서비스의 데이터를 업데이트하며 메시지 또는 이벤트를 발행하여 다음 단계 서비스를 호출하는 방식으로 동작합니다. 만약 해당 프로세스가 실패하게 되면 데이터 정합성을 맞추기 위해 이전 트랜잭션에 대한 보상 트랜잭션을 실행합니다.
SAGA 패턴은 트랜잭션의 관리 주체가 DB 가 아닌 애플리케이션에 있기 때문에 보상 트랜잭션 또한 애플리케이션에 구현되어야 합니다. 각각의 동작들 속에서 전체 데이터가 동시에 영속화되는 것이 아닌 순차적인 단계의 트랜잭션으로 구성되어 있습니다.
이 중 SAGA 패턴을 구현하는 대표적인 방법은 다음 두가지가 있습니다.
- Choreography SAGA
- Orchestration SAGA
먼저 Choreography 방식은 각 서비스끼리 이벤트를 주고 받는 방식으로 각 서비스가 다른 서비스의 로컬 트랜잭션을 이벤트 트리거하는 방식으로 동작합니다.모든 서비스가 메시지 브로커를 통해 이벤트를 Pub/Sub 하는 구조입니다. 따라서 중앙 집중형이 아니기 때문에 SPOF(단일 실패지점)이 없다느 것이 특징입니다.
다음 Orchestration 방식은 중앙 집중형으로 Ochestrator 가 서비스 실행 흐름을 관리하며 요청을 실행, 상태 확인, 실패에 대한 보상 트랜잭션을 실행하는 주체로 존재합니다. 따라서 트랜잭션 처리를 위한 별도의 트랜잭션 매니저 인스턴스가 존재하며 서비스의 전체 워크플로우를 관리하기 떄문에 SPOF(단일 실패지점)가 될 가능성이 있습니다.
오탈자 및 오류 내용을 댓글 또는 메일로 알려주시면, 검토 후 조치하겠습니다.
'ETC' 카테고리의 다른 글
[ETC] Merge 문의 동시성 (1) | 2024.03.24 |
---|