Skip to content

ADR-16: 멀티큐 우선순위 라우팅 아키텍처

🇬🇧 English Version

날짜작성자관련 레포
2026-01-19@KubrickCodeweb, worker, infra

컨텍스트

비즈니스 문제

Specvital 플랫폼은 ADR-13에 따라 테스트 분석 서비스 수익화를 위한 티어 요금제 (Free, Pro, Pro Plus, Enterprise) 도입. 그러나 기존 큐 인프라는 모든 요청을 동등하게 처리하여 세 가지 문제 발생:

문제비즈니스 영향
우선순위 미구분피크 시간대 유료 고객이 무료 티어 뒤에서 대기
스케줄러 경합백그라운드 재분석 작업과 사용자 요청이 동일 큐 공유
워커 라우팅 오류Analyzer와 SpecGenerator 워커가 잘못된 작업 타입 수신

기술적 발전 과정

큐 아키텍처의 세 단계 발전:

Phase 1: 단일 공유 큐

모든 요청 → 단일 FIFO 큐 → 모든 워커

결과: 워커가 호환되지 않는 작업 타입 수신 시 "Job kind not registered" 오류 발생.

Phase 2: 서비스별 전용 큐

Analysis 요청 → analysis 큐 → Analyzer 워커
SpecView 요청 → specview 큐 → SpecGen 워커

결과: 작업 라우팅 오류 해결, 그러나 유료 사용자 우선순위 제어 불가.

Phase 3: 티어 기반 멀티큐 (현재)

Pro 사용자 분석 → analysis_priority → Analyzer (priority 워커)
Free 사용자 분석 → analysis_default → Analyzer (default 워커)
스케줄러 작업   → analysis_scheduled → Analyzer (scheduled 워커)

제약 조건

제약출처영향
River 큐 네이밍라이브러리 검증큐 이름에 콜론(:) 사용 불가
PostgreSQL 기반 큐ADR-04기존 River 설정과 통합 필수
워커 프로세스 분리ADR-05 WorkerWorker와 Scheduler 별도 배포
빌링 티어 통합ADR-13큐 선택 시 구독 정보의 티어 활용 필수

결정 배경

ADR-13에서 구독 티어 체계 수립 완료. 그러나 운영상 차별화 부재로 유료 고객이 체감하는 처리 속도 이점 없음. 유료 요금제 가치 제안 약화.

결정

서비스당 3-티어 큐 아키텍처 및 환경 변수 기반 워커 할당 채택.

큐 구조

각 워커 서비스는 티어 기반 라우팅으로 세 개의 큐 유지:

{service}_priority   ← Pro / Pro Plus / Enterprise 사용자
{service}_default    ← Free 티어 사용자
{service}_scheduled  ← 백그라운드 스케줄러 작업

구체적 큐 이름:

서비스Priority 큐Default 큐Scheduled 큐
Analyzeranalysis_priorityanalysis_defaultanalysis_scheduled
SpecGeneratorspecview_priorityspecview_defaultspecview_scheduled

워커 할당 전략

환경 변수를 통한 큐별 워커 수 설정 (합리적 기본값 제공):

Analyzer 서비스:

ANALYZER_QUEUE_PRIORITY_WORKERS=5   # 워커 용량의 50%
ANALYZER_QUEUE_DEFAULT_WORKERS=3    # 워커 용량의 30%
ANALYZER_QUEUE_SCHEDULED_WORKERS=2  # 워커 용량의 20%

SpecGenerator 서비스:

SPECGEN_QUEUE_PRIORITY_WORKERS=3    # 워커 용량의 50%
SPECGEN_QUEUE_DEFAULT_WORKERS=2     # 워커 용량의 33%
SPECGEN_QUEUE_SCHEDULED_WORKERS=1   # 워커 용량의 17%

큐 선택 로직

go
func SelectQueue(baseQueue string, tier PlanTier, isScheduled bool) string {
    if isScheduled {
        return baseQueue + "_scheduled"
    }
    switch tier {
    case PlanTierPro, PlanTierProPlus, PlanTierEnterprise:
        return baseQueue + "_priority"
    default:
        return baseQueue + "_default"
    }
}

네이밍 규칙

River 검증 규칙 준수를 위해 언더스코어를 구분자로 사용:

  • 허용: 영문자, 숫자, 언더스코어, 하이픈
  • 금지: 콜론, 공백, 특수문자

초기 설계 analysis:priority에서 analysis_priority로 변경.

검토 옵션

Option A: 티어 기반 멀티큐 + 환경 변수 워커 할당 (선택)

설명: 서비스별 티어별 별도 큐, 환경 변수로 워커 수 설정.

analysis_priority   → Priority 워커 (5)
analysis_default    → Default 워커 (3)
analysis_scheduled  → Scheduled 워커 (2)

장점:

이점설명
명확한 SLA 경계Priority 큐 깊이로 유료 사용자 경험 측정 가능
독립적 스케일링코드 변경 없이 워커 비율 조정
스케줄러 격리백그라운드 작업이 사용자 요청 기아 방지
모니터링 세분화큐별 메트릭으로 티어별 알림 설정 가능
우아한 저하Priority 큐 비어있으면 워커 유휴 (우선순위 역전 방지)

단점:

트레이드오프완화 방안
설정 복잡도합리적 기본값 제공; 필요 시에만 조정
워커 유휴 용량SLA 보장을 위한 수용 가능한 트레이드오프
모니터링 큐 증가큐별 패널이 있는 통합 대시보드

Option B: 단일 큐 + Priority 필드

설명: 서비스당 단일 큐, 각 작업에 priority 필드. ORDER BY priority DESC로 우선 처리.

sql
SELECT * FROM river_job
WHERE queue = 'analysis' AND state = 'available'
ORDER BY priority DESC, scheduled_at ASC
LIMIT 1 FOR UPDATE SKIP LOCKED;

장점:

  • 간단한 설정 (서비스당 큐 하나)
  • 워커 항상 작업 보유 (유휴 용량 없음)
  • 인프라 구성 요소 최소화

단점:

문제심각도
우선순위 역전 위험높음 - 대량의 무료 작업이 Pro 사용자 지연
쿼리 복잡도중간 - 핫 테이블에 ORDER BY
SLA 보장 불가높음 - 우선 처리 시간 보장 불가
모니터링 어려움중간 - 단일 큐 깊이로 티어 상태 파악 불가

판정: 기각. 우선순위 역전이 유료 티어 차별화라는 핵심 비즈니스 목표 훼손.

Option C: 티어별 워커 인스턴스 분리

설명: 티어별 전용 워커 배포:

analyzer-priority-worker  (Pro/Enterprise 전용)
analyzer-default-worker   (Free 전용)
analyzer-scheduled-worker (Scheduler 전용)

장점:

  • 완전한 리소스 격리
  • 독립적 스케일링 및 배포
  • 명확한 보안 경계 설정 가능

단점:

문제심각도
인프라 비용높음 - 서비스당 3배 워커 배포
용량 낭비높음 - 티어 간 유휴 워커 공유 불가
운영 복잡도높음 - 6개 이상 서비스 배포/모니터링
배포 조율중간 - 스키마 변경 시 모든 인스턴스 영향

판정: 기각. 현재 규모에서 과도한 인프라 오버헤드. 대규모 엔터프라이즈 전용 워커 필요 시 재검토.

결과

긍정적

유료 사용자 가치 제안:

  • Pro/Enterprise 사용자가 워커 용량의 50% 전용 할당 경험
  • 피크 로드 시 유료 사용자 일관된 처리 시간 유지, 무료 티어는 우아하게 저하
  • 요금제 차별화의 명확한 근거

운영 제어:

  • 배포 없이 환경 변수로 워커 할당 조정 가능
  • 티어별 큐 깊이로 사전 스케일링 결정 가능
  • 스케줄러 작업이 사용자 대면 지연에 영향 불가

관측 가능성:

메트릭의미
Priority 큐 깊이유료 사용자 경험 건강도
Default 큐 깊이무료 티어 대기 시간
Scheduled 큐 깊이백그라운드 작업 백로그

부정적

설정 오버헤드:

  • 워커 서비스당 6개 환경 변수 (전체 3개 서비스에 18개)
  • 잘못된 비율 설정 시 용량 낭비 또는 유료 경험 저하
  • 운영자용 문서화 필요

워커 유휴 용량:

  • 유료 사용자 트래픽 저조 시 Priority 워커 유휴
  • 큐 간 동적 재균형 불가
  • SLA 보장을 위한 수용 가능한 트레이드오프

모니터링 복잡도:

  • 서비스당 3개 큐 (기존 2개 대비 총 6개)
  • 대시보드 및 알림 설정 증가
  • 디버깅 시 큐 간 상관관계 분석 필요

기술적 함의

측면함의
River 설정워커당 3개 항목의 QueueConfig
큐 네이밍언더스코어 구분자 (_priority, _default, _scheduled)
티어 조회API 레이어에서 enqueue 전 구독 정보 조회
우아한 저하알 수 없는 티어는 _default 큐로 라우팅
워커 헬스각 큐별 독립적 워커 풀 상태
배포환경 변수로 기본 워커 수 오버라이드

참조

Open-source test coverage insights