Postgres만 사용하기: 내구성 워크플로 실행 재고

Durable 워크플로는 신뢰할 수 있는 프로그램을 구축하기 위한 강력한 도구입니다. 진행 상황을 데이터베이스에 정기적으로 체크포인트하면, 프로그램은 마지막으로 완료된 단계부터 다시 로드하여 충돌이나 실패에서 복구할 수 있습니다—마치 비디오 게임에서 진행 상황을 저장하는 것과 같습니다. 전통적으로 이는 외부 오케스트레이션을 통해 달성되었습니다. 여기서는 중앙 서버(Temporal, Airflow, AWS Step Functions 등)가 단계들을 조정하고 상태를 관리합니다.

하지만 외부 오케스트레이션이 근본적으로 과도하게 복잡하다는 주장이 점점 늘어나고 있습니다. 내구성 워크플로의 핵심 목적이 데이터베이스에 상태를 체크포인트하는 것이라면, 왜 별도의 오케스트레이터 서버를 도입해야 할까요? 데이터베이스 자체를 오케스트레이터로 사용하면 개발자는 스택을 단순화하고, 실패 지점을 줄이며, 기존 데이터베이스 전문 지식을 활용할 수 있습니다.

Postgres 기반 실행 아키텍처

Postgres 기반 시스템에서는 애플리케이션 서버가 데이터베이스와 직접 통신합니다. 중앙 오케스트레이터가 작업을 디스패치하는 대신, 프로세스는 다음과 같이 진행됩니다:

  1. Submission: 클라이언트가 Postgres 워크플로 테이블에 엔트리를 생성합니다.
  2. Execution: 애플리케이션 서버가 해당 테이블을 폴링하여 워크플로를 디큐하고 실행합니다.
  3. Checkpointing: 서버가 각 단계를 실행하면서 출력 결과를 직접 Postgres에 체크포인트합니다.
  4. Recovery: 서버가 충돌하면, 다른 서버가 마지막 체크포인트부터 워크플로를 복구할 수 있습니다.

이 설계는 중앙 코디네이터의 필요성을 없앱니다. 서버들은 SKIP LOCKED와 같은 잠금 메커니즘을 사용해 워크플로를 협력적으로 디큐하여 각 워크플로가 정확히 하나의 워커에 의해 처리되도록 보장합니다. 데이터베이스 무결성 제약은 여러 워커가 동시에 동일 워크플로를 실행하려 할 때 중복 작업을 방지합니다.

데이터베이스 중심 접근 방식의 주요 장점

확장성 및 가용성

오케스트레이션을 데이터베이스로 옮기면 시스템의 확장성이 데이터베이스 성능에 직접 연결됩니다. Postgres는 수만 개의 워크플로를 초당 처리하도록 수직 확장이 가능하고, 샤딩이나 CockroachDB와 같은 분산 버전을 통해 수평 확장도 구현할 수 있습니다. 워커가 교체 가능하기 때문에 데이터베이스가 온라인만 있으면 시스템은 계속 가용합니다.

내장 관측성

가장 큰 장점 중 하나는 워크플로 상태가 관계형 테이블에 저장된다는 점입니다. 이는 SQL을 통해 관측성이 "무료"가 됨을 의미합니다. 지난 한 달 동안 실패한 모든 워크플로를 식별하는 등 복잡한 분석 쿼리를 특수 관측 도구나 키‑값 스토어 스캔 없이 선언적으로 실행할 수 있습니다.

보안 표면 감소

외부 오케스트레이터는 새로운 실패 지점과 보안 위험을 도입합니다. 이들은 종종 민감한 애플리케이션 데이터를 다루기 때문입니다. Postgres를 사용하면 기존 핵심 인프라를 재사용하게 됩니다. 애플리케이션이 이미 Postgres에 의존하고 있다면, 내구성 실행을 추가해도 새로 강화·감사·보안해야 할 인프라가 생기지 않습니다.

커뮤니티 관점 및 트레이드오프

이점이 설득력 있지만, 개발자 커뮤니티는 전용 오케스트레이터를 포기할 때 고려해야 할 몇 가지 중요한 사항을 강조합니다.

"구축 vs 구매" 복잡성 함정

일부 엔지니어는 초기 설정은 간단하지만 복잡성이 결국 오케스트레이터에서 애플리케이션 로직으로 이동한다고 경고합니다. 시스템이 성장함에 따라 전용 엔진이 기본 제공하는 기능을 직접 구현하게 될 수 있습니다:

재시도, 백오프, 타임아웃, 취소, 버전 관리, 가시성, 작업 라우팅, 속도 제한, 임대, 하트비트, 워커 정체 감지, 재생/디버깅 의미론, 워크플로 마이그레이션, 팬아웃/팬인, 장기 타이머, 감사 로그, 운영자 도구 등을 필요로 하면, "데이터베이스만 사용" 이야기는 "워크플로 엔진의 열악한 복제본과 다수의 워커를 구축"하는 이야기로 변합니다.

정확성 및 멱등성

데이터 정확성에 대한 논쟁이 크게 존재합니다. 일부는 내구성 워크플로에 대한 단순 의사코드가 충돌 시 데이터 손상을 초래할 수 있다고 주장합니다. 흔히 제안되는 대안은 멱등 연산으로 워크플로를 구성하는 것으로, 시스템이 처음부터 다시 시작해도 이미 완료된 단계를 건너뛸 수 있게 합니다.

대규모 성능

Postgres는 다재다능하지만, 클라우드에서 고내구성 Postgres 인스턴스를 운영하는 비용이 비싸다는 의견도 있습니다. 또 다른 의견은 극한 규모에서는 디스크 로그 기반 아키텍처가 전통적인 데이터베이스 기반 시스템보다 비용 효율적일 수 있다는 것입니다.

대안 구현

DBOS 외에도 비슷한 철학을 구현하는 여러 도구와 패턴이 있습니다:

  • Absurd: Postgres용 내구성 워크플로의 최소 구현.
  • Oban: Postgres를 사용하는 Elixir용 인기 잡 처리 라이브러리.
  • Custom Rust Implementations: 다양한 팀이 Postgres 내에서 내구성 액터와 FSM(유한 상태 머신)을 처리하기 위한 최소 Rust 크레이트를 구축했습니다.
  • SQLite: 작은 워크로드의 경우, 일부 개발자는 LISTEN/NOTIFY 대신 간단한 폴링 루프를 사용해 이러한 개념을 SQLite에 포팅했습니다.

결론

오케스트레이션을 데이터베이스로 옮기는 것은 이미 Postgres 생태계에 깊이 투자한 팀에게 실용적인 선택입니다. 이는 아키텍처 오버헤드를 줄이고 관측성을 단순화합니다. 그러나 트레이드오프는 소유권의 이동입니다: 별도의 오케스트레이터 운영 복잡성을 포기하고, 정확성을 보장하고 고급 워크플로 기능을 수동으로 구현하는 개발 복잡성을 감수해야 합니다. 많은 경우, 모든 데이터를 한 곳에 보관하고 SQL로 모니터링할 수 있다는 점이 큰 장점이 되어 승리하는 선택이 됩니다.

Sources