Cloudflare Workflows 엔진에 사가(Saga) 패턴 롤백 기능을 추가한 과정을 다룬다. 다단계 워크플로우에서 중간 단계가 실패했을 때 이전 단계의 부작용을 되돌리는 보상 로직(compensating action)을 각 step에 선언적으로 정의할 수 있게 됐다. Workers RPC의 callable reference를 활용해 엔진 재시작 후에도 핸들러를 복구할 수 있는 내구적 설계를 택했다.
핵심 포인트- step.do() 세 번째 인자에 rollback 함수를 선언하는 메타데이터 방식 채택 — 유창한 인터페이스·빌더 패턴은 Promise pipeline 모호성·silent bug 위험으로 탈락
- 롤백 핸들러는 Workers RPC callable reference로 직렬화 — 엔진 재시작·장애 후에도 복구 가능한 내구성 보장
- 실행 순서는 단계 완료 역순이 아닌 단계 시작 역순 — 부분 완료 시나리오에서 일관성 확보
- 롤백 핸들러도 일반 step과 동일한 재시도·타임아웃 설정 적용, 멱등성 전제
- 현재는 순차 롤백만 지원, 향후 waitForEvent·병렬 롤백·Python Workflows 지원 로드맵
상세 정리- 배경: 은행 A 출금 후 은행 B 입금 실패 시 수동 try/catch 보상 로직이 복잡하고 누락 실수가 빈번 — 플랫폼 레벨 지원 필요성 대두
- 기존 방식: debitA/creditB 변수를 추적하며 catch 블록에서 조건부로 역거래 수동 구현 — 코드 분산, 단계 추가 시 누락 위험
- 새 API: step.do("debit", action, { rollback: async ({ output }) => bankA.credit(...) }) — 선언 시점에 보상 로직을 단계와 함께 정의
- API 설계 1차 후보 — 유창한 인터페이스(step.do().rollback()): Promise 파이프라이닝과 충돌하며 단계 실행 타이밍이 모호해져 탈락
- API 설계 2차 후보 — 빌더 패턴(step.saga().do().rollback().run()): 불필요한 보일러플레이트, .run() 누락 시 아무 일도 안 일어나는 silent bug 위험으로 탈락
- 최종 선택 — 메타데이터 방식: 기존 step.do() API와 완전 호환, 재시작 후 핸들러 복구 가능, 명시적
- 내부 구조 1 — Durable Step History: 각 단계의 실행 여부, 완료 여부, 반환값, 롤백 핸들러 등록 여부를 내구적으로 기록
- 내부 구조 2 — Rollback Handler Stub: Workers RPC callable reference로 핸들러를 직렬화 저장, 재생(replay) 모드에서도 복구
- 재생 모드: 재시작 시 완료된 단계는 실제 부작용을 재실행하지 않고 저장된 결과를 읽어 재구성 — 핸들러 스텁도 동일하게 복구
- 적격 단계: 이미 시작됐거나 완료된 단계(롤백 핸들러 등록 조건), 실패한 단계도 핸들러 있으면 포함
- 롤백 실패 시: 해당 핸들러가 재시도 한도 소진하면 나머지 핸들러 실행 중단, 워크플로우는 실패 상태
- 현재 한계: waitForEvent 단계는 롤백 미지원, 병렬 실행 없이 순차만 가능
왜 읽나Cloudflare Workers 기반 Workflows를 쓰거나 분산 시스템에서 사가 패턴을 직접 구현할 백엔드·인프라 엔지니어에게 플랫폼 레벨 사가 설계의 결정 과정과 트레이드오프 레퍼런스.