Introduction
Running background jobs in production requires operational practices beyond code: deploying without job loss, scaling workers, and handling backlogs.
Key Concepts
- Graceful Shutdown: Workers finish current jobs before stopping.
- Backlog Management: Strategies for when jobs pile up faster than workers process them.
- Deploy Safety: Ensuring job compatibility during deployments.
Real World Context
During a deploy, old workers stop and new ones start. If a worker is mid-job when stopped, what happens? Graceful shutdown lets it finish.
Deep Dive
Graceful Shutdown
Solid Queue and Sidekiq both support graceful shutdown. When receiving SIGTERM:
ruby# Solid Queue: workers finish current jobs, then exit # Configure shutdown timeout: # bin/jobs start --timeout 30 # Sidekiq: same behavior # SIDEKIQ_TIMEOUT=25 bundle exec sidekiq
Scaling Workers
bash# Scale Solid Queue workers via environment variable JOB_CONCURRENCY=4 bin/jobs start # Or adjust config/queue.yml: # workers: # - queues: "*" # threads: 5 # processes: 4
Handling Backlogs
ruby# Check if a backlog is building ready_count = SolidQueue::ReadyExecution.count if ready_count > 10_000 Rails.logger.warn "Job backlog: #{ready_count} jobs waiting" # Consider: add workers, pause non-critical enqueuing, or prioritize end
Deploy-Safe Jobs
ruby# If you rename a job class, old jobs in the queue will fail. # Keep the old class as an alias during transition: class OldJobName < ApplicationJob def perform(*args) NewJobName.perform_now(*args) end end
Common Pitfalls
- Deploying breaking changes to job arguments — Old jobs in the queue have old arguments.
- Killing workers with SIGKILL instead of SIGTERM — SIGKILL skips graceful shutdown.
Best Practices
- Always use SIGTERM for worker shutdown — Gives workers time to finish.
- Make job changes backward-compatible — Old and new versions may coexist during deploys.
Summary
- Use SIGTERM for graceful worker shutdown.
- Scale workers via processes and threads configuration.
- Monitor backlogs and scale workers proactively.
- Make job changes backward-compatible during deploys.
Code Examples
ruby
# Monitor backlog and alert
ready = SolidQueue::ReadyExecution.count
oldest = SolidQueue::ReadyExecution.order(:created_at).first
latency = oldest ? (Time.current - oldest.created_at).round : 0
puts "Backlog: #{ready} jobs, oldest waiting #{latency}s"