DBOS와 내구성이 있는 워크플로 (Durable Workflows)의 비용 감소
요약
분산 시스템 환경에서 워크플로가 중간에 중단되는 문제를 해결하기 위한 '내구성이 있는 실행(Durable Execution)'의 중요성을 다룹니다. 기존의 복잡한 수동 구현 방식(Saga, Kafka 등)의 한계를 지적하며 DBOS와 같은 솔루션의 필요성을 설명합니다.
핵심 포인트
- 워크플로 중간 단계의 실패는 중복 결제나 데이터 불일치를 유발함
- 기존의 Saga 패턴이나 멱등성 키 기반 구현은 관리 복잡도가 매우 높음
- 팀마다 서로 다른 워크플로 관리 방식을 사용하여 운영 난이도가 상승함
- DBOS와 Temporal은 워크플로의 상태를 보존하여 내구성을 제공함
짜증 나는 실패는 중요한 일이 일어나기 전에 모든 것이 종료되는 실패가 아닙니다. 그런 경우는 깔끔합니다. 작업을 재시도하거나, 사용자가 다시 클릭하거나, 스케줄러가 다음 분에 실행하면 됩니다. 괜찮습니다.
진짜 실패는 3단계 이후에 프로세스가 종료되는 경우입니다.
1단계에서 고객 레코드를 생성했습니다. 2단계에서 무언가를 예약했습니다. 3단계에서 돈을 이체하거나, 구독을 활성화하거나, 문서를 보내거나, 혹은 당신이 요청한 일을 잊지 않을 외부 제공자 (External Provider)를 호출했습니다. 그러고 나서 워커 (Worker)가 사라집니다. Kubernetes가 포드 (Pod)를 축출합니다. VM이 재시작됩니다. 배포 (Deployment)가 프로세스를 종료합니다. 누군가는 그 "무상태 서비스 (Stateless Service)"가 아키텍처라기보다는 열망에 가까웠다는 사실을 깨닫습니다.
이제 어떻게 해야 할까요? 작업을 재시작하면 중복 결제가 발생할 수 있습니다. 건너뛰면 고객은 활성화 도중에 멈춰버립니다. 수동으로 복구한다면, 축하합니다. 당신은 불안감을 UI로 사용하는 운영 큐 (Operations Queue)를 발명한 것입니다.
이것이 바로 내구성이 있는 실행 (Durable Execution)이 지루하게 만들고자 하는 문제입니다.
쉬운 것이 아니라, 지루하게 만드는 것입니다.
우리는 예전에 이것을 직접 구축했습니다
핀테크 (Fintech) 분야에서 이러한 문제는 도처에서 나타납니다. 워크플로 (Workflows)가 요청 (Request)보다 길고, 해피 패스 (Happy Path) 다이어그램보다 더 취약하기 때문입니다.
미주 지역의 한 핀테크 기업에서 내놓은 답은 많은 진지한 백엔드 팀들이 도달했던 것과 같았습니다: 사가 (Sagas), Kafka, 멱등성 키 (Idempotency Keys), 상태 테이블 (State Tables), 재시도 (Retries), 조정 작업 (Reconciliation Jobs), 그리고 규율입니다.
그 아키텍처는 어리석은 것이 아니었습니다. 그 시대에는 적절했습니다.
프로세스를 단계별로 나눕니다. 각 단계는 이벤트를 발행하거나 상태를 기록합니다. 명령 (Commands)은 멱등성 키를 포함합니다. 외부 호출은 가능한 경우 경계에서 중복 제거 (Deduplication)를 수행합니다. 시스템에 이상한 상태가 발생할 것임을 알기에, 나중에 조정 작업 (Reconciliation Jobs)이 이상한 상태를 스캔합니다. 모두가 "최소 한 번 (At least once)"는 전달 보장이 아니라, 성격 테스트라는 것을 배우게 됩니다.
어려운 점은 결코 사가 (Saga)를 작성하는 것만이 아니었습니다. 어려운 점은 그것을 책임지는 것이었습니다.
무엇이 상태(state)를 의미하는지, 어떤 실패가 일시 중단되어야 하는지, 어떤 실패가 보상(compensate)되어야 하는지, 그리고 어떤 실패가 사람에게 알림(page)을 보내야 하는지를 결정해야 했습니다. 서비스 경계를 가로질러 멱등성 키(idempotency keys)가 계속 흐르도록 유지해야 했습니다. 코드상에서 원자적(atomic)으로 보이는 단계가 비즈니스 측면에서도 원자적으로 작동하도록 보장해야 했습니다.
그리고 이 메커니즘을 직접 구현(hand-rolled)했기 때문에, 결국 모든 팀이 동일한 기계 장치의 각기 다른 버전을 소유하게 되었습니다.
어느 팀은 오케스트레이션 테이블(orchestration table)을 가졌고, 다른 팀은 Kafka 토픽을 통한 코레오그래피(choreography)를 사용했습니다. 또 다른 팀은 절반만 완료된 행(row)을 복구하는 cron 작업을 가졌습니다. 이 모든 것들은 잘 작동하다가, 어느 순간 작동하지 않게 되었고, 그때가 되면 조직의 기억은 이전에 고생했던 사람들의 머릿속에만 남게 되었습니다.
이것이 바로 DBOS와 Temporal이 저에게 흥미롭게 다가오는 이유입니다. 그들은 워크플로(workflow)가 충돌(crash) 후에도 생존해야 한다는 아이디어를 발명한 것이 아닙니다. 그들은 우리가 예전에 직접 재구축해야 했던 컨트롤 플레인(control-plane) 부분을 제품화(productize)했습니다.
DBOS가 변화시키는 것
저는 현재 걸프 지역의 한 은행에서 구독 활성화 워크플로와 예약된 스윕(scheduled sweeps) 작업을 살펴보던 중 처음으로 DBOS에 주목하게 되었습니다. 저장 모델(storage model)이 제 눈길을 끌었습니다.
DBOS Transact는 Postgres에 완료된 단계를 체크포인트(checkpointing)함으로써 워크플로를 내구성 있게(durable) 만듭니다. 프로세스가 충돌하더라도, 재시작된 앱은 마지막으로 완료된 단계부터 재개할 수 있습니다.
또한 도입 방식도 변화시킵니다. DBOS는 내구성(durability)을 확보하기 전에 모든 팀이 별도의 워크플로 클러스터를 운영하도록 요구하지 않습니다. 오픈 소스 Transact 라이브러리는 Postgres 호환 저장소 위에 구축된, 애플리케이션 인접형 내구성 있는 실행(application-adjacent durable execution) 모델로 포지셔닝되어 있습니다. 그들의 자료에 따르면 상태(state)는 여전히 여러분의 애플리케이션 데이터베이스에 존재할 수 있습니다. DBOS는 또한 사용자, 애플리케이션 및 체크포인트 볼륨을 기준으로 가격이 책정된 유료 플랜과 함께 컨트롤 플레인(control-plane) 레이어를 판매합니다.
이것이 중요한 이유는 워크플로 엔진에 대한 첫 번째 반대 의견이 대개 운영상의 부담(operational weight)이기 때문입니다. DBOS는 모든 서비스를 연구 대상으로 만들지 않으면서도 충돌 복구(crash recovery), 내구성 있는 큐(durable queues), 예약된 작업(scheduled work), 그리고 관측성(observability)을 원하는 영역을 목표로 하고 있습니다.
트레이드오프(tradeoff)는 명확합니다. 만약 내구성 있는 실행(durable execution) 메타데이터가 Postgres에 있다면, Postgres는 런타임(runtime)의 일부가 됩니다. 이는 기능이 될 수도 있고 결합 지점(coupling point)이 될 수도 있습니다. 소유권과 쿼리 가능성(queryability)은 단순해지지만, 데이터 보존(retention), 마이그레이션(migrations), 백업(backups), 그리고 애플리케이션 데이터 근처에 워크플로 히스토리를 배치함으로써 발생하는 영향 범위(blast radius)를 여전히 이해해야 합니다.
세상에 공짜 점심은 없습니다. 움직이는 부품이 적어지는 것뿐입니다.
Temporal이 제공하는 것
Temporal은 형태가 다릅니다.
Temporal은 워크플로가 이벤트 히스토리(event histories)로 기록되는 내구성 있는 실행 플랫폼을 제공합니다. 워크플로는 몇 초, 며칠, 또는 몇 년 동안 실행될 수 있으며, 서비스는 복구 및 지속에 필요한 이벤트를 영구적으로 저장합니다. 액티비티(Activities)는 결정론적(deterministic)인 워크플로 로직 외부에서 실행됩니다. SDK는 상태를 재구축하기 위해 워크플로 히스토리를 리플레이(replay)합니다.
이 모델은 워크플로가 수명이 길고, 여러 서비스를 가로지르며, 시그널(signals), 타이머(timers), 재시도(retries), 버전 관리(versioning), 그리고 강력한 운영 표면(operational surface)이 필요할 때 매우 강력합니다.
Temporal Cloud의 가격 책정 방식 또한 제품의 형태를 명확히 보여줍니다. 이는 액션(Actions), 활성 스토리지(active storage), 보존 스토리지(retained storage), 그리고 지원(support)을 기준으로 가격이 책정되는 관리형 플랫폼입니다. 여러분은 내구성 있는 실행 제어 평면(control plane)을 구매하는 것입니다.
이는 많은 팀에게 합리적인 거래입니다. 또한 이는 실제적인 플랫폼 결정이기도 합니다. 일단 Temporal이 프로세스의 중심에 들어오면, 누군가는 네임스페이스(namespaces), 워커(workers), 태스크 큐(task queues), 재시도 정책(retry policies), 버전 관리(versioning), 보안(security), 그리고 비용을 책임져야 합니다.
어려운 부분은 여전히 여러분의 몫입니다
내구성 있는 실행이 흥미롭게 다가올 때 사람들이 종종 간과하는 부분이 여기 있습니다.
프레임워크는 3단계가 완료되었다는 사실을 기억할 수 있습니다. 하지만 3단계가 적절한 경계(boundary)였는지는 말해주지 못합니다.
만약 3단계가 결제 제공업체를 호출했다면, 해당 제공업체가 멱등성 키(idempotency key)를 수락했나요? 네트워크 타임아웃(network timeout)이 발생했다면, 결제가 이루어졌나요, 아니면 되지 않았나요? 워크플로가 내일 재개된다면, 재시도해야 할까요, 제공업체에 쿼리해야 할까요, 고객에게 보상해야 할까요, 아니면 수동 검토를 위해 중단해야 할까요?
내구성 있는 실행은 이러한 질문들을 가시화(visible)해 줍니다. 하지만 질문에 대한 답을 대신 해주지는 않습니다.
그러한 가시성(visibility)은 유용합니다. 직접 구현한(hand-rolled) 시스템에서는 단계의 경계(step boundaries)가 토픽 이름, 재시도 루프(retry loops), 데이터베이스 행(database rows), 그리고 암묵적인 지식(tribal knowledge) 사이로 흘러넘치는 경우가 많습니다. 하지만 DBOS나 Temporal에서는 워크플로의 형태를 명시적으로 만들 수밖에 없습니다. 단계를 명명해야 합니다. 무엇을 체크포인트(checkpoint)로 저장할지, 무엇이 재실행(replay)하기에 안전한지, 그리고 무엇이 절대 재실행되어서는 안 되는지를 결정해야 합니다.
그것이 진정한 설계 작업입니다.
"어떻게 프로세스를 영원히 살아있게 유지할 것인가?"가 아닙니다. 질문은 "이 비즈니스 액션이 발생했다는 것이 무엇을 의미하는가?"가 되어야 합니다.
핀테크 엔지니어들이 재시도(retry)에 대해 민감하게 반응하는 이유가 바로 이것입니다. 돈이 개입될 때 재시도는 기술적인 동사가 아닙니다. 그것은 "HTTP 호출을 다시 시도하라"는 뜻일 수도 있지만, "두 번 결제하라"는 뜻이 될 수도 있습니다.
경계(boundary)가 중요합니다.
소유권(ownership)도 마찬가지입니다. 결제, 구독, 알림, 그리고 컴플라이언스(compliance)를 가로지르는 내구성 있는 워크플로는 프레임워크의 소유가 아닙니다. 그것은 팀의 소유이거나, 혹은 아무의 소유도 아닙니다.
내구성이 구매 가능한 속성이 되다
더 넓은 패턴이 흥미로운 지점입니다.
백엔드 엔지니어링의 역사는 지루한 속성들이 제품(product)이 되어온 역사와 같습니다. 우리는 과거에 배포 스크립트(deployment scripts)를 직접 만들었습니다. 그러다 배포는 플랫폼의 속성이 되었습니다. 우리는 메트릭 파이프라인(metrics pipelines)을 직접 구현했습니다. 그러다 관측성(observability)은 하나의 제품 카테고리가 되었습니다. 어떤 팀들은 여전히 타당한 이유로 이러한 것들을 직접 구축하기도 합니다. 하지만 기본값(default)이 바뀌었습니다.
내구성 있는 실행(Durable execution)도 동일한 변화를 겪고 있습니다.
"이 다단계 작업은 충돌(crash) 상황에서도 생존한다"는 것은 과거에는 모든 진지한 백엔드 시스템 내부에 숨겨져 있던 엔지니어링 성숙도의 상징이었습니다. 이제 이것은 점점 더 인프라의 속성이 되어가고 있습니다. 라이브러리를 설치하거나, 플랫폼을 실행하거나, 관리형 서비스(managed service)에 비용을 지불할 수 있습니다.
이는 좋은 현상입니다. 더 적은 수의 팀이 제1원리(first principles)로부터 실패 모드(failure modes)를 재발견할 필요가 없어졌기 때문입니다. 더 많은 워크플로가 막연한 느낌(vibes) 대신 명시적인 상태(explicit state)를 가질 수 있습니다. 예약된 스윕(scheduled sweeps), 온보딩 흐름(onboarding flows), 구독 활성화, 환불, 신원 확인, 그리고 정리 작업(cleanup jobs)들이 프로세스는 오직 편리한 시간에만 종료된다는 환상에 의존하는 것을 멈출 수 있습니다.
하지만 이는 엔지니어링적 판단(engineering judgment)에 대한 기준 또한 높이고 있습니다.
내구성 (durability)을 추가하기가 쉽다면, 팀들은 단순하게 유지되어야 했을 것들에 그것을 추가할 것입니다. 모든 백그라운드 작업 (background job)이 워크플로 (workflow)가 된다면, 신뢰할 수 있지만 추론하기 어려운 (hard to reason about) 시스템을 만들게 될 수도 있습니다.
질문은 "모든 것에 DBOS나 Temporal을 사용해야 하는가?"가 아닙니다.
질문은 "어떤 비즈니스 프로세스가 내구성 있는 실행 (durable execution)을 누릴 가치가 있으며, 프로세스가 재개될 때 그 의미론 (semantics)을 누가 소유하는가?"여야 합니다.
이는 "cron 재시도 (retries)를 몇 번이나 추가해야 하는가?"라는 질문보다 더 나은 질문입니다.
그리고 바로 그 지점이 우리가 집중해야 할 작업입니다.
references
- DBOS, Postgres-backed Durable Workflow Execution
- DBOS pricing
- Temporal, Durable Execution Solutions
- Temporal Cloud pricing documentation
제 프로젝트를 테스트하기 위해 저는 Railway를 사용합니다. 시작할 때 20달러(USD)를 받고 싶다면, 이 링크를 사용하세요.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기