분산처리 환경에서의 송금 서비스 디자인 1 -Saga

Created
May 27, 2025
Created by
D
DaEun Kim
Tags
system-design
Property

참고자료

우리가 은행/증권 앱에서 자주 사용하는 송금 기능은 트랜잭션의 원자성을 철저히 보장해야만 한다. TPS 가 높지 않은 서비스라면 송금과 관련된 쓰기 쿼리들은 단일 primary DB 노드에서 하나의 트랜잭션으로 묶어서 commit/rollback 하면 된다. 하지만 데이터베이스를 샤딩으로 나눌 만큼 TPS가 높은 서비스에서 사용자들의 계좌 정보가 서로 다른 샤드에 저장되어 있다면, 송금 트랜잭션의 원자성을 어떻게 보장할 수 있을까?

2PC(Two Phase Commit) 코디네이터를 별도로 둬서 코디네이터가 각 샤드 내 트랜잭션의 commit/rollback 을 조정하는 방법도 있으나 아래와 같은 단점이 있다.

  1. 2PC 코디네이터가 SPoF(Single Point of Failure)가 될 수 있다. SPoF 되지 않으려면 고가용성을 보장하도록 분산처리 되어야 하는데 구현 난이도가 너무 높다.
  2. 하나의 샤드에서 트랜잭션에 대한 결과를 코디네이터에게 전달할 때까지 다른 샤드의 트랜잭션이 대기해야 한다. 이는 데이터베이스 성능을 떨어트린다.

Saga

Saga 는 2PC 와 같이 분산된 노드를 대상으로 단일 트랜잭션을 수행하는 데 필요한 해결법 중 하나로 MSA 에서 표준으로 채택된 방법이다. Saga 는 크게 2가지 단계로 나뉜다.

  1. 각 샤드 내에서 수행할 연산을 순서대로 수행한다.
  2. 예) 1번 샤드에서 사용자 A 계좌 감액 → 2번 샤드에서 사용자 B 계좌 증액

  3. 연산이 실패하면 전체 프로세스는 실패한 연산부터 맨 앞 연산까지 역순으로 보상 트랜잭션(compensating transaction) 을 통해 롤백한다.
image

보상 트랜잭션 내에 있는 연산을 포함한 모든 연산들을 각 샤드에서 수행하도록 조율하는 방식은 2가지가 있다.

choreography

saga 트랜잭션에 관여하는 모든 서비스가 다른 서비스의 이벤트를 구독하여 작업하는 탈중앙화 방식으로, 이벤트 스트리머가 필요하다.

송금 트랜잭션이 정상적으로 처리되는 경우
송금 트랜잭션이 정상적으로 처리되는 경우

1. A 계좌 -1000 commit

  • 데이터베이스에 A 계좌 출금과 관련된 트랜잭션 수행 ex) 잔액 갱신, 송금이력 생성
  • 출금 트랜잭션에는 출금 이벤트가 성공적으로 발행되는 것까지 포함되어야 함

2. A 계좌 출금 이벤트 발행

  • A 계좌에서 출금했음을 알리는 이벤트 발행
  • 이벤트 페이로드 예시
    • transfer_id 키/값은 이벤트를 수신하는 곳에서 멱등성을 보장하기 위한 수단
{
   "state": "DEBIT_SUCCEED"
   "fromAccount": "A",
   "toAccount": "B",
   "amount": 1000,
   "transfer_id": "78453453",
}

3. A 계좌 출금 이벤트 수신

  • A 계좌에서 출금했음을 알리는 이벤트 수신

4. B 계좌 +1000 commit

  • 데이터베이스에 B 계좌 증액과 관련된 트랜잭션 수행 ex) 잔액 갱신, 입금이력 생성
송금 과정에서 오류가 발생하여 보상 트랜잭션을 수행하는 경우
송금 과정에서 오류가 발생하여 보상 트랜잭션을 수행하는 경우

4. B 계좌 +1000 rollback

  • 모종의 이유로 데이터베이스에서 롤백 발생 ex) 계좌 유효성 검사 실패, 샤드 다운 이슈

5. B 계좌 입금실패 이벤트 발행

  • B 계좌에 입금 처리가 되지못했음을 알리는 이벤트 발행
  • 이벤트 페이로드 예시
{
  "state": "CREDIT_FAILED",
  "fromAccount": "A",
  "toAccount": "B",
  "amount": 1000,
  "reason": ...,
  "transfer_id": "78453454",
} 

6. 계좌 입금실패 이벤트 수신

  • B 계좌 입금에 실패했음을 알리는 이벤트 수신

7. 계좌 + 1000 commit

  • B 계좌 입금에 실패했으므로 A 계좌 출금에 대한 보상 트랜잭션 수행 ex) 잔액 갱신, 출금이력 삭제

orchestration

하나의 코디네이터가 모든 서비스들이 순서대로 작업을 실행하도록 조율하는 중앙화 방식

(작성중)

  • 송금 서비스에서 saga 가 활용되는 사례 (다이어그램)
  • 연산 순서의 2가지 방식 (코레오그래피, 오케스트레이션)
  • 샤드 대상으로 saga 구현한 예시 코드
  • MSA 에서 saga 가 사용되는 사례 (다이어그램)