Waymark: Python을 위한 내구성 있는 워크플로우 엔진

개요

Waymark는 코드를 재실행(replay)하는 대신 중간 표현(intermediate representation)으로 컴파일하여 내구성 있는 백그라운드 워크플로우를 오케스트레이션하도록 설계된 라이브러리입니다. Rust 기반 런타임과 기본 저장소로 Postgres를 활용함으로써, 개발자는 별도의 복잡한 인프라 스택 없이도 서버 재시작, 충돌, 장기 실행 작업에서 살아남는 표준 비동기 Python 코드를 작성할 수 있습니다.

한 번 컴파일하는 아키텍처(Compile-Once Architecture)

"재실행" 메커니즘에 의존하는 Temporal이나 Vercel Workflow와 같은 기존의 내구성 있는 실행 엔진과 달리, Waymark는 한 번 컴파일하는 방식을 사용합니다. 재실행 기반 시스템에서는 워크플로우 코드가 각 단계마다 처음부터 다시 실행되므로, 사용자는 워크플로우 로직 내에서 random()이나 datetime.now()와 같은 비결정적 함수를 피하고 엄격하게 결정적인 코드를 작성해야 합니다.

Waymark는 Python run() 메서드의 추상 구문 트리(AST)를 중간 표현(IR)으로 파싱하여 이를 변경합니다. 이 IR은 사용자 지정 내구성 가상 머신을 위한 바이트코드로 변환됩니다. 런타임이 Postgres에서 이 가상 머신의 상태를 추적하기 때문에, 복구 중에 워크플로우를 처음부터 다시 실행할 필요가 없습니다. 이를 통해 개발자는 루프, 조건문, 병렬 분기를 포함한 표준 Python 제어 흐름을 결정론에 대한 제약 없이 사용할 수 있습니다.

핵심 개념

Waymark는 분산 작업을 관리하기 위해 두 가지 주요 기본 요소를 구분합니다.

  • Actions: 네트워크 호출, 데이터베이스 쿼리 또는 실패할 수 있고 독립적인 재시도 작업이 필요한 모든 작업과 같은 분산 작업 단위를 나타냅니다. Actions는 노드에 의해 실행되며 특정 재시도 정책 및 타임아웃으로 구성할 수 있습니다.
  • Workflows: 내구성 있는 제어 흐름 계층 역할을 합니다. Actions를 오케스트레이션하지만 자체적으로 무거운 작업을 수행하지는 않습니다. 병렬 팬아웃을 위한 asyncio.gather와 프로세스 재시작 전반에 걸쳐 지속되는 내구성 있는 일시 중지를 위한 asyncio.sleep과 같은 표준 Python 패턴을 지원합니다.

배포 및 사용

Waymark는 Postgres 데이터베이스만 있으면 되는 "인프라 경량화"를 지향합니다. 이 시스템에는 워크플로우 인스턴스, 호출 타임라인, 노드 부하에 대한 가시성을 제공하는 waymark-executor를 통해 액세스할 수 있는 내장 웹 애플리케이션이 포함되어 있습니다.

현재 이 프로젝트는 초기 개발 단계에 있으며 프로덕션 환경에서의 사용은 권장되지 않지만, 백그라운드 작업 스택을 단순화하여 제품-시장 적합성(product-market fit)을 달성하려는 팀을 지원하는 것을 목표로 합니다. 100ms 미만의 지연 시간이나 대규모 동시성(예: 1만 개 이상의 동시 작업)이 필요한 고규모 환경을 위한 것은 아닙니다.

커뮤니티 관점

Waymark의 도입은 설계의 장단점에 대한 논의를 불러일으켰습니다. 일부 사용자는 워크플로우 엔진과 애플리케이션 코드 간의 긴밀한 결합 가능성을 지적하며, Python 버전이나 타사 종속성이 발전함에 따라 장기적인 유지 관리성에 대한 우려를 표명했습니다.

다른 사용자들은 이를 Bazel이나 Nix와 같은 빌드 시스템에 비유하며 아키텍처의 변화를 강조했습니다. 한 댓글 작성자는 다음과 같이 언급했습니다.

"Nix와 Bazel에서 사용되는 '샌드박스화된 명령형 구성 요소를 연결하는 순수 조정 언어'라는 아키텍처 패턴이 분산 워크플로우에 적용된 것을 이제야 보게 된 것이 놀랍습니다." — @handle

또한, 일부 개발자들은 이 라이브러리가 asyncio에 크게 의존하고 있다는 점을 지적하며, 주로 동기식 코드베이스로 작업하는 팀에게는 부담스러울 수 있다고 언급했습니다.

Sources