Cloudflare Workflows, 사가 롤백으로 데이터 정합성 강화
Cloudflare Workflows는 장기 실행 프로세스에서 상태 지속성(State Persistence)과 재시도를 지원하지만, 단계 실패 시 이전 작업의 불일치 상태(Inconsistent State)를 야기할 수 있음
사가 롤백(Saga Rollback) 기능을 도입하여 각 단계 내에서 보상 로직(Compensation Logic)을 선언함으로써 실패 시 이전 작업의 데이터 정합성(Data Consistency)을 보장함
Idempotency Key를 활용하여 순방향 및 역방향 작업 모두 재시도 안전성을 확보하고, 역순 단계 실행(Reverse Step-Start Order)으로 롤백 순서를 예측 가능하게 함
사가 패턴(Saga Pattern)의 작동 원리
사가 패턴은 분산 트랜잭션에서 데이터 일관성(Data Consistency)을 유지하기 위한 디자인 패턴으로, 각 단계를 개별 트랜잭션으로 실행하고 실패 시 보상 트랜잭션(Compensating Transaction)을 통해 이전 상태로 복구함.
순방향 실행(Forward Execution): 각 단계는 독립적인 작업을 수행하며, 성공 시 다음 단계로 진행.
보상 트랜잭션(Compensation Transaction): 이전 단계의 작업을 취소하거나 되돌리는 작업으로, 실패한 단계의 역순으로 실행됨.
중요 고려사항: 각 보상 작업은 멱등성(Idempotency)을 가져야 하며, 실패 시에도 재시도 가능해야 함.
Cloudflare Workflows는 이 패턴을 내장하여 개발자가 복잡한 예외 처리 로직을 직접 구현할 필요 없이 내구성 있는 워크플로우(Durable Workflow)를 구축하도록 지원함.
롤백 API 설계: 플루언트 vs 빌더 vs 메타데이터
Cloudflare는 롤백 기능을 step.do() 메서드의 옵션 객체에 포함시키는 방식을 채택함. 이는 초기 고려했던 플루언트 API(`step.do(...).rollback(...)`)나 빌더 API(`step.saga(...).run()`)보다 명확성과 단순성을 제공함.
플루언트 API의 문제점: `step.do()`의 반환 값인 Promise의 비동기적 특성과 결합 시 실행 시점 예측이 어렵고 Promise 파이프라이닝(Promise Pipelining)과 충돌 가능성 존재.
빌더 API의 문제점: 추가적인 코드 복잡성(Ceremony)을 야기하며, 기존 `step.do()` 패턴과의 일관성을 해침.
메타데이터 방식의 장점: 롤백 로직을 순방향 로직과 같은 위치에 명시하여 가독성을 높이고, `step.do()`의 기존 동작 방식(Promise 반환, 즉시 실행)을 그대로 유지함.
결과적으로, 이 방식은 기존 API 확장이라는 목표에 부합하며 개발자가 쉽게 채택하고 이해할 수 있도록 함.
멱등성(Idempotency)과 재시도 안전성 확보
사가 롤백 구현에서 멱등성(Idempotency)은 매우 중요한 요소로, 순방향 작업과 보상 작업 모두 여러 번 실행해도 동일한 결과를 보장해야 함.
Idempotency Key 활용: 각 작업(순방향, 역방향)마다 고유한 키를 부여하여 외부 시스템에서 중복 실행을 방지함. 예를 들어, 송금 시 `transferId:debit-account-a`와 같은 키를 사용.
재시도 메커니즘: Workflows는 실패한 롤백 핸들러에 대해 재시도(Retries) 및 타임아웃(Timeout) 설정을 지원하며, 멱등성 키 덕분에 재시도 시에도 데이터 불일치 위험이 없음.
`output === undefined` 처리: 롤백 핸들러는 단계가 실패하기 전에 값을 반환하지 못했을 경우를 대비해 `output`이 `undefined`일 수 있음을 인지하고 처리해야 함.
이러한 설계는 안정적인 상태 관리(Stable State Management)를 가능하게 하여, 예기치 못한 오류 발생 시에도 워크플로우의 내구성(Durability)을 유지함.
롤백 실행 순서: 역순 단계 시작 기준
워크플로우 실패 시 롤백 핸들러는 역순 단계 시작 순서(Reverse Step-Start Order)에 따라 실행됨. 이는 병렬 실행되는 단계가 많을 경우 완료 순서와 다를 수 있어 예측 가능성을 높이기 위함임.
대상 단계: 롤백 핸들러가 등록된 모든 시작 또는 완료된 단계가 대상이 됨.
실행 순서: 단계가 시작된 순서의 역순으로 롤백 핸들러가 호출됨. (예: Step 1 -> Step 2 -> Step 3 순서로 시작했다면, 롤백은 Step 3 -> Step 2 -> Step 1 순서로 진행)
실패 처리: 만약 롤백 핸들러 실행 중에도 실패가 발생하면, 설정된 재시도 횟수를 초과할 경우 해당 롤백은 실패로 기록되고 워크플로우는 최종적으로 'Errored' 상태가 됨.
이러한 명확한 실행 순서 정의는 디버깅 용이성(Debugging Ease)을 높이고, 복잡한 분산 시스템에서 상태 추적(State Tracking)을 단순화함.
워크플로우 복구 메커니즘: 스테이트 및 스텁 관리
워크플로우 엔진이 재시작되거나 충돌할 경우, Cloudflare Workflows는 내구성 있는 기록(Durable Record)과 롤백 스텁(Rollback Stub)을 활용하여 롤백 상태를 복구함.
내구성 있는 기록: 각 단계의 시작 여부, 완료 여부, 반환 값, 롤백 핸들러 등록 여부 등 워크플로우 실행 상태를 영구적으로 저장함.
롤백 스텁: 런타임 중에 롤백 핸들러 함수에 대한 참조를 유지하며, 엔진 재시작 시 메모리에서 사라질 수 있음.
복구 과정 (Replay): 엔진이 재시작되면, Workflows는 저장된 기록을 바탕으로 워크플로우 코드를 재실행(Replay)함. 이때 완료된 순방향 단계는 재실행하지 않고 저장된 결과를 사용하며, 롤백이 필요한 단계에 대해서는 다시 롤백 스텁을 생성하여 롤백을 재개함.
이 메커니즘은 상태 유실 없이(Without State Loss) 롤백을 안정적으로 완료할 수 있도록 보장하며, 장애 복구 능력(Fault Tolerance)을 강화함.