11P by GN⁺ 17시간전 | ★ favorite | 댓글 1개
  • Rails 8은 기본 스택에서 Redis 의존성을 제거하고, SolidQueue·SolidCache·SolidCable을 통해 모든 작업을 관계형 데이터베이스(RDB) 위에서 처리하도록 전환
  • Redis는 빠르고 안정적이지만, 설정·보안·클러스터 관리·백업 등 운영 복잡성을 초래함
  • SolidQueue는 PostgreSQL의 FOR UPDATE SKIP LOCKED 기능을 활용해 경쟁 없는 병렬 작업 처리를 구현
  • 주기적 작업, 동시성 제어, 모니터링 대시보드(Mission Control) 등 Redis+Sidekiq 유료 기능을 무료로 제공
  • 대부분의 Rails 애플리케이션은 SolidQueue로 충분하며, 초고속·실시간 처리가 필요한 일부 경우만 Redis 유지 필요

Redis의 숨은 비용

  • Redis는 단순한 호스팅 비용 외에도 설치·유지보수·보안 구성·HA 클러스터 관리 등 지속적 관리 부담이 존재
    • Rails와 Redis 간 네트워크 연결 및 방화벽 설정, 클라이언트 인증, Sidekiq 프로세스 오케스트레이션 필요
  • 장애 발생 시 Redis와 RDBMS 두 시스템을 동시에 디버깅해야 하며, 이중 백업 전략도 요구됨
  • 반면 Redis 없는 Rails 스택에서는 PostgreSQL 하나만 관리하면 되어 단순화 가능

SolidQueue의 동작 원리

  • PostgreSQL의 FOR UPDATE SKIP LOCKED 기능을 이용해 잠금 충돌(lock contention) 없이 여러 워커가 동시에 작업을 가져감
  • 주요 테이블 구조
    1. solid_queue_jobs: 작업 메타데이터 저장
    2. solid_queue_scheduled_executions: 예약된 작업 대기
    3. solid_queue_ready_executions: 실행 준비된 작업 큐
  • 워커·디스패처·스케줄러·슈퍼바이저 프로세스가 각기 다른 테이블을 주기적으로 폴링하며 협력
  • PostgreSQL의 MVCC 설계와 autovacuum 덕분에 대량의 삽입·삭제 작업도 안정적으로 처리

반복 작업 스케줄링

  • SolidQueue는 cron 스타일의 반복 작업을 기본 제공하며, config/recurring.yml 파일로 설정
  • 스케줄러는 실행 시점이 된 작업을 큐에 넣고, 다음 실행 시점을 자동 예약
  • Fugit 라이브러리로 자연어 스케줄을 파싱하고, Concurrent::ScheduledTask로 스레드를 생성
  • GoodJob의 결정적(deterministic) 스케줄링 방식을 차용해 프로세스 재시작에도 일정 유지

동시성 제어 기능

  • SolidQueue는 POSIX 세마포어 패턴을 사용해 작업 단위별 동시 실행 제한을 지원
    • 예: limits_concurrency to: 1, key: ->(user) { user.id } 설정 시 사용자별 1개 작업만 실행
  • 세마포어 만료(duration)를 지정해 작업 충돌·데드락 방지
  • 관련 테이블
    • solid_queue_semaphores: 동시성 제한 추적
    • solid_queue_blocked_executions: 대기 중인 작업 저장

Mission Control을 통한 모니터링

  • Mission Control Jobs는 Rails 8용 무료 오픈소스 대시보드로, /jobs 경로에 간단히 마운트 가능
  • 주요 기능
    • 실시간 큐 상태, 실패 작업 추적, 재시도/폐기 제어
    • 예약·반복 작업 타임라인 시각화
    • 큐별 처리량 및 메트릭 그래프
  • SQL 기반 조회를 지원해 추가 도구 없이 데이터베이스에서 직접 분석 가능

Sidekiq에서 SolidQueue로의 마이그레이션

  • 1단계: config.active_job.queue_adapter = :solid_queue 설정
  • 2단계: bundle add solid_queuerails solid_queue:installdb:migrate 실행
  • 3단계: sidekiq.yml의 cron 스케줄을 recurring.yml로 변환
  • 4단계: Procfilejobs: bundle exec rake solid_queue:start 추가
  • 5단계: Redis·Sidekiq 관련 gem 삭제
  • 기존 ActiveJob 코드는 수정 없이 그대로 작동

Redis가 여전히 필요한 경우

  • 초당 수천 건 이상의 지속적 작업 처리
  • 1ms 미만 지연(latency) 이 필수인 실시간 시스템
  • 복잡한 pub/sub 구조 또는 정교한 rate limiting·카운터 연산 필요
  • 예시로 Shopify는 초당 833 요청, 1,172 워커 프로세스를 운영하며 Redis 인프라를 사용

실제 구현 가이드

  • Rails 8 신규 앱 생성 시 SolidQueue·SolidCache·SolidCable 자동 구성
  • config/database.yml별도 queue 데이터베이스 연결 설정 권장
  • Mission Control 인증 추가 및 /jobs 라우트 마운트
  • Procfile.devjobs: bundle exec rake solid_queue:start 추가 후 bin/dev 실행으로 전체 구동
  • 테스트 작업 생성 후 Mission Control에서 상태 확인 가능

자주 발생하는 문제와 해결

  • 단일 데이터베이스 구성도 가능하지만, 운영 유연성 저하
  • 프로덕션 환경 Mission Control에는 반드시 인증 추가 필요
  • 폴링 간격 기본값은 예약 1초, 즉시 작업 0.2초로 대부분의 앱에 적합
  • ActionCable/Turbo Streams 사용 시 SolidCable을 별도 DB 연결로 설정해야 함

확장성과 성능

  • SolidQueue는 대부분의 Rails 앱에서 충분히 확장 가능
  • PostgreSQL 기반으로 초당 200~300 작업 처리 가능하며, 37signals는 하루 2천만 작업을 Redis 없이 처리
  • 비교표
    항목 Redis + Sidekiq SolidQueue
    설정 복잡도 별도 서비스 필요 내장 DB 사용
    쿼리 언어 Redis 명령어 SQL
    모니터링 별도 대시보드 Mission Control
    장애 시나리오 6개 이상 2개
    처리량 수천 건/초 200–300건/초
    적합 대상 99.9% 앱 95% 앱

결론

  • Redis와 Sidekiq는 뛰어난 기술이지만, 대부분의 Rails 애플리케이션에는 과도한 복잡성과 비용을 초래
  • SolidQueue는 단일 데이터베이스 기반으로 운영 단순화·비용 절감·유지보수 효율화를 실현
  • Rails 8 시대의 기본 선택지로 SolidQueue 전환이 권장됨
Hacker News 의견들
  • 모든 오픈소스 저자는 자신의 프로젝트 범위를 통제할 권리가 있다고 생각함
    하지만 우리 팀이 good_job에서 SolidQueue로 바꾼 건 후회 중임
    Basecamp은 MySQL 중심이라 RDBMS 엔진 특화 쿼리를 받지 않음. GitHub 이슈를 보면 MySQL 성능에만 집중하는 걸 볼 수 있음
    또한 아직 배치 작업 지원이 없음 (관련 PR)

    • 최악의 조합처럼 들림. 우리도 MySQL을 쓰지만, 엔진 특화 쿼리 없이는 못 살겠음
      복잡한 JOIN에서 MySQL이 쿼리 플랜을 잘못 짜는 경우가 많아서, 나는 STRAIGHT_JOIN으로 순서를 강제함. 미래 대비용임
    • MySQL에 그렇게 깊게 묶여 있다면, MySQL 전용 기능을 쓰는 게 논리적이지 않음? 뭔가 빠진 것 같음
    • GoodJob을 SolidQueue보다 추천하는 이유를 좀 더 구체적으로 듣고 싶음
      나는 resque에서 옮길 후보로 두 개를 비교 중임. GoodJob은 pg 전용 기능 때문에 pgbouncer transaction 모드와 호환이 안 됨
      세션 지속이 필요해서 귀찮지만, 성능 향상은 대부분의 규모에서 큰 의미가 없음
      그래도 GoodJob의 개발 모델과 코드 가독성은 훨씬 신뢰감 있음
    • 동의함. good_job은 Postgres 기반 큐로는 이상적인 접근임
    • SolidQueue는 아직 잘 모르겠지만, good_job은 써보니 정말 즐거움. 잘 작동함
  • 프로덕션 환경이 단순해질 수 있다면 언제나 좋은 일임
    Rails에서 이상적인 상황은 Redis로 간단히 전환할 수 있는 구조라고 생각함
    SolidQueue로 시작했다가 확장성 한계에 부딪히면 Redis로 옮길 수 있으면 좋겠음
    대부분의 Rails 앱은 트래픽이 크지 않으니, 두 시스템을 유지하는 게 오히려 복잡함

    • Rails는 Active Job이라는 추상화된 API를 제공해서 Redis로 전환이 꽤 쉬움
      물론 특정 큐 구현에 의존하는 앱도 있지만, 일반적인 경우 설정만 바꾸면 됨
    • Redis AOF 모드가 WAL처럼 모든 변경을 기록하는지 궁금함
      로그가 너무 커지는 걸 막기 위해 스냅샷도 병행하는지, 그리고 이게 분산 모드에서도 작동하는지 알고 싶음
    • 트랜잭션에 너무 의존하면 마이그레이션이 어려워짐
      특히 작업 생성이 다른 DB 변경과 함께 일어나는 경우, 그 보장성을 잃는 게 문제임
    • Rails는 백그라운드 작업 프로세서를 프로덕션 DB와 분리하지 않아서 혼란스러움
      Redis는 이 점에서 가볍고 독립적인 상태 저장소로 유리했음
      SolidQueue는 이런 분리를 명확히 하지 않는 것 같음 (riverqueue.com)
  • 내 사이드 프로젝트에서 SolidQueue를 실험해봤음
    결론은, Sidekiq에 문제가 없다면 굳이 바꿀 이유가 없음
    Redis 인프라를 없애고 싶을 때만 고려할 만함
    새 프로젝트라면 GoodJob 쪽이 더 성숙하고 커뮤니티도 좋음
    SolidQueue는 UI가 너무 단순해서 불편했음. 인덱스 최적화가 안 되어 데이터가 많아지면 페이지가 멈췄음
    RDBMS를 쓰면 커넥션 풀 관리 비용이 추가된다는 점도 고려해야 함

  • 확장성 걱정하는 사람들을 위해, Elixir의 Oban 벤치마크를 보면
    단일 노드에서 분당 백만 개의 작업을 처리함. 대부분의 앱은 그보다 훨씬 적은 작업량임

    • 우리 회사도 Oban을 쓰는데, Oban은 Redis를 notifier로 사용하거나 폴링을 권장함 (scaling 문서)
    • 하지만 그 벤치마크는 실제 앱과는 거리가 큼
      5000개 작업을 한 번에 배치로 넣는 구조라 TPS는 실제로 200 정도임
      배치 없이 개별 작업을 넣으면 SQL 트랜잭션 부하가 훨씬 커짐
  • 우리는 SolidQueue 이전부터 DB에 작업을 저장해왔음
    장점은 프로덕션 상태를 그대로 개발 환경으로 스냅샷할 수 있다는 점임
    다만 rate limiter는 Redis에 두고 있음. DB 부하 방지를 위해서임

  • DB 기반 큐의 한계는 대용량 payload
    큰 JSON을 큐에 넣으면 DB 쓰기 오버헤드로 비효율적임
    Redis(Sidekiq)가 이런 경우 훨씬 빠름
    SolidQueue+SQLite는 단순히 primary key 전달용으로는 괜찮음
    하지만 다수의 워커가 같은 DB를 폴링하면 금방 병목이 생김

    • 실제로는 작업 파라미터를 ID 한두 개만 전달하는 게 일반적임. 그 이상은 비효율적임
    • Redis도 메모리 한계 때문에 큰 payload에는 부적합함
      큰 데이터는 S3 같은 외부 스토리지에 두고 참조만 전달하는 게 낫다고 생각함
    • 어차피 시스템의 다른 부분에서도 스토리지가 필요하니, S3나 garage 같은 임시 저장소를 쓰는 게 현실적임
      혹시 벤치마크 결과를 정리한 자료가 있는지 궁금함
    • Sidekiq 문서에서도 식별자만 전달하라고 권장함
    • Redis에 큰 payload를 저장하는 건 나쁜 관행임. 메모리가 한정되어 있기 때문임
  • SolidQueue에서 SKIP LOCKED를 언급하지만, 15분짜리 작업을 트랜잭션으로 유지하는 건 위험함
    장시간 열린 트랜잭션은 DB 성능을 망치고, 네트워크 끊김에도 취약함
    이런 구조는 안티패턴으로 이어질 수 있음. 나중에 보니 lease 방식인 듯함

  • Postgres for everything 철학에 공감함
    단순하게 PostgreSQL 하나로 통합하는 게 좋다고 생각함

    • 나도 PG 올인 아이디어를 좋아하지만, “망치만 있으면 모든 게 못처럼 보인다”는 말을 자주 듣음
      이 비유에 어떻게 반박해야 할지 모르겠음
    • 요즘은 NVMe 스토리지가 너무 빨라서 Redis의 이점이 줄었다고 느낌
      복잡성을 늘리면서까지 Redis를 쓸 이유가 있을까 싶음
  • “1ms 미만의 지연 시간이 중요한 비즈니스”라니, Rails로 HFT를 한다는 말인가?

    • 물론 아님. 그 회사는 SimpleThread 사례를 보면 HFT와는 거리가 멂
    • 거래 엔진은 Rails로 안 돌겠지만, 거래 모니터링 UI는 Rails로 만들 수도 있음
  • Postgres가 세상을 먹을 것임

    • 나도 동의함. 언젠가 pg_kernel 확장이 나오면 Linux를 지워버릴지도 모름
    • 하지만 몇 년 뒤엔 “Postgres for everything”도 “MongoDB for everything”처럼 과한 유행이었다는 걸 깨달을 수도 있음
    • 그래도 MySQL은 유지보수도 쉽고 성능도 준수함 (나는 Postgres 유저임에도 그렇게 느낌)
    • 지금은 PGQM과 PG_CRON을 쓰는데, 예전의 MySQL + Redis + AWS 조합보다 훨씬 깔끔함
    • 결국 중요한 건 기능 세트임. RDBMS가 세상을 먹을 것임