콘텐츠로 이동

ADR-0015: asynq vs Temporal Split

Umbra 는 두 개의 비동기 처리 엔진을 사용한다: 짧은 비동기 작업은 asynq, 장기 실행 워크플로우는 Temporal. 둘의 역할 경계를 명확히 분리한다.

Status

Accepted

  • Decided at — 2026-04-13
  • Decided by — Pablo

Context

Umbra 는 다양한 비동기 작업이 존재한다.

  • 짧은 작업 — 결제 cron 트리거, Toss API 호출, 알림 발송, Live Sync 배치, 빌링키 발급 후속 처리
  • 장기 워크플로우 — Restore 복구, AntiNuke 감지 및 대응

처음에는 "하나의 도구로 통일" 유혹이 있다. asynq 만으로 workflow 까지 구현하거나, Temporal 로 cron 까지 실행하는 방식.

현실 관찰:

  • asynq 로 장기 워크플로우 구현 → 자체 상태 관리, retry, signal 을 전부 수동. Temporal 재구현.
  • Temporal 로 모든 비동기 처리 → Temporal 워크플로우 규칙(결정론, Activity 분리) 부담이 결제 cron 같은 단순 작업에 오버킬

즉 도구의 강점이 작업 특성에 따라 다르다.

Decision

두 도구를 역할에 따라 분리 하여 사용한다.

asynq 의 책임

  • 결제 cron (매 정각 결제 대상 조회 및 큐잉)
  • 결제 재시도 (24h/48h/72h delayed task)
  • Live Sync 배치 처리
  • 알림 발송 (Discord DM, 채널 알림)
  • Toss 웹훅 후속 처리
  • Snapshot 자동 생성 스케줄 (일별/주별)
  • Outbox 이벤트 archive cron

Temporal 의 책임

  • Restore workflow (복구 실행)
  • AntiNuke workflow (이상 감지 및 대응)

구분 기준

작업을 Temporal vs asynq 로 분류하는 결정 트리:

  1. 실행 시간이 5분 이상 예상되는가? → Temporal
  2. 프로세스 장애 시 중간에서 재개 가 필요한가? → Temporal
  3. 여러 단계가 signal 로 조정 되어야 하는가? → Temporal
  4. 위 모두 아니라면 → asynq

선택 근거:

  • 도구별 강점 활용 — asynq 는 단순 큐잉에 최적, Temporal 은 복잡 워크플로우에 최적
  • 학습 부담 최소화 — 단순 작업까지 Temporal 을 쓰면 워크플로우 결정론 규칙 부담이 커짐
  • 인프라 재활용 — asynq 는 Redis(이미 사용 중) 백엔드, Temporal 은 Postgres(이미 사용 중) persistence

Consequences

Positive

  • 각 도구의 강점을 활용해 작업별 최적 경험
  • 간단한 작업(결제 cron)은 간단한 코드로 구현
  • 복잡한 워크플로우(Restore)는 Temporal 의 복원력 활용
  • 개발자가 작업 복잡도에 맞는 도구를 선택 가능

Negative

  • 두 도구 학습 필요
  • 운영 대시보드 분리 (asynq web UI + Temporal Web UI)
  • "언제 무엇을 써야 하지" 판단 필요 (구분 기준으로 완화)
  • 같은 worker 프로세스 안에서 두 종류 워커가 공존 → 그레이스풀 셧다운 순서 주의

Neutral

  • Outbox poller 는 둘 다 아닌 별도 고루틴 (ADR-0016)
  • 둘 다 Worker 프로세스(apps/worker/) 안에서 실행

Workload assignment rationale

Workload Tool Why
정기 결제 cron asynq 단순 스케줄 + 짧은 실행
결제 실패 재시도 asynq delayed task 로 24h 뒤 실행
Live Sync 배치 asynq 주기적 배치, 짧은 실행
Discord DM 발송 asynq 단일 API 호출
Toss 웹훅 후속 처리 asynq 웹훅 응답 후 비동기 처리
Snapshot 자동 생성 asynq cron 스케줄
Outbox archive asynq nightly cron
Restore workflow Temporal 장기 실행, 다단계, 재시작
AntiNuke workflow Temporal 장기 실행, signal 기반

Alternatives considered

Alternative 1: asynq 만 (Temporal 제외)

Pros

  • 인프라 단순 (Temporal 서버 없음)
  • 학습 부담 ↓

Cons

  • Restore 같은 장기 워크플로우의 장애 복원을 수동 구현
  • 결과적으로 Temporal 의 부분 재구현
  • 복구는 유료 기능 핵심이라 신뢰성 결정적

Why rejected — Recovery 의 신뢰성 요구가 Temporal 도입 비용을 정당화.

Alternative 2: Temporal 만 (asynq 제외)

Pros

  • 도구 통일

Cons

  • 결제 cron 같은 단순 작업에 결정론 규칙 부담
  • 수십 줄 간단한 작업이 Activity 분리로 복잡도 ↑
  • Temporal 인프라를 모든 작업의 경로에 배치 → 단일 장애 지점 확장

Why rejected — 짧은 작업에 Temporal 은 과함. asynq 의 경량 실행이 더 적합.

Alternative 3: River (PostgreSQL 기반 큐)

Pros

  • PostgreSQL 만으로 큐 가능 (Redis 제거 가능)
  • 트랜잭션 일관성 우수

Cons

  • Redis 가 이미 세션/idempotency 용도로 필수 → 제거 불가
  • asynq 대비 커뮤니티 덜 성숙
  • 추가 이점 없이 도구 교체 비용

Why rejected — Redis 는 다른 용도로 유지되므로 asynq 의 Redis 백엔드가 자연스러움. River 전환 시 별 이점 없음.

Alternative 4: Cloud Tasks / Pub/Sub

Pros

  • 관리형

Cons

  • AWS/GCP 종속
  • Fly.io 와의 통합 복잡
  • 지연 작업(delayed task) 지원 약함

Why rejected — 현재 인프라 선택과 맞지 않음.

Compliance

  • Restore, AntiNuke 외 작업은 Temporal 로 만들지 않음 (코드 리뷰에서 확인)
  • asynq task 는 짧은 실행(수 초~수 분) 기준 유지, 초과 시 Temporal 이전 검토
  • Worker 프로세스의 그레이스풀 셧다운은 asynq → Temporal → Outbox poller 순 중단
  • 작업 추가 시 구분 기준(결정 트리)을 팀 리뷰에서 적용

Revisit triggers

  • asynq task 가 장기 실행으로 늘어나 Temporal 적합성이 커지면 이전 검토
  • Temporal 만 사용해도 되는 규모(팀/복잡도)면 도구 통일 재평가
  • River 가 의미 있게 성숙하여 Redis 이점을 압도하면 asynq → River 검토

References

  • ADR-0007 — Redis 선택 (asynq 백엔드)
  • ADR-0014 — Temporal 채택
  • ADR-0016 — Outbox poller (두 도구 외 별도 고루틴)
  • ADR-0017 — Worker 프로세스에 두 워커 공존
  • ADR-0022 — 결제 cron (asynq 작업)
  • ADR-0023 — Live Sync (asynq 작업)