SolidQueue는 데이터베이스를 활용한 백그라운드 작업 큐잉 시스템이 직면하는 고유한 도전 과제, 특히 “폴링 경쟁(polling contention)” 문제를 해결하는 데 중점을 둡니다. 기존 데이터베이스 기반 큐 시스템은 여러 워커가 동시에 새 작업을 찾기 위해 테이블을 폴링할 때, 작업 잠금으로 인해 대기 시간이 발생하는 문제가 있었습니다. SolidQueue는 MySQL과 PostgreSQL에서 제공하는 FOR UPDATE SKIP LOCKED
기능을 활용하여 워커들이 서로 대기하지 않고 잠긴 행을 건너뛰고 다음 작업을 즉시 가져갈 수 있도록 함으로써 이 문제를 해결합니다.
또한, SolidQueue는 큐 테이블의 크기를 최소화하는 독창적인 설계 방식을 채택했습니다. 모든 작업을 하나의 거대한 테이블에 저장하는 대신, ready
, scheduled
, claimed
등 작업의 상태에 따라 별도의 작은 테이블에 메타데이터를 분리하여 저장합니다. 이를 통해 폴링 쿼리의 성능을 극대화하고, 데이터베이스 부하를 줄입니다. 예를 들어, 미래에 실행될 작업은 scheduled
테이블에, 즉시 실행될 준비가 된 작업은 ready
테이블에 보관하며, 워커가 작업을 가져가면 claimed
테이블로 이동시킵니다. 작업이 완료되면 claimed
테이블에서 제거되어 테이블 크기가 항상 작게 유지됩니다.
SolidQueue는 지연 작업(delayed jobs), 반복 작업(recurring jobs), 큐 일시 중지(pausing queues), 그리고 동시성 제어(concurrency controls)와 같은 다양한 Active Job 기능을 지원합니다. 특히 동시성 제어는 특정 작업들이 동시에 실행되지 않도록 보장하며, 이는 기존 Resque 시스템에서 Redis의 데이터 구조를 활용하여 구현했던 복잡한 로직을 세마포어(semaphore) 개념을 도입하여 데이터베이스 수준에서 간결하게 처리합니다.
성능 측면에서, SolidQueue는 높은 부하를 처리하기 위해 자체 데이터베이스 인스턴스를 사용하는 것을 권장합니다. 이는 특히 SQLite와 같이 동시 쓰기(concurrent writes)를 지원하지 않는 데이터베이스에서 애플리케이션의 성능 저하를 방지하는 데 중요합니다. 폴링 쿼리는 큐 이름과 우선순위에 대한 커버링 인덱스(covering indexes)를 활용하여 최적화되었으며, 이를 통해 초당 수백 건의 쿼리에도 불구하고 각 쿼리당 평균 110 마이크로초의 매우 낮은 실행 시간을 보여줍니다.
SolidQueue의 작업 인큐잉 속도는 Redis 기반 솔루션(예: Resque)보다 느리지만(약 3~6ms vs 1ms 미만), 대부분의 Rails 애플리케이션에서 작업 인큐잉이 쓰기 요청(POST/PUT)에서 발생하고, 이러한 요청 자체가 데이터베이스 쓰기로 인해 느리다는 점을 고려할 때, 전체 응답 시간에 미치는 영향은 미미합니다. 오히려 대량 작업 인큐잉 시 SolidQueue의 일괄 처리 기능은 Resque보다 빠른 성능을 제공합니다. 운영적인 측면에서 SolidQueue는 Active Record 객체를 통해 작업을 관리하므로, 기존 Rails 애플리케이션 데이터를 다루는 것처럼 직관적이고 문제 해결이 용이하다는 큰 이점이 있습니다.