| name | solid-queue-setup |
| description | Configures Solid Queue for background jobs in Rails 8. Use when setting up background processing, creating background jobs, configuring job queues, recurring jobs, or migrating from Sidekiq to Solid Queue. |
| allowed-tools | Read, Write, Edit, Bash, Glob, Grep |
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
- Web UI available via Mission Control
Quick Start
bundle add solid_queue
bin/rails solid_queue:install
bin/rails db:migrate
Configuration
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.active_job.queue_adapter = :solid_queue
Naming Convention
Use _later for async, _now for synchronous:
SendWelcomeEmailJob.perform_later(user.id)
SendWelcomeEmailJob.perform_now(user.id)
Creating Jobs
Basic Job
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
class ProcessPaymentJob < ApplicationJob
queue_as :critical
retry_on PaymentGatewayError, wait: :polynomially_longer, attempts: 5
discard_on ActiveRecord::RecordNotFound
rescue_from(StandardError) do |exception|
ErrorNotifier.notify(exception)
raise
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
def priority
-10
end
def perform(notification_id)
notification = Notification.find(notification_id)
notification.deliver!
end
end
Enqueueing Jobs
SendWelcomeEmailJob.perform_later(user.id)
SendReminderJob.set(wait: 1.hour).perform_later(user.id)
SendReportJob.set(wait_until: Date.tomorrow.noon).perform_later
ProcessJob.set(queue: :low).perform_later(data)
Recurring Jobs
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
session_cleanup:
class: SessionCleanupJob
schedule: every day at 3am
Testing Jobs
Job Test Template
require "test_helper"
class SendWelcomeEmailJobTest < ActiveJob::TestCase
setup do
@user = users(:one)
end
test "sends welcome email" do
assert_enqueued_emails 1 do
SendWelcomeEmailJob.perform_now(@user.id)
end
end
test "enqueues on default queue" do
assert_enqueued_with(job: SendWelcomeEmailJob, queue: "default") do
SendWelcomeEmailJob.perform_later(@user.id)
end
end
end
Testing Enqueueing
require "test_helper"
class ProcessPaymentJobTest < ActiveJob::TestCase
test "enqueues the job with correct arguments" do
order = orders(:one)
assert_enqueued_with(job: ProcessPaymentJob, args: [order.id]) do
ProcessPaymentJob.perform_later(order.id)
end
end
test "enqueues on critical queue" do
assert_enqueued_with(job: ProcessPaymentJob, queue: "critical") do
ProcessPaymentJob.perform_later(orders(:one).id)
end
end
end
Testing Job Side Effects
require "test_helper"
class CleanupOldRecordsJobTest < ActiveJob::TestCase
test "deletes old sessions" do
old_session = sessions(:old)
old_session.update!(created_at: 31.days.ago)
recent_session = sessions(:one)
CleanupOldRecordsJob.perform_now
assert_not Session.exists?(old_session.id)
assert Session.exists?(recent_session.id)
end
end
Testing with perform_enqueued_jobs
require "test_helper"
class SignupFlowTest < ActionDispatch::IntegrationTest
test "signup sends welcome email" do
perform_enqueued_jobs do
post signups_path, params: {
signup: { email: "new@example.com", name: "Test" }
}
end
assert_emails 1
end
end
Running Solid Queue
bin/rails solid_queue:start
web: bin/rails server
worker: bin/rails solid_queue:start
Monitoring
Mission Control (Web UI)
gem "mission_control-jobs"
mount MissionControl::Jobs::Engine, at: "/jobs"
Console Queries
SolidQueue::Job.where(finished_at: nil).count
SolidQueue::FailedExecution.count
SolidQueue::FailedExecution.last.retry
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: |
Checklist