๐Ÿ“‹

Solid Queue (Rails)

DB-only without Redis โ€” Rails 8 default background job system

GitHub: rails/solid_queue

Solid Queue is a DB-based ActiveJob backend created by 37signals, included by default starting with Rails 8. It started from the question: "Do we really need Redis?"

Please refer to the Korean version for the detailed architecture analysis, including: the core idea of managing state via separate tables instead of status columns, the 10-table DB schema, the job lifecycle (enqueue โ†’ dispatch โ†’ claim & execute), 3 process types (Worker/Dispatcher/Scheduler), FOR UPDATE SKIP LOCKED for non-blocking polling, and transaction safety with enqueue_after_transaction_commit?.

Architecture Diagram

Job State Table Separation (ERD)

๐Ÿ“‹ solid_queue_jobs GitHub ↗
queue_name, class_name, arguments, priority, scheduled_at, finished_at
ON DELETE CASCADE
โœ…
ready_executions
Ready
โฐ
scheduled_executions
Scheduled
๐Ÿ”„
claimed_executions
Processing
โŒ
failed_executions
Failed
๐Ÿ”’
blocked_executions
Concurrency wait
๐Ÿ”‘
semaphores
Concurrency control
Key point: <strong>State-specific tables</strong> instead of a <strong>status column</strong>
Optimized indexes per table = fast polling

Job Lifecycle

MyJob.perform_later(args) Job.rb ↗
solid_queue_jobs INSERT + after_create :prepare_for_execution
Run now?
Scheduled?
Concurrency limit?
ready_executions
scheduled_executions
blocked_executions
Dispatcher moves scheduled โ†’ ready dispatcher.rb ↗
Worker: claim (FOR UPDATE SKIP LOCKED)
ready โ†’ claimed (transaction)
worker.rb ↗
ActiveJob::Base.execute(arguments)
Success
finished_at updated
Failed
failed_executions

3 Process Types

โš™๏ธ
Worker
Poll ready_executions
claim (SKIP LOCKED)
Execute in thread pool
worker.rb ↗
๐Ÿ“ก
Dispatcher
Poll scheduled_executions
Move due jobs โ†’ ready
Concurrency maintenance
dispatcher.rb ↗
๐Ÿ”
Scheduler
Check cron schedule
Enqueue recurring jobs
Prevent duplicate execution
scheduler.rb ↗

Key Points

1

Open rails/solid_queue repository on GitHub

2

db/migrate/ โ†’ check 10 table schemas (state-per-table separation pattern)

3

lib/solid_queue/worker.rb โ†’ analyze polling loop and claim logic

4

lib/solid_queue/dispatcher.rb โ†’ analyze scheduled job dispatch logic

5

app/models/solid_queue/job.rb โ†’ check enqueue flow

6

app/models/solid_queue/ready_execution.rb โ†’ check FOR UPDATE SKIP LOCKED

7

app/models/solid_queue/claimed_execution.rb โ†’ execution/success/failure handling

8

lib/solid_queue/supervisor.rb โ†’ Fork vs Async process management

Pros

  • No Redis needed โ€” complete job system with DB only
  • Rails 8 default โ€” ready to use without separate installation
  • ActiveJob compatible โ€” switch without changing existing job code
  • FOR UPDATE SKIP LOCKED โ€” non-blocking competition between workers
  • Built-in concurrency control โ€” semaphore-based concurrency limit
  • Puma plugin โ€” no separate process needed in development

Cons

  • May have lower throughput than Redis-based Sidekiq
  • Increased DB load โ€” heavy write burden with many jobs
  • SKIP LOCKED not supported in SQLite (graceful fallback)
  • Sidekiq/Redis still advantageous for large-scale services
  • Web UI less mature than Sidekiq (Mission Control separate)

Use Cases

Email sending: UserMailer.welcome.deliver_later Data processing: CsvImportJob.perform_later(file) Scheduled tasks: generate daily midnight reports with Scheduler Concurrency control: sequential execution of same user payment jobs Sidekiq โ†’ Solid Queue migration Background job processing without Redis in self-hosted apps