도입
본 글은 2011년부터 2015년까지 Heroku API를 운영하며 루비 생태계의 성장을 지켜본 저자가 루비 백그라운드 작업 프레임워크의 역사를 정리한 기술 아티클입니다. 기술의 발전은 이전 세대의 약점을 보완하며 이루어진다는 관점에서, 초기 BackgroundDRb부터 최신 Solid Queue에 이르기까지 각 단계의 기술적 특징과 한계를 분석합니다. 이를 통해 현대의 도구들이 왜 현재와 같은 형태를 갖추게 되었는지에 대한 깊이 있는 맥락을 제공하며, 과거의 기술적 제약이 어떻게 극복되었는지 설명합니다.
1. 초기 프레임워크: BackgroundDRb와 Delayed::Job
- BackgroundDRb (2008): 루비 표준 라이브러리인 DRb를 활용하여 시작된 초기 프레임워크입니다. 이미 2008년에 데이터베이스 기반의 작업 영속성을 구현했으나, 작업 완료(
#finish!)나 해제(#release_job)를 개발자가 수동으로 호출해야 하는 위험이 있었고 내장된 재시도 메커니즘이 부족했습니다.
- Delayed::Job (Shopify): 쇼피파이에서 추출된 프레임워크로,
attempts와 run_at 컬럼을 도입하여 재시도와 예약 실행을 체계화했습니다. 하지만 당시 데이터베이스 기술의 한계로 SKIP LOCKED를 사용하지 못했고, 모든 워커가 개별 프로세스로 실행되는 포킹 모델을 사용하여 메모리 사용량이 매우 높았습니다.
2. Redis의 등장과 Resque의 혁신
- Resque (GitHub): Redis의 리스트(List)와 셋(Set) 자료구조를 활용하여 데이터베이스의 부하를 줄이고 성능을 극대화했습니다. 원자적인 push/pop 연산을 통해 빠른 처리가 가능했으나, 데이터베이스 트랜잭션과의 연동이 불가능하다는 치명적인 약점이 있었습니다. 즉, 데이터가 DB에 커밋되기 전에 워커가 작업을 시작하여 레코드를 찾지 못하는 레이스 컨디션 문제가 발생하기 쉬웠으며, 이를 해결하기 위해 수동적인 백오프 로직이 필요했습니다.
3. Postgres 전용 프레임워크: Queue Classic과 Que
- Queue Classic: Postgres의
LISTEN/NOTIFY 기능을 활용하여 폴링 루프 없이 워커에게 즉각적인 알림을 전달하고, 권고 잠금(Advisory Locks)을 통해 성능을 개선했습니다.
- Que: Queue Classic을 정교화하여 리더-워커 모델을 도입했습니다. 이는 Postgres의 MVCC 모델에서 발생하는 테이블 비대화(Bloat) 문제를 완화하기 위한 선택이었습니다. 하지만 여전히
SKIP LOCKED를 완전히 활용하지는 못했습니다.
4. 현대적 표준: Sidekiq과 Solid Queue
- Sidekiq: Redis 기반이면서도 풍부한 기능(주기적 작업, 속도 제한 등)과 기본 제공되는 웹 UI를 통해 루비 커뮤니티의 표준이 되었습니다. 개발자 경험(DX)을 크게 향상시킨 ‘배터리 포함(Batteries included)’ 전략의 승리였습니다.
- GoodJob (2020): ActiveRecord와 Postgres 전용 기능을 결합한 2세대 백엔드로, 단순한 아키텍처를 지향하며 현대적인 Ruby on Rails 환경에 최적화되었습니다.
- Solid Queue (2023): DHH가 발표한 레일즈의 통합 큐 구현체입니다. SSD의 보급으로 디스크 기반 데이터베이스의 성능이 향상됨에 따라 다시 DB 기반으로 회귀했습니다.
SKIP LOCKED를 적극 활용하여 경합을 줄이고, 멀티 스레드 모델을 통해 루비의 동시성 성능을 최대한 끌어올렸습니다.
결론
루비 백그라운드 작업 프레임워크의 역사는 성능을 위한 외부 저장소(Redis) 사용과 정합성을 위한 데이터베이스 회귀 사이의 끊임없는 상호작용을 보여줍니다. 최신 Solid Queue는 과거의 운영적 교훈과 현대 데이터베이스의 발전을 결합하여, 추가 인프라 없이도 강력한 트랜잭션 일관성과 성능을 모두 확보하는 단계에 이르렀습니다. 이러한 진화 과정은 기술적 선택이 단순히 유행을 따르는 것이 아니라, 인프라 하드웨어의 변화와 누적된 운영 경험의 산물임을 시사하며 개발자들에게 깊은 통찰을 제공합니다.