Jobs en arrière-plan Rails en 2026 : Sidekiq vs Good Job et questions d'entretien

Comparaison approfondie entre Sidekiq, Good Job et Solid Queue pour les tâches asynchrones Rails, avec les questions techniques posées en entretien.

Comparaison des solutions de jobs en arrière-plan Rails : Sidekiq, Good Job et Solid Queue

Les jobs en arrière-plan Rails ont considérablement évolué avec Rails 8.x et l'introduction de Solid Queue comme backend Active Job par défaut. Le choix entre Sidekiq, Good Job et Solid Queue dépend des exigences de débit, des contraintes d'infrastructure et des fonctionnalités nécessaires à chaque projet.

Guide de décision rapide

Pour les nouveaux projets Rails 8, Solid Queue constitue le point de départ recommandé. Sidekiq s'impose pour les charges de travail à haut débit dépassant 100 000 jobs par jour ou lorsque la latence au niveau Redis est critique. Good Job convient aux jobs stockés dans PostgreSQL avec support natif des batches et des contraintes d'unicité.

Comment Active Job unifie le traitement en arrière-plan

Active Job fournit une interface standardisée pour déclarer, mettre en file d'attente et exécuter des tâches en arrière-plan dans Rails. Ce framework abstrait les différences entre les backends de file d'attente, permettant au code de rester portable entre Sidekiq, Good Job, Solid Queue ou tout autre adaptateur.

ruby
# app/jobs/payment_processor_job.rb
class PaymentProcessorJob < ApplicationJob
  queue_as :critical
  retry_on Stripe::RateLimitError, wait: :polynomially_longer, attempts: 5
  discard_on Stripe::InvalidRequestError

  def perform(order_id)
    order = Order.find(order_id)
    PaymentService.process(order)
    OrderMailer.confirmation(order).deliver_later
  end
end

Les callbacks retry_on et discard_on gèrent les échecs transitoires et les erreurs permanentes sans code spécifique au backend. La directive queue_as attribue une priorité que chaque backend interprète selon sa propre stratégie de traitement des files d'attente.

Sidekiq 8.x : débit optimisé avec Redis

Sidekiq reste la référence en matière de performance pour les jobs en arrière-plan Rails. La série 8.x requiert Ruby 3.2+, Rails 7.0+ et Redis 7.2+ (Valkey et Dragonfly fonctionnent comme remplacements directs depuis le changement de licence Redis).

ruby
# config/initializers/sidekiq.rb
Sidekiq.configure_server do |config|
  config.redis = { url: ENV.fetch('REDIS_URL') }
  config.concurrency = 10
end

Sidekiq.configure_client do |config|
  config.redis = { url: ENV.fetch('REDIS_URL') }
end

Sidekiq 8 a introduit trois changements majeurs : une interface Web entièrement réécrite réduisant le CSS de 160 Ko à 16 Ko et le temps de rendu moyen de 55 ms à 3 ms, le profilage des jobs avec Vernier accessible via un nouvel onglet Profiles, et la conservation des métriques jusqu'à 72 heures.

Quand choisir Sidekiq

Sidekiq convient aux charges de travail nécessitant un débit soutenu, une latence de récupération des jobs inférieure à la milliseconde, ou des fonctionnalités entreprise comme la limitation de débit et les jobs uniques justifiant l'utilisation de Redis. Les Capsules, introduites en version 7.0 et désormais standard, permettent à un processus Sidekiq d'exécuter plusieurs pools de threads isolés avec leur propre concurrence et leurs propres files d'attente.

ruby
# config/sidekiq.yml
:concurrency: 10
:queues:
  - [critical, 3]
  - [default, 2]
  - [low, 1]

# Capsules pour traitement isolé
:capsules:
  imports:
    :concurrency: 2
    :queues:
      - imports

L'API d'embedding permet également d'exécuter Sidekiq à l'intérieur du processus Puma pour les petites applications, de manière similaire au plugin Puma de Solid Queue.

Prêt à réussir tes entretiens Ruby on Rails ?

Entraîne-toi avec nos simulateurs interactifs, fiches express et tests techniques.

Good Job 4.x : batches et unicité natives PostgreSQL

Good Job utilise LISTEN/NOTIFY de PostgreSQL pour la récupération des jobs et les verrous consultatifs pour garantir une exécution unique. La version 4.19.x (actuelle en septembre 2026) supporte Rails 6.1+ et Ruby 3.0+.

ruby
# config/application.rb
config.active_job.queue_adapter = :good_job

# config/initializers/good_job.rb
Rails.application.configure do
  config.good_job.execution_mode = :async
  config.good_job.max_threads = 5
  config.good_job.poll_interval = 5
  config.good_job.shutdown_timeout = 25
end

Good Job inclut les batches et les jobs uniques dans la version open-source, des fonctionnalités que Sidekiq réserve aux licences payantes. L'appel perform_later insère une ligne dans la table good_jobs au sein de la même transaction de base de données que le code appelant, donc si la transaction est annulée, le job n'est jamais créé.

Batches sans licence

ruby
# app/jobs/batch_import_job.rb
class BatchImportJob < ApplicationJob
  def perform(batch, params)
    batch.on(:finish, ImportCompletionJob, params)
    
    params[:file_ids].each do |file_id|
      batch.add do
        ProcessFileJob.perform_later(file_id)
      end
    end
  end
end

# Mise en file d'attente du batch
GoodJob::Batch.enqueue do |batch|
  BatchImportJob.perform_later(batch, file_ids: [1, 2, 3])
end

Jobs uniques pour l'idempotence

ruby
# app/jobs/sync_inventory_job.rb
class SyncInventoryJob < ApplicationJob
  include GoodJob::ActiveJobExtensions::UniqueJob

  good_job_control_concurrency_with(
    perform_limit: 1,
    key: -> { "sync_inventory_#{arguments.first}" },
    duration: 1.hour
  )

  def perform(warehouse_id)
    InventorySync.run(warehouse_id)
  end
end

La contrainte perform_limit: 1 garantit qu'une seule synchronisation s'exécute par entrepôt à tout moment, évitant le travail en double lorsque le même job est mis en file d'attente plusieurs fois.

Solid Queue : le défaut de Rails 8

Solid Queue stocke les jobs dans la base de données de l'application en utilisant FOR UPDATE SKIP LOCKED, une fonctionnalité PostgreSQL et MySQL qui transforme une table ordinaire en file d'attente de jobs. Les nouvelles applications Rails 8 sont livrées avec Solid Queue configuré par défaut.

ruby
# config/solid_queue.yml
production:
  dispatchers:
    - polling_interval: 1
      batch_size: 500
  workers:
    - queues: "*"
      threads: 5
      polling_interval: 0.1

Solid Queue peut s'exécuter à l'intérieur du processus du serveur web Puma avec SOLID_QUEUE_IN_PUMA=true, ce qui simplifie le déploiement pour les applications qui n'ont pas besoin de processus worker dédiés. Les jobs persistent dans les tables de la base de données, survivant aux redémarrages de processus et aux déploiements sans dépendances externes.

Tableau comparatif des fonctionnalités

FonctionnalitéSidekiq 8.xGood Job 4.xSolid Queue 1.x
BackendRedis 7.2+PostgreSQLPostgreSQL/MySQL/SQLite
BatchesPro/EnterpriseInclusPrévu
Jobs uniquesEnterpriseInclusManuel via verrous
Planification cronEnterpriseInclusVia solid_queue_cron
Interface WebIncluseIncluseMinimale (Mission Control)
Plafond de débit100k+ jobs/min10k jobs/min10k jobs/min
Intégration PumaOui (8.x)NonOui
LicenceLGPL + CommercialMITMIT

Questions d'entretien sur les jobs en arrière-plan Rails

Les entretiens techniques sondent fréquemment la compréhension de la fiabilité des jobs, des stratégies de retry et des décisions architecturales. Voici des questions qui distinguent les candidats expérimentés.

Q1 : Quelle différence entre retry_on et la logique de retry spécifique au backend ?

retry_on est un callback Active Job qui fonctionne avec tous les backends. Il intercepte des exceptions spécifiques, applique une stratégie d'attente (:polynomially_longer effectue un backoff exponentiel) et remet le job en file d'attente. La logique de retry spécifique au backend, comme sidekiq_retry_in de Sidekiq, ne fonctionne qu'avec ce backend et peut surcharger le comportement d'Active Job.

ruby
# Retry Active Job (portable)
class ApiSyncJob < ApplicationJob
  retry_on Net::OpenTimeout, wait: :polynomially_longer, attempts: 8
  
  def perform(record_id)
    ExternalApi.sync(record_id)
  end
end

Q2 : Que devient un job si la transaction de base de données qui l'a mis en file d'attente est annulée ?

Avec les files d'attente basées sur la base de données (Good Job, Solid Queue), la ligne du job fait partie de la transaction, donc elle est annulée avec tout le reste. Avec les files d'attente basées sur Redis (Sidekiq), le job est déjà dans Redis avant la fin de la transaction, créant des jobs orphelins. La solution est d'utiliser les callbacks after_commit :

ruby
# Pattern correct pour Sidekiq
class Order < ApplicationRecord
  after_commit :enqueue_confirmation, on: :create

  private

  def enqueue_confirmation
    OrderConfirmationJob.perform_later(id)
  end
end

Q3 : Quand Good Job surpasserait-il Sidekiq ?

Good Job surpasse Sidekiq lorsque le coût opérationnel de Redis dépasse ses avantages en termes de débit. Pour les applications traitant moins de 50 000 jobs par jour, les files d'attente basées sur PostgreSQL évitent la complexité d'infrastructure, de monitoring et de basculement d'un cluster Redis séparé. Good Job fournit également les batches et les jobs uniques sans frais de licence, ce qui compte pour les équipes ayant besoin de ces fonctionnalités avec un budget limité.

Q4 : Expliquer l'idempotence des jobs et son importance pour les retries

L'idempotence signifie que l'exécution du même job plusieurs fois produit le même résultat qu'une seule exécution. Les jobs doivent être idempotents car les pannes réseau, les crashs de processus ou la gestion des timeouts peuvent causer des exécutions en double. Les techniques incluent les contraintes d'unicité dans la base de données, la vérification des résultats existants avant traitement et l'utilisation de clés d'idempotence externes pour les APIs de paiement.

ruby
# Traitement de paiement idempotent
def perform(order_id, idempotency_key)
  return if Payment.exists?(idempotency_key: idempotency_key)
  
  Payment.create!(
    order_id: order_id,
    idempotency_key: idempotency_key,
    amount: Order.find(order_id).total
  )
end

Q5 : Comment gérer les jobs qui doivent s'exécuter exactement une fois sur des workers distribués ?

Les verrous distribués empêchent l'exécution concurrente. Good Job utilise les verrous consultatifs PostgreSQL. Sidekiq Enterprise propose les jobs uniques. Pour une implémentation manuelle, utiliser Redis SETNX ou PostgreSQL pg_try_advisory_lock :

ruby
class ExclusiveJob < ApplicationJob
  def perform(resource_id)
    lock_key = "exclusive_job:#{resource_id}"
    
    ActiveRecord::Base.connection.execute(
      "SELECT pg_try_advisory_lock(hashtext('#{lock_key}'))"
    ).first['pg_try_advisory_lock'] or return
    
    begin
      process_resource(resource_id)
    ensure
      ActiveRecord::Base.connection.execute(
        "SELECT pg_advisory_unlock(hashtext('#{lock_key}'))"
      )
    end
  end
end

Passe à la pratique !

Teste tes connaissances avec nos simulateurs d'entretien et tests techniques.

Choisir le bon backend pour les jobs en arrière-plan Rails

  • Commencer avec Solid Queue pour les nouveaux projets Rails 8 qui n'ont pas encore Redis dans leur stack
  • Passer à Sidekiq quand le volume de jobs dépasse 50 000 par jour ou que la latence de récupération inférieure à 100 ms compte
  • Choisir Good Job pour les déploiements PostgreSQL uniquement nécessitant batches, jobs uniques ou planification cron sans coûts de licence
  • Utiliser les callbacks after_commit lors de la mise en file d'attente de jobs avec Sidekiq pour éviter les jobs orphelins en cas d'annulation de transaction
  • Rendre chaque job idempotent car les retries et le traitement distribué peuvent causer des exécutions en double
  • Pour les entretiens, démontrer la compréhension de la mise en file d'attente transactionnelle des jobs, des stratégies de retry et des patterns de verrouillage distribué
Défi du jour

Tu saurais repérer le bug en Ruby on Rails ?

Un vrai bout de code, un bug caché, une tentative par jour. Sans compte pour essayer.

Anthony Fillion-Maillet

Écrit par

Anthony Fillion-Maillet

Fondateur de SharpSkill

Développeur fullstack depuis plus de 10 ans. Il dirige SharpSkill et répond de tout ce qui y est publié.

Mis à jour le 12 septembre 2026

Tags

#Rails
#Sidekiq
#Good Job
#Background Jobs
#Active Job

Partager

Articles similaires