# 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. - Published: 2026-09-12 - Updated: 2026-09-12 - Author: Anthony Fillion-Maillet - Tags: Rails, Sidekiq, Good Job, Background Jobs, Active Job - Reading time: 12 min --- 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](https://github.com/sidekiq/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](https://github.com/jhawthorn/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. ## Good Job 4.x : batches et unicité natives PostgreSQL [Good Job](https://github.com/bensheldon/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](https://github.com/rails/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.x | Good Job 4.x | Solid Queue 1.x | |----------------|-------------|--------------|------------------| | Backend | Redis 7.2+ | PostgreSQL | PostgreSQL/MySQL/SQLite | | Batches | Pro/Enterprise | Inclus | Prévu | | Jobs uniques | Enterprise | Inclus | Manuel via verrous | | Planification cron | Enterprise | Inclus | Via solid_queue_cron | | Interface Web | Incluse | Incluse | Minimale (Mission Control) | | Plafond de débit | 100k+ jobs/min | 10k jobs/min | 10k jobs/min | | Intégration Puma | Oui (8.x) | Non | Oui | | Licence | LGPL + Commercial | MIT | MIT | ## 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 ``` ## 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é --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/fr/blog/ruby-on-rails/rails-background-jobs-sidekiq-good-job-comparison