-
MSA 분산 트랜잭션 설계의 기원: 1987년 Sagas 논문과 완화된 원자성
ARCHIVE 2026. 10. 15. 09:03반응형마이크로서비스 아키텍처(MSA)에서 데이터 정합성을 다룰 때 가장 먼저 마주치는 해법이 사가(Saga) 패턴이다. 오늘날 카프카(Kafka) 기반 이벤트 코레오그래피나 템포럴(Temporal), 캠문다(Camunda) 같은 워크플로 오케스트레이터로 구현되는 이 패턴은 1987년 헥터 가르시아-몰리나(Hector Garcia-Molina)와 케네스 살렘(Kenneth Salem)이 발표한 논문 "Sagas"에 뿌리를 둔다.
40년 전 데이터베이스 엔지니어들이 단일 DBMS 환경에서 마주했던 병목과 이를 극복하기 위해 제안한 원자성 완화(Relaxing Atomicity) 원리는 현대 분산 트랜잭션 설계의 본질을 그대로 관통한다.
1. LLT(Long-Lived Transaction)와 전통 ACID의 붕괴
전통적인 데이터베이스 트랜잭션 모델은 ACID(원자성·일관성·격리성·지속성)를 전제한다. 트랜잭션이 수 분에서 수 시간, 길게는 수일에 걸쳐 이어지는 장기 트랜잭션(LLT, Long-Lived Transaction) 환경에서는 ACID 모델이 시스템 성능을 무너뜨린다.
🔍 클릭하여 확대flowchart TD LLT["장기 트랜잭션 (LLT)\n대용량 배치·결산·외부 승인 대기"] L1["1. 락 지연 (Locking Delay)\n커밋 시점까지 배타 락 유지\n단기 OLTP 트랜잭션 전면 차단"] L2["2. 데드락 폭발 (Deadlock Explosion)\nJim Gray 법칙: 데드락 빈도 ∝ N⁴\n접근 객체 수 급증에 따른 연쇄 충돌"] L3["3. 중단 확률 (Abort Probability)\n실행 시간 비례 장애 확률 증가\n거대 롤백 및 재시작 반복"] LLT --> L1 LLT --> L2 LLT --> L31987년 논문은 LLT가 시스템을 마비시키는 요인을 세 가지로 짚었다.
- 락 지연(Locking Delay): 엄격한 2단계 락킹(2PL) 규약에 따라 트랜잭션이 끝날 때까지 락을 잡는다. 수초 내에 끝나는 단기 트랜잭션들이 줄지어 대기하며 전체 처리량이 급락한다.
- 데드락 폭발(Deadlock Explosion): 짐 그레이(Jim Gray)의 분석에 따르면 시스템 내 데드락 발생 빈도는 트랜잭션 크기(접근 객체 수) $N$의 네제곱($N^4$)에 비례한다. 객체를 대량으로 읽고 쓰는 LLT는 시스템 전반에 데드락을 유발한다.
- 중단 확률(Abort Probability) 증가: 실행 시간이 길어질수록 하드웨어 결함이나 타임아웃으로 인한 강제 중단 위험이 커진다. 끝 무렵에 실패하면 거대한 원복 비용이 들고, 다시 처음부터 재실행하며 지연이 누적된다.
논문은 "There is no solution that eliminates the problem of LLT(전통적 ACID 체계 안에서는 LLT 문제를 완전히 없앨 해법이 없다)"고 선언했다.
2. 패러다임 전환: 원자성 완화와 시맨틱 보상
가르시아-몰리나와 살렘은 거대한 단일 트랜잭션의 원자성을 포기하는 역발상을 제안했다.
"Relaxing the requirement that an LLT execute as an atomic unit."
전체 작업을 단 하나의 원자적 블록으로 묶지 않고, 독립적으로 커밋과 락 해제가 가능한 작은 하위 트랜잭션 시퀀스로 분해한다.
$$S = (T_1, T_2, \dots, T_n)$$
각 하위 트랜잭션 $T_i$는 데이터베이스의 국소적 일관성을 보장하며 즉시 커밋된다. 락은 단계마다 즉각 해제되어 다른 트랜잭션의 진입을 허용한다.
🔍 클릭하여 확대sequenceDiagram autonumber participant Client as 사가 코디네이터 participant T1 as 하위 트랜잭션 1 (예약) participant T2 as 하위 트랜잭션 2 (결제) participant T3 as 하위 트랜잭션 3 (발권 실패) participant C2 as 보상 트랜잭션 2 (결제 취소) participant C1 as 보상 트랜잭션 1 (예약 취소) Client->>T1: 실행 및 즉시 커밋 (락 해제) Client->>T2: 실행 및 즉시 커밋 (락 해제) Client->>T3: 실행 시도 -> 예외 발생 (Abort) Note over Client,C1: 시맨틱 롤백 (역순 보상 트랜잭션 발동) Client->>C2: 보상 실행 (결제 환불 커밋) Client->>C1: 보상 실행 (좌석 원복 커밋)중간 단계에서 실패가 발생하면 데이터베이스 엔진의 물리적 롤백(
UNDO로그 복원)을 사용하지 않는다. 이미 커밋된 이전 트랜잭션들을 비즈니스 관점에서 상쇄하는 보상 트랜잭션(Compensating Transaction) 시퀀스를 역순으로 실행한다.$$C_n, \dots, C_2, C_1$$
비행기 표 예매를 예로 들면, DB 로우를 트랜잭션 시작 전 스냅샷으로 덮어쓰는 행위가 아니라 이미 완료된 '좌석 점유 커밋'에 대응하여 '예약 취소 커밋'을 새롭게 발행하는 시맨틱 무효화(Semantic Invalidation) 방식이다.
3. 사가 실행 프로토콜과 3가지 복구 전략
1987년 논문은 개발자가 상태 머신을 명시적으로 제어하도록 프로토콜을 규정했다.
원전 API 설명 Begin Saga 사가 식별자(ID) 발급 및 전체 흐름 추적 시작 Begin Transaction 하위 트랜잭션 시작. 실행 전 대응하는 보상 트랜잭션 ID와 파라미터를 미리 등록 Abort Saga 사가 강제 중단. 이미 성공한 트랜잭션들을 대상으로 등록된 보상 트랜잭션을 역순 호출 End Saga 정상 종료 선언. 등록된 보상 메타데이터 폐기 장애 복구 전략은 업무 특성에 따라 세 갈래로 나뉜다.
🔍 클릭하여 확대flowchart TD Fail["하위 트랜잭션 Ti 실패"] Fail --> BR["후진 복구 (Backward Recovery)\n실행 완료된 Ti-1...T1을 역순 보상(Ci)\n주문 취소, 결제 환불 등 표준 취소 경로"] Fail --> FR["전진 복구 (Forward Recovery)\n실패 지점(Savepoint)부터 재시도\n보상이 불가능하거나 반드시 성공해야 하는 과업"] Fail --> PR["부분 복구 (Partial Recovery)\n실패한 단계만 국소 보상 후 대체 경로 실행\n대체 PG사 결제 우회, 보조 좌석 재선택"]- 후진 복구(Backward Recovery): 가장 일반적인 취소 방식이다. 성공한 단계들을 역순으로 보상 트랜잭션 처리하여 시스템을 시작 지점의 등가 상태로 되돌린다.
- 전진 복구(Forward Recovery): 보상 처리가 불가능하거나 반드시 완료되어야 하는 작업에 쓴다. 실패 지점의 저장점(Savepoint)부터 멱등성을 확보하며 성공할 때까지 재시도한다.
- 부분 복구(Partial Recovery): 실패한 특정 단계만 보상한 뒤 대체 전략(예: 1차 결제 대행사 실패 시 2차 결제사 호출)으로 분기하여 사가를 끝까지 완수한다.
4. 1987년 논문의 구현체: SEC와 Saga Daemon
가르시아-몰리나와 살렘은 DBMS 커널을 뜯어고치지 않고 사가를 구현하는 아키텍처를 제시했다.
🔍 클릭하여 확대flowchart LR App["애플리케이션 계층\n비즈니스 로직"] --> SEC["Saga Execution Component (SEC)\n트랜잭션 순서 제어"] SEC --> DB["단일 DBMS / 스토리지"] SEC --> Log["이중 사가 로그 (Duplex Log)\n사가 진행 상태 및 보상 메타데이터"] SD["Saga Daemon\n비정상 종료 모니터링 프로세스"] -.->|주기적 검사| Log SD -.->|미완료 사가 감지 시| SEC- SEC (Saga Execution Component): 트랜잭션 조정자 상위에서 사가의 진행 순서와 상태 전이를 통제한다. 장애로 프로세스가 죽어도 상태를 잃지 않도록 이중 사가 로그(Duplex Log)에 하위 트랜잭션 진행 상황을 남긴다.
- Saga Daemon: 백그라운드 데몬 프로세스다. 메인 프로세스 비정상 종료 시 미완료 사가를 로그에서 감지하고, 보상 트랜잭션이나 재시도를 자동으로 기동한다.
- 애플리케이션 주도 보상: 보상 로직과 파라미터는 DB 내부 엔진이 아닌 애플리케이션 테이블에 관리된다. 사가의 정합성은 인프라가 아닌 비즈니스 로직의 책임 영역이다.
5. 1987년 논문에서 현대 MSA로
크리스 리차드슨(Chris Richardson)을 비롯한 현대 분산 아키텍트들은 이 설계를 네트워크 분할 환경의 마이크로서비스로 옮겨왔다. 서비스마다 데이터베이스를 격리하는 MSA 환경에서는 2단계 커밋(2PC, Two-Phase Commit)의 코디네이터 단일 장애점(SPOF)과 분산 락 병목 탓에 사가 패턴이 사실상 표준으로 자리 잡았다.
비교 축 코레오그래피 (Choreography) 오케스트레이션 (Orchestration) 제어 방식 도메인 이벤트 기반 비중앙 분산 제어 중앙 오케스트레이터의 명시적 워크플로 지휘 인프라 Kafka, RabbitMQ 등 메시지 브로커 Temporal, Camunda, AWS Step Functions 장점 서비스 간 결합도 저하, 빠른 단일 서비스 구현 전체 흐름 시각화 용이, 모니터링과 디버깅 명확 단점 연쇄 이벤트 추적 난해, 데드락·순환 참조 위험 오케스트레이터 계층 관리 부담, 병목 가능성 🔍 클릭하여 확대flowchart LR subgraph Choreography["코레오그래피 (이벤트 기반)"] O1["주문 서비스"] -->|주문 생성 이벤트| P1["결제 서비스"] P1 -->|결제 완료 이벤트| D1["배송 서비스"] D1 -.->|재고 부족 이벤트| P1 end subgraph Orchestration["오케스트레이션 (중앙 집중)"] SagaMgr["사가 오케스트레이터\n(상태 머신 엔진)"] SagaMgr -->|1. 결제 명령| P2["결제 서비스"] SagaMgr -->|2. 배송 명령| D2["배송 서비스"] SagaMgr -.->|실패 시 보상 명령| P2 end
6. 사가 패턴이 요구하는 아키텍처 대가: 격리성(Isolation) 부재
원자성을 완화하여 처리량을 얻은 대가로 사가 패턴은 격리성(Isolation)을 잃는다.
하위 트랜잭션이 커밋되는 순간 아직 전체 비즈니스 프로세스가 끝나지 않았음에도 중간 변경 사항이 외부 트랜잭션에 노출된다.
- 더티 리드(Dirty Read): 결제 완료 후 배송 단계에서 실패하여 취소될 운명인 주문 데이터를 다른 분석 배치가 읽는다.
- 갱신 손실(Lost Update): 사가 A가 데이터를 변경하고 커밋한 직후, 사가 B가 동일 데이터를 덮어쓰고, 이후 사가 A가 실패하여 보상 트랜잭션을 실행할 때 사가 B의 변경분이 지워진다.
실무 분산 시스템은 격리성 결여를 방어하기 위해 세 가지 보완책을 쓴다.
- 시맨틱 락(Semantic Lock): 자원 레코드에
PENDING_CONFIRMATION같은 비즈니스 플래그를 두고, 타 트랜잭션의 임의 수정을 방어한다. - 비관적 관점의 가환 연산(Commutative Updates): 입출금 순서가 뒤바뀌어도 최종 합계가 어긋나지 않도록 연산을 덧셈·뺄셈 형태로 정규화한다.
- 읽기 전용 더티 데이터 표기: 사용자 화면에 '처리 중' 상태를 명시하여 중간 상태 데이터임을 알린다.
반응형'ARCHIVE' 카테고리의 다른 글
카프카를 탄생시킨 핵심 설계 원칙: 2011 NetDB 논문과 초고처리량 아키텍처 (0) 2026.10.10 카프카(Kafka)를 메시지 큐로만 쓰면 안 되는 이유: 'The Log'와 분산 커밋 로그 아키텍처 (0) 2026.09.21