Solid Queue : Tâches de Fond Sans Redis


Bienvenue dans ce troisième article dans la série sur le Triumvirat "Solid" dans Rails 8 ! Aujourd'hui on parle de Solid Queue. La SOLUTION dont vous ne pourrez plus vous passer pour créer des tâches de fond simplement.


  1. Envoyer un email automatique ? ✅ Check.
  2. Dès qu’un utilisateur s’inscrit, Solid Queue prend le relais pour envoyer un email de bienvenue sans ralentir la réponse HTTP.
  3. Générer un PDF en arrière-plan ? ✅ Check.
  4. Besoin d’un rapport ou d’une facture ? Le job est lancé en tâche de fond, et le fichier est prêt quand l’utilisateur revient.
  5. Synchroniser des données avec une API externe ? ✅ Check.
  6. Une mise à jour de profil déclenche un job qui envoie les infos à un CRM ou un outil marketing.
  7. Nettoyer les anciennes sessions ou fichiers temporaires ? ✅ Check.
  8. Un job planifié chaque nuit garde votre base propre et légère.
  9. Importer un fichier CSV volumineux ? ✅ Check.
  10. L’utilisateur téléverse un fichier ? Solid Queue le traite ligne par ligne sans faire exploser le serveur.
  11. Planifier des rappels ou des relances ? ✅ Check.
  12. Grâce à l’ordonnancement des jobs, vous pouvez envoyer un rappel 24h avant un événement.


Vous pouvez faire tellement de chose que ça en donne le tournis ! De l'installation à la configuration jusqu'à son utilisaiton. Solid Queue n'aura plus de secret pour vous !



N'hésitez pas à parcourir mes autres articles sur le sujet Triumvirat "Solid" qui n'aura plus de secret pour vous !


  1. "Solid Cache" : Ne plus dépendre de Redis pour le cache dans Rails
  2. "Solid Cable" : Des websockets et du temps réel simplement et sans prise de tête
  3. "Solid Queue" : Des taches de fonds à la sauce Rails (vous êtes ici)
  4. Le triumvirat appliqué à un projet complet


Accrochez ça va être du solide :)

Focus sur Solid Queue

Qu'est-ce qu'un Job de Fond ?

Les jobs de fond (ou background jobs) sont des tâches qui prennent du temps et qu'on ne veut pas exécuter pendant la requête HTTP de l'utilisateur. Sinon on bloquerait l'application ou on aurait en retour une erreur "timeout" nous expliquant que le serveur qu'on a interroger à mis trop de temps à répondre.


Exemples classiques :

  1. Envoyer un email de bienvenue après inscription
  2. Générer un rapport PDF complexe
  3. Traiter une image uploadée
  4. Synchroniser des données avec une API externe
  5. Nettoyer des anciennes données


Sans background jobs :

Utilisateur clique "S'inscrire"
Serveur crée le compte (100ms)
Serveur envoie l'email (2000ms) ← L'utilisateur attend...
Réponse "Inscription réussie !" (2100ms total)


Avec background jobs :

Utilisateur clique "S'inscrire"
Serveur crée le compte (100ms)
Serveur enqueue le job email (10ms)
Réponse "Inscription réussie !" (110ms total)
(Plus tard) Le job envoie l'email en fond

Solid Queue : Comment ça fonctionne ?

Solid Queue est un système de gestion de jobs basé sur votre base de données, utilisant Active Job (l'interface standard de Rails pour les background jobs).


L'architecture

Solid Queue crée plusieurs tables dans votre base :

# Tables principales
solid_queue_jobs # Jobs en attente
solid_queue_ready_executions # Jobs prêts à être exécutés
solid_queue_claimed_executions # Jobs en cours d'exécution
solid_queue_blocked_executions # Jobs bloqués (concurrence)
solid_queue_failed_executions # Jobs échoués
solid_queue_scheduled_executions # Jobs schedulés pour plus tard
solid_queue_recurring_tasks # Tâches récurrentes (cron-like)

Les composants clés

1. Workers (Travailleurs) Les workers sont les processus qui exécutent réellement vos jobs. Ils interrogent régulièrement la table solid_queue_ready_executions pour trouver du travail.

2. Dispatchers (Répartiteurs) Les dispatchers déplacent les jobs de solid_queue_scheduled_executions vers solid_queue_ready_executions quand leur moment d'exécution arrive.

3. Scheduler (Planificateur) Le scheduler gère les tâches récurrentes (comme les cron jobs).

La magie : FOR UPDATE SKIP LOCKED

Solid Queue utilise une fonctionnalité SQL avancée appelée FOR UPDATE SKIP LOCKED qui permet à plusieurs workers de lire la même table simultanément sans se bloquer mutuellement.

-- Requête simplifiée d'un worker
SELECT * FROM solid_queue_ready_executions
WHERE queue_name = 'default'
ORDER BY priority DESC, created_at ASC
LIMIT 1
FOR UPDATE SKIP LOCKED;


Ce qui se passe :

  1. Worker 1 prend le job A et le verrouille
  2. Worker 2 essaie de prendre un job
  3. Au lieu d'attendre que Worker 1 finisse (LOCKED), il saute le job A (SKIP LOCKED) et prend le job B
  4. Résultat : zero blocage, performance maximale !

Installation et Configuration

Sur Rails 8

Solid Queue est activé par défaut ! Vérifiez simplement votre configuration.


# Vérifier que tout est en place
rails db:migrate

Configuration dans config/solid_queue.yml

default: &default
dispatchers:
- polling_interval: 1 # Vérifier les jobs schedulés chaque seconde
batch_size: 500 # Traiter 500 jobs à la fois
workers:
- queues: "*" # Traiter toutes les queues
threads: 3 # 3 threads par processus
processes: 1 # Nombre de processus
polling_interval: 0.1 # Vérifier les nouveaux jobs toutes les 0.1s
production:
<<: *default
workers:
# Queue critique : haute priorité
- queues: critical
threads: 5
processes: 2
polling_interval: 0.1
# Queue par défaut : ressources modérées
- queues: default
threads: 3
processes: 3
polling_interval: 1
# Queue emails : beaucoup de threads (I/O bound)
- queues: mailers
threads: 10
processes: 2
polling_interval: 2
# Queue rapports : peu de threads (CPU intensive)
- queues: reports
threads: 2
processes: 1
polling_interval: 5
development:
<<: *default

Exemples Pratiques

Exemple 1 : Job Simple - Envoyer un Email

# app/jobs/welcome_email_job.rb
class WelcomeEmailJob < ApplicationJob
queue_as :mailers # Utiliser la queue "mailers"
def perform(user_id)
user = User.find(user_id)
UserMailer.welcome_email(user).deliver_now
end
end


# app/controllers/users_controller.rb
class UsersController < ApplicationController
def create
@user = User.new(user_params)
if @user.save
# Enqueuer le job pour envoi immédiat
WelcomeEmailJob.perform_later(@user.id)
redirect_to root_path, notice: "Compte créé ! Vérifiez vos emails."
else
render :new
end
end
end

Exemple 2 : Job Schedulé - Envoyer plus tard

# Envoyer dans 1 heure
ReminderEmailJob.set(wait: 1.hour).perform_later(user.id)
# Envoyer à une heure précise
ReportJob.set(wait_until: Date.tomorrow.noon).perform_later
# Avec une priorité spécifique (plus le nombre est bas, plus c'est prioritaire)
UrgentJob.set(priority: 0).perform_later(data)

Exemple 3 : Tâches Récurrentes (Cron Jobs)

# config/solid_queue.yml
production:
recurring_tasks:
# Nettoyer les données tous les jours à 2h du matin
cleanup:
class: CleanupJob
schedule: "0 2 * * *" # Format cron
# Envoyer une newsletter tous les lundis à 9h
weekly_newsletter:
class: NewsletterJob
args: ["weekly"]
schedule: "0 9 * * 1" # 9h tous les lundis
# Générer des rapports toutes les heures
hourly_reports:
class: GenerateReportsJob
schedule: "0 * * * *" # Toutes les heures

Exemple 4 : Job avec Gestion d'Erreurs

# app/jobs/api_sync_job.rb
class ApiSyncJob < ApplicationJob
queue_as :default
# Réessayer jusqu'à 5 fois avec délai exponentiel
retry_on StandardError, wait: :exponentially_longer, attempts: 5
# Mais ne pas réessayer si c'est une erreur d'authentification
discard_on AuthenticationError
def perform(resource_id)
resource = Resource.find(resource_id)
# Appel API qui peut échouer
ExternalApiService.sync(resource)
# Log du succès
Rails.logger.info "Resource #{resource_id} synced successfully"
rescue => e
# Log de l'erreur
Rails.logger.error "Failed to sync resource #{resource_id}: #{e.message}"
raise # Re-raise pour que le retry fonctionne
end
end

Exemple 5 : Contrôle de Concurrence

Solid Queue permet de limiter combien de jobs d'un certain type peuvent s'exécuter simultanément.

# app/jobs/report_generator_job.rb
class ReportGeneratorJob < ApplicationJob
queue_as :reports
# Maximum 3 jobs de génération de rapport en même temps
limits_concurrency to: 3, key: -> { "report_generation" }
def perform(report_id)
report = Report.find(report_id)
# Génération coûteuse en CPU
report.generate_pdf
report.generate_excel
report.generate_charts
report.mark_as_complete!
end
end


Cas d'usage : Si vous avez 10 rapports à générer, seulement 3 s'exécuteront en parallèle. Les 7 autres attendront qu'un slot se libère.

Exemple 6 : Job avec Progress

# app/jobs/bulk_import_job.rb
class BulkImportJob < ApplicationJob
queue_as :default
def perform(file_path, user_id)
user = User.find(user_id)
lines = File.readlines(file_path)
total = lines.count
lines.each_with_index do |line, index|
# Importer la ligne
import_line(line)
# Mettre à jour la progression
progress = ((index + 1).to_f / total * 100).round(2)
# Notifier l'utilisateur via WebSocket
ActionCable.server.broadcast(
"import_#{user_id}",
{ progress: progress, completed: index + 1, total: total }
)
end
end
private
def import_line(line)
# Logique d'import
end
end

Démarrer le Worker

En développement

Rails 8 utilise Puma avec un plugin Solid Queue :

# config/puma.rb
plugin :solid_queue if ENV["SOLID_QUEUE_IN_PUMA"] || Rails.env.development?

Avec bin/dev, tout démarre automatiquement !

En production


Option 1 : Processus séparé

# Démarrer le worker Solid Queue en plus de votre application
bin/jobs


Option 2 : Avec Kamal/Docker

# config/deploy.yml
servers:
web:
- 192.168.1.100
job:
hosts:
- 192.168.1.101
cmd: bin/jobs # Serveur dédié aux jobs


Option 3 : Systemd

# /etc/systemd/system/solid-queue.service
[Unit]
Description=Solid Queue Worker
After=network.target
[Service]
Type=simple
User=deploy
WorkingDirectory=/var/www/mon_app
ExecStart=/usr/local/bin/bundle exec bin/jobs
Restart=always
[Install]
WantedBy=multi-user.target

Mission Control : L'Interface d'Administration

Rails 8 peut utiliser Mission Control - Jobs, une interface web pour monitorer vos jobs. C'est hyper pratique donc je vous conseille vivement de l'installer si vous utilisez Solid Queue.

Installation

# Gemfile
gem "mission_control-jobs"
bundle install
rails mission_control:jobs:install

Configuration

# config/routes.rb
Rails.application.routes.draw do
mount MissionControl::Jobs::Engine, at: "/jobs"
end


Mission Control vient avec une authentification HTTP basique. La configuration se fait via les crédentials de Rails :

mission_control:
http_basic_auth_user: dev
http_basic_auth_password: secret


Mais si vous utilisez un système d'authentification comme Devise, autant "nester" la route de Mission Control avec comme par exemple avec un rôle d'administrateur :

# config/routes.rb
Rails.application.routes.draw do
authenticate :user, ->(u) { u.admin? } do
mount MissionControl::Jobs::Engine, at: '/jobs'
end
end


Il faut bien entendu que votre modèle utilisateur possède une colonne booléen "admin" et une méthode admin?

class User < ApplicationRecord
def admin?
admin
end
end


Accédez à http://localhost:3000/jobs pour voir :

  1. Jobs en cours
  2. Jobs échoués avec stack traces
  3. Historique des jobs
  4. Statistiques de performance
  5. Possibilité de réessayer les jobs échoués

Monitoring et Debugging

Vérifier les jobs dans la console

# Console Rails
rails c
# Voir tous les jobs en attente
SolidQueue::Job.count
# Voir les jobs par queue
SolidQueue::Job.group(:queue_name).count
# => {"default"=>5, "mailers"=>12, "reports"=>2}
# Voir les jobs échoués
SolidQueue::FailedExecution.count
# Voir le dernier job échoué
failed = SolidQueue::FailedExecution.last
failed.error # Message d'erreur
failed.exception_executions # Stack trace
# Réessayer un job échoué
failed.retry
# Purger les vieux jobs terminés (gardés 7 jours par défaut)
SolidQueue::Job.finished_before(7.days.ago).delete_all

Logs utiles

# config/environments/production.rb
config.active_job.logger = Logger.new(Rails.root.join('log', 'jobs.log'))

Solid Queue vs Autres Solutions

Solution
Vitesse
Features
Setup
Dépendances
Solid Queue
Bonne
Riches
Très simple
Aucune
Sidekiq
Excellente
Très riches
Simple
Redis
GoodJob
Bonne
Riches
Simple
Aucune
Resque
Moyenne
Basiques
Simple
Redis
DelayedJob
Lente
Basiques
Très simple
Aucune


Utilisez Solid Queue si :

  1. Vous démarrez avec Rails 8
  2. Vous voulez zéro dépendance externe
  3. Vos besoins sont standards (80% des apps)
  4. Vous avez quelques milliers de jobs par heure

Passez à Sidekiq si :

  1. Vous avez des dizaines de milliers de jobs par heure
  2. Vous avez besoin de fonctionnalités avancées, comme des jobs async
  3. La performance est critique

Considérez GoodJob si :

  1. Vous voulez les avantages de Solid Queue
  2. Mais avec une interface d'admin encore plus poussée


Conclusion : Solid Queue, la simplicité au service de la performance


Solid Queue marque une évolution naturelle dans l’écosystème Rails : une solution de gestion de jobs asynchrones qui mise sur la simplicité, la robustesse et l’intégration native. Fini les dépendances externes comme Redis ou Sidekiq : avec une base SQL et une configuration minimaliste, Solid Queue permet de gérer les tâches en arrière-plan avec élégance et efficacité.

Que ce soit pour envoyer des emails, générer des rapports, ou orchestrer des workflows complexes, Solid Queue s’impose comme un allié fiable pour les développeurs Rails. Et avec Rails 8 qui l’intègre en standard, il est temps de repenser la manière dont on conçoit les traitements asynchrones.


🚀 Moins de friction, plus de productivité. Solid Queue, c’est le futur du background processing dans Rails.