Rails 8에 새롭게 추가된 Solid Queue는 데이터베이스만을 기반으로 하는 백그라운드 작업 처리 라이브러리로, 특히 주목할 만한 기능입니다. 기존의 Resque나 Sidekiq와 같은 인기 라이브러리가 요구했던 Redis와 같은 외부 의존성을 제거함으로써 애플리케이션 인프라를 단순화하는 것을 목표로 합니다. 이러한 접근 방식은 Rails 7부터 도입된 SQLite 기본 데이터베이스 활용 전략의 연장선상에 있으며, 37Signals 팀이 운영 오버헤드를 줄이기 위해 개발되었습니다. 새로운 Rails 기본값으로서 Solid Queue는 다양한 기능 지원, 데이터베이스 호환성, 작업 손실 방지 등의 안정성, 그리고 대규모 시스템에서도 활용 가능한 성능 등 까다로운 요구사항을 모두 충족해야 합니다.
Solid Queue의 아키텍처는 크게 Job과 Worker로 구성됩니다. Job은 ActiveRecord 모델로 사용자가 백그라운드 실행을 위해 생성하는 작업 단위를 나타내며, perform_later
등의 메서드로 큐에 추가됩니다. Worker는 실제 작업을 처리하는 백그라운드 프로세스로, 애플리케이션 설정(config/queue.yml
)에 따라 자동으로 생성됩니다. Job과 Worker는 여러 데이터베이스 테이블을 통해 상호작용하며 작업을 관리합니다. 작업이 큐에 추가되면 solid_queue_jobs
테이블에 상세 정보가 저장되고, 즉시 실행 대기 상태인 경우 solid_queue_ready_executions
테이블에도 기록됩니다. Worker는 작업을 찾기 위해 이 ready_executions
테이블을 폴링(polling)합니다. 성능 향상을 위해 Solid Queue는 전체 jobs
테이블 대신 규모가 작은 ready_executions
테이블을 집중적으로 사용하며, FOR UPDATE SKIP LOCKED
와 같은 최신 데이터베이스 기능을 활용하여 Worker 간의 동시성을 확보합니다. 다만 SQLite는 SKIP LOCKED
를 지원하지 않아 동시성 측면에서 일부 제약이 있습니다. 작업 손실을 방지하는 안정성은 Solid Queue의 핵심 기능입니다. Worker가 작업을 처리하기 시작하면 solid_queue_claimed_executions
테이블에 해당 작업과 자신의 프로세스 ID를 기록하여 클레임합니다. 또한 모든 Worker는 solid_queue_processes
테이블에 주기적으로 자신의 상태(heartbeat)를 업데이트합니다. 별도의 supervisor
프로세스는 solid_queue_processes
테이블을 모니터링하며, 설정된 임계값(기본 5분) 이상 heartbeat가 업데이트되지 않은 Worker를 비정상 종료된 것으로 판단합니다. supervisor
는 해당 Worker의 프로세스 레코드를 삭제하고, solid_queue_claimed_executions
테이블에서 해당 Worker가 클레임했던 작업들을 회수하여 ready_executions
테이블로 되돌려 놓습니다. 이를 통해 비정상 종료된 Worker가 처리 중이던 작업이 손실되지 않고 다른 Worker에 의해 다시 처리될 수 있도록 보장합니다.
본 게시물에서는 Solid Queue의 개발 배경과 데이터베이스 중심 아키텍처, 기본적인 작업의 생성부터 완료까지의 생명주기, 그리고 효율적인 성능과 작업 손실 없는 안정성을 보장하기 위한 주요 메커니즘(작은 테이블 폴링, `SKIP LOCKED`, `supervisor` 프로세스)을 상세히 살펴보았습니다. 이러한 핵심적인 내용 외에도 Solid Queue는 예약 작업(scheduled jobs) 및 순차 작업(sequential jobs) 등 다양한 기능을 제공하며, 이 부분은 다음 게시물에서 더 자세히 다룰 예정입니다.