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
- Web UI available via Mission Control
Quick Start
bundle add solid_queue
bin/rails solid_queue:install
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
Naming Convention
Use _later for async, _now for synchronous:
# Async (queued via Solid Queue) - preferred
SendWelcomeEmailJob.perform_later(user.id)
# Synchronous (runs immediately, skips queue) - use sparingly
SendWelcomeEmailJob.perform_now(user.id)
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 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
# Lower number = higher priority (default is 0)
def priority
-10
end
def perform(notification_id)
notification = Notification.find(notification_id)
notification.deliver!
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)
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
session_cleanup:
class: SessionCleanupJob
schedule: every day at 3am
Testing Jobs
Job Test Template
# test/jobs/send_welcome_email_job_test.rb
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
# test/jobs/process_payment_job_test.rb
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
# test/jobs/cleanup_old_records_job_test.rb
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
# test/integration/signup_flow_test.rb
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
# Development
bin/rails solid_queue:start
# Production (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
SolidQueue::Job.where(finished_at: nil).count # Pending
SolidQueue::FailedExecution.count # Failed
SolidQueue::FailedExecution.last.retry # Retry
SolidQueue::Job.where("finished_at < ?", 1.week.ago).delete_all # Cleanup
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
- Solid Queue gem installed
- Migrations run
- Queue adapter configured
- Jobs use
perform_later(notperform_now) - Error handling with
retry_on/discard_on - Recurring jobs configured
- Job tests written
- Mission Control mounted (optional)
- All tests GREEN
Weekly Installs
2
Repository
dchuk/rails_ai_agentsFirst Seen
7 days ago
Security Audits
Installed on
opencode2
gemini-cli2
antigravity2
claude-code2
windsurf2
codex2