solid-queue-setup

SKILL.md

Solid Queue Setup for Rails 8

Overview

Solid Queue is Rails 8's default Active Job backend:

  • Database-backed (no Redis required)
  • Built-in concurrency controls
  • Supports priorities and multiple queues
  • Mission-critical job processing
  • Web UI available via Mission Control

Quick Start

Installation

# Add to Gemfile (included in Rails 8 by default)
bundle add solid_queue

# Install Solid Queue
bin/rails solid_queue:install

# Run migrations
bin/rails db:migrate

Configuration

# config/solid_queue.yml
default: &default
  dispatchers:
    - polling_interval: 1
      batch_size: 500
  workers:
    - queues: "*"
      threads: 3
      processes: 1
      polling_interval: 0.1

development:
  <<: *default

production:
  <<: *default
  workers:
    - queues: [critical, default]
      threads: 5
      processes: 2
    - queues: [low]
      threads: 2
      processes: 1

Set as Active Job Adapter

# config/application.rb
config.active_job.queue_adapter = :solid_queue

# Or per environment
# config/environments/production.rb
config.active_job.queue_adapter = :solid_queue

Workflow Checklist

Solid Queue Setup:
- [ ] Add solid_queue gem
- [ ] Run solid_queue:install
- [ ] Run migrations
- [ ] Configure queues in solid_queue.yml
- [ ] Set queue adapter in config
- [ ] Create first job with spec
- [ ] Test job execution
- [ ] Configure recurring jobs (if needed)

Creating Jobs

Basic Job

# app/jobs/send_welcome_email_job.rb
class SendWelcomeEmailJob < ApplicationJob
  queue_as :default

  def perform(user_id)
    user = User.find(user_id)
    UserMailer.welcome(user).deliver_now
  end
end

Job with Retries

# app/jobs/process_payment_job.rb
class ProcessPaymentJob < ApplicationJob
  queue_as :critical

  # Retry on specific errors
  retry_on PaymentGatewayError, wait: :polynomially_longer, attempts: 5

  # Don't retry on these
  discard_on ActiveRecord::RecordNotFound

  # Custom error handling
  rescue_from(StandardError) do |exception|
    ErrorNotifier.notify(exception)
    raise # Re-raise to trigger retry
  end

  def perform(order_id)
    order = Order.find(order_id)
    PaymentService.new.charge(order)
  end
end

Job with Priority

class UrgentNotificationJob < ApplicationJob
  queue_as :critical

  # Lower number = higher priority
  # Default is 0
  def priority
    -10
  end

  def perform(notification_id)
    # Process urgent notification
  end
end

Enqueueing Jobs

# Enqueue immediately
SendWelcomeEmailJob.perform_later(user.id)

# Enqueue with delay
SendReminderJob.set(wait: 1.hour).perform_later(user.id)

# Enqueue at specific time
SendReportJob.set(wait_until: Date.tomorrow.noon).perform_later

# Enqueue on specific queue
ProcessJob.set(queue: :low).perform_later(data)

# Perform immediately (skips queue - use sparingly)
SendWelcomeEmailJob.perform_now(user.id)

Recurring Jobs

# config/recurring.yml
production:
  daily_report:
    class: GenerateDailyReportJob
    schedule: every day at 6am
    queue: low

  cleanup:
    class: CleanupOldRecordsJob
    schedule: every sunday at 2am

  sync:
    class: SyncExternalDataJob
    schedule: every 15 minutes

Testing Jobs

Job Spec Template

# spec/jobs/send_welcome_email_job_spec.rb
require 'rails_helper'

RSpec.describe SendWelcomeEmailJob, type: :job do
  let(:user) { create(:user) }

  describe '#perform' do
    it 'sends welcome email' do
      expect {
        described_class.perform_now(user.id)
      }.to have_enqueued_mail(UserMailer, :welcome)
    end
  end

  describe 'enqueueing' do
    it 'enqueues the job' do
      expect {
        described_class.perform_later(user.id)
      }.to have_enqueued_job(described_class)
        .with(user.id)
        .on_queue('default')
    end
  end

  describe 'retry behavior' do
    it 'retries on PaymentGatewayError' do
      expect(described_class).to have_retry_on(PaymentGatewayError)
    end
  end
end

Test Helpers

# spec/rails_helper.rb
RSpec.configure do |config|
  config.include ActiveJob::TestHelper
end

# In specs
it 'processes all jobs' do
  perform_enqueued_jobs do
    UserSignupService.call(user_params)
  end
  expect(user.reload.welcome_email_sent?).to be true
end

it 'enqueues multiple jobs' do
  expect {
    BatchProcessor.process(items)
  }.to have_enqueued_job(ProcessItemJob).exactly(items.count).times
end

Running Solid Queue

# Development (runs in separate terminal)
bin/rails solid_queue:start

# Production (via Procfile)
# Procfile
web: bin/rails server
worker: bin/rails solid_queue:start

Monitoring

Mission Control (Web UI)

# Gemfile
gem "mission_control-jobs"

# config/routes.rb
mount MissionControl::Jobs::Engine, at: "/jobs"

Console Queries

# Check pending jobs
SolidQueue::Job.where(finished_at: nil).count

# Check failed jobs
SolidQueue::FailedExecution.count

# Retry failed job
SolidQueue::FailedExecution.last.retry

# Clear old completed jobs
SolidQueue::Job.where('finished_at < ?', 1.week.ago).delete_all

Migration from Sidekiq

Sidekiq Solid Queue
perform_async(args) perform_later(args)
perform_in(5.minutes, args) set(wait: 5.minutes).perform_later(args)
sidekiq_options queue: 'critical' queue_as :critical
sidekiq_retry_in retry_on with wait:
Weekly Installs
21
GitHub Stars
407
First Seen
Jan 23, 2026
Installed on
opencode15
codex14
claude-code12
github-copilot12
gemini-cli12
cursor12