Skip to content

ADR-04: 큐 기반 비동기 처리

🇺🇸 English Version

날짜작성자리포지토리
2024-12-17@KubrickCodeweb, collector

배경

장시간 실행 작업의 본질

연산 분석을 수행하는 시스템은 근본적인 도전에 직면함: 처리 시간이 크게 변동하며 사전 예측이 불가능함. 이는 빠른 응답에 대한 사용자 기대와 실제 분석 소요 시간 사이의 충돌을 야기함.

이러한 워크로드의 핵심 특성:

특성설명
예측 불가능한 소요시간입력 크기에 따라 수 초에서 수 분까지 변동
높은 리소스 소비CPU, 메모리, I/O 집약적 작업
사용자 기대치작업 크기와 무관하게 빠른 확인(<1초)
장애 모드네트워크 문제, 메모리 부족, 타임아웃 시나리오

HTTP 프로토콜의 한계

표준 HTTP 상호작용은 실용적 제약을 부과함:

  • 브라우저 타임아웃: 대부분의 브라우저는 30-60초 후 연결 해제
  • 로드밸런서 제한: 인프라에서 일반적으로 60초 타임아웃 강제
  • 연결 관리: 장시간 유지되는 연결은 비효율적으로 리소스 소비
  • 사용자 경험: 동기 요청 중 사용자가 다른 페이지로 이동 불가

핵심 질문

수 초에서 수 분이 소요될 수 있는 작업을 시작하는 요청에서, 요청 수락과 결과 전달 사이의 통신을 어떻게 처리해야 하는가?

결정

장시간 실행 작업에 큐 기반 비동기 처리 채택

River를 선택한 이유:

  • 폴링 문제: Asynq는 지속적인 Redis 폴링 필요, 지연시간 및 리소스 사용량 증가
  • 트랜잭션 일관성: River는 PostgreSQL 사용, 동일 DB 트랜잭션 내 작업 enqueue 가능
  • 운영 단순성: 데이터와 큐를 단일 PostgreSQL 인스턴스로 통합 (별도 Redis 불필요)
  • 내구성: ACID 보장을 갖춘 PostgreSQL 기반 큐

패턴은 다음 흐름을 따름:

사용자 → API (요청 수락) → 큐 → 워커 (처리) → 데이터베이스
                ↓                                    ↓
           작업 ID 반환                         결과 저장
                ↓                                    ↓
           사용자 상태 조회 ←─────────────────────────┘

핵심 원칙:

  1. 즉각적 확인: API가 밀리초 내에 작업 식별자 반환
  2. 백그라운드 처리: 워커가 자체 속도로 큐에서 작업 소비
  3. 상태 가시성: 사용자가 블로킹 없이 진행 상황 확인 가능
  4. 재시도 기능: 실패한 작업이 백오프와 함께 자동 재시도

검토한 옵션

옵션 A: 큐 기반 비동기 처리 (선택)

작동 방식:

  1. API가 요청 수신, 입력 검증, 작업 레코드 생성
  2. 작업이 메타데이터(작업 ID, 파라미터)와 함께 큐에 등록
  3. API가 작업 ID와 함께 HTTP 202 Accepted 반환
  4. 워커가 큐에서 작업을 가져와 처리, 데이터베이스 업데이트
  5. 사용자가 상태 엔드포인트를 폴링하거나 알림 수신

장점:

  • 처리 시간과 무관하게 즉각적인 사용자 피드백
  • API와 워커 컴포넌트의 독립적 스케일링
  • 장애 격리: 워커 장애가 API를 다운시키지 않음
  • 지수 백오프를 포함한 내장 재시도 메커니즘
  • 복구 불가 장애를 위한 Dead Letter Queue (DLQ)
  • 백프레셔 처리: 큐가 트래픽 급증을 버퍼링

단점:

  • 추가 인프라: 메시지 큐 시스템 필요
  • 운영 복잡도: 모니터링할 컴포넌트 증가
  • 최종적 일관성: 결과가 즉시 사용 불가
  • 폴링 오버헤드 또는 실시간 연결의 복잡성

옵션 B: 동기 처리

작동 방식:

사용자 → API → 처리 (블로킹) → 응답
         └────── 30+ 초 ──────┘

장점:

  • 단순한 구현: 단일 요청-응답 사이클
  • 추가 인프라 불필요
  • 성공 시 즉각적인 결과 전달
  • 디버깅 용이: 단일 실행 경로

단점:

  • 장시간 작업에서 HTTP 타임아웃 실패
  • 리소스 경쟁: 처리가 API 스레드 블로킹
  • 저조한 사용자 경험: 대기 중 피드백 없음
  • 연쇄 장애: 메모리 부족이 전체 서비스에 영향
  • 재시도 기능 없음: 사용자가 수동으로 재시도 필요
  • 처리를 독립적으로 스케일링 불가

옵션 C: 웹훅 콜백

작동 방식:

  1. 사용자가 콜백 URL과 함께 작업 제출
  2. API가 수락을 반환하고 처리 시작
  3. 완료 시 시스템이 콜백 URL로 결과 POST
  4. 사용자 서버가 알림 수신

장점:

  • 완료 시 실시간 알림
  • 폴링 불필요
  • 이벤트 기반 아키텍처와 정렬
  • 상태 확인으로 인한 API 부하 감소

단점:

  • 사용자가 콜백 엔드포인트 제공 및 유지 필요
  • 전달 신뢰성 우려: 콜백을 위한 재시도, DLQ
  • 보안 복잡도: URL 검증, HMAC 서명
  • 최종 사용자 대면 애플리케이션에 부적합
  • 소비자에게 높은 통합 장벽

결과

긍정적

사용자 경험

지표동기 처리비동기 처리
초기 응답 시간30+ 초<500ms
이탈률40-60%10-20%
에러율 (타임아웃)작업에 따라 변동거의 0
진행 상황 가시성없음전체 상태

시스템 안정성

  • 장애 격리: 워커 메모리 부족이 API 서비스를 다운시키지 않음
  • 우아한 성능 저하: 큐가 다운스트림 장애 중 요청 버퍼링
  • 자동 복구: 일시적 장애가 사용자 개입 없이 재시도
  • 관측성: 큐 깊이가 명확한 상태 신호 제공

확장성

  • 큐 깊이를 기반으로 워커 독립 스케일링
  • 요청 빈도를 기반으로 API 스케일링
  • 큐 버퍼링으로 트래픽 급증 처리
  • 리소스 최적화: 워커에 고메모리, API에 저지연

부정적

운영 오버헤드

  • 큐 시스템이 핵심 인프라가 됨
  • 모니터링 필요: 큐 깊이, 처리 지연시간, 장애율
  • 유지보수할 다중 배포 파이프라인
  • 환경 설정 동기화 필요

복잡도

  • 분산 시스템 디버깅 필요
  • 사용자에게 전달할 최종적 일관성 모델
  • 추가 장애 모드: 큐 불가용, 메시지 손실
  • 컴포넌트 간 상태 동기화

기술적 함의

측면함의
큐 선택트랜잭션 일관성과 운영 단순성을 위한 PostgreSQL 기반 River
재시도 전략지터가 포함된 지수 백오프; 일시적 vs 영구적 장애 분류
DLQ 처리수동 검사 및 재실행 기능 필요
모니터링큐 깊이, 처리 시간, 장애율 대시보드
멱등성워커가 중복 작업 전달을 안전하게 처리해야 함

에러 분류 전략

에러 유형재시도 동작예시
일시적지수 백오프네트워크 타임아웃, 임시 DB 장애
비일시적즉시 DLQ로 이동잘못된 입력, 파싱 에러
리소스 제한더 긴 대기 후 백오프레이트 리밋, 메모리 압박

사용자 커뮤니케이션 패턴

  1. 제출: 예상 시간과 함께 작업 ID 반환
  2. 진행 중: 현재 단계와 퍼센티지 표시
  3. 완료: 결과 또는 에러 상세 제공
  4. 실패: 재시도 옵션과 함께 명확한 설명

Open-source test coverage insights