# Jobs en segundo plano en Rails 2026: Sidekiq vs Good Job y preguntas de entrevista > Comparación completa entre Sidekiq, Good Job y Solid Queue para tareas asíncronas en Rails, con las preguntas técnicas más frecuentes en entrevistas. - 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 --- Los jobs en segundo plano en Rails han evolucionado significativamente con Rails 8.x y la introducción de Solid Queue como backend predeterminado de Active Job. La elección entre Sidekiq, Good Job y Solid Queue depende de los requisitos de rendimiento, las restricciones de infraestructura y las funcionalidades que cada proyecto necesita. > **Guía de decisión rápida** > > Para nuevos proyectos Rails 8, se recomienda comenzar con Solid Queue. Sidekiq es la opción adecuada para cargas de trabajo de alto rendimiento que superan los 100,000 jobs por día o cuando la latencia a nivel de Redis es crítica. Good Job es ideal para jobs respaldados por PostgreSQL con soporte nativo de batches y restricciones de unicidad. ## Cómo Active Job unifica el procesamiento en segundo plano Active Job proporciona una interfaz estandarizada para declarar, encolar y ejecutar trabajo en segundo plano en Rails. El framework abstrae las diferencias entre los backends de cola, permitiendo que el código sea portable entre Sidekiq, Good Job, Solid Queue o cualquier otro adaptador. ```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 ``` Los callbacks `retry_on` y `discard_on` manejan fallos transitorios y errores permanentes sin código específico del backend. La directiva `queue_as` asigna prioridad, que cada backend interpreta según su propia estrategia de procesamiento de colas. ## Sidekiq 8.x: rendimiento respaldado por Redis [Sidekiq](https://github.com/sidekiq/sidekiq) sigue siendo la referencia de rendimiento para jobs en segundo plano en Rails. La serie 8.x requiere Ruby 3.2+, Rails 7.0+ y Redis 7.2+ (Valkey y Dragonfly funcionan como reemplazos directos desde que Redis cambió su licencia). ```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 introdujo tres cambios principales: una interfaz Web reescrita desde cero que reduce el CSS de 160KB a 16KB y el tiempo promedio de renderizado de 55ms a 3ms, perfilado de jobs con [Vernier](https://github.com/jhawthorn/vernier) accesible desde una nueva pestaña Profiles, y retención de métricas hasta por 72 horas. ### Cuándo elegir Sidekiq Sidekiq es apropiado para cargas de trabajo donde el rendimiento sostenido, la latencia de recuperación de jobs inferior al milisegundo, o las características empresariales como limitación de tasa y jobs únicos justifican ejecutar Redis. Las Capsules, introducidas en la versión 7.0 y ahora estándar, permiten que un proceso Sidekiq ejecute múltiples pools de hilos aislados con su propia concurrencia y colas. ```ruby # config/sidekiq.yml :concurrency: 10 :queues: - [critical, 3] - [default, 2] - [low, 1] # Capsules para procesamiento aislado :capsules: imports: :concurrency: 2 :queues: - imports ``` La API de embedding también permite ejecutar Sidekiq dentro del proceso Puma para aplicaciones pequeñas, similar a cómo funciona el plugin Puma de Solid Queue. ## Good Job 4.x: batches y unicidad nativas con PostgreSQL [Good Job](https://github.com/bensheldon/good_job) utiliza LISTEN/NOTIFY de PostgreSQL para la recuperación de jobs y bloqueos advisory para garantizar ejecución única. La versión 4.19.x (actual en septiembre 2026) soporta Rails 6.1+ y 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 incluye batches y jobs únicos en la versión open-source, funcionalidades que Sidekiq reserva para licencias de pago. La llamada `perform_later` inserta una fila en la tabla `good_jobs` dentro de la misma transacción de base de datos que el código llamante, por lo que si la transacción se revierte, el job nunca se crea. ### Batches sin licencia ```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 # Encolar el batch GoodJob::Batch.enqueue do |batch| BatchImportJob.perform_later(batch, file_ids: [1, 2, 3]) end ``` ### Jobs únicos para idempotencia ```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 restricción `perform_limit: 1` asegura que solo se ejecute una sincronización por almacén en cualquier momento, previniendo trabajo duplicado cuando el mismo job se encola múltiples veces. ## Solid Queue: el predeterminado de Rails 8 [Solid Queue](https://github.com/rails/solid_queue) almacena jobs en la base de datos de la aplicación usando `FOR UPDATE SKIP LOCKED`, una característica de PostgreSQL y MySQL que convierte una tabla regular en una cola de jobs. Las nuevas aplicaciones Rails 8 vienen con Solid Queue configurado por defecto. ```ruby # config/solid_queue.yml production: dispatchers: - polling_interval: 1 batch_size: 500 workers: - queues: "*" threads: 5 polling_interval: 0.1 ``` Solid Queue puede ejecutarse dentro del proceso del servidor web Puma con `SOLID_QUEUE_IN_PUMA=true`, lo que simplifica el despliegue para aplicaciones que no necesitan procesos worker dedicados. Los jobs persisten en las tablas de la base de datos, sobreviviendo a reinicios de procesos y despliegues sin dependencias externas. ## Tabla comparativa de funcionalidades | Funcionalidad | Sidekiq 8.x | Good Job 4.x | Solid Queue 1.x | |---------------|-------------|--------------|------------------| | Backend | Redis 7.2+ | PostgreSQL | PostgreSQL/MySQL/SQLite | | Batches | Pro/Enterprise | Incluido | Planificado | | Jobs únicos | Enterprise | Incluido | Manual via bloqueos | | Programación cron | Enterprise | Incluido | Via solid_queue_cron | | Interfaz Web | Incluida | Incluida | Mínima (Mission Control) | | Techo de rendimiento | 100k+ jobs/min | 10k jobs/min | 10k jobs/min | | Embeber en Puma | Sí (8.x) | No | Sí | | Licencia | LGPL + Comercial | MIT | MIT | ## Preguntas de entrevista sobre jobs en segundo plano en Rails Las entrevistas técnicas frecuentemente evalúan la comprensión de la confiabilidad de jobs, estrategias de retry y decisiones arquitectónicas. A continuación se presentan preguntas que distinguen a los candidatos experimentados. ### P1: ¿Cuál es la diferencia entre `retry_on` y la lógica de retry específica del backend? `retry_on` es un callback de Active Job que funciona con todos los backends. Captura excepciones específicas, aplica una estrategia de espera (`:polynomially_longer` realiza backoff exponencial) y vuelve a encolar el job. La lógica de retry específica del backend, como `sidekiq_retry_in` de Sidekiq, solo funciona con ese backend y puede sobrescribir el comportamiento de Active Job. ```ruby # Retry de 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 ``` ### P2: ¿Qué sucede con un job si la transacción de base de datos que lo encoló se revierte? Con colas respaldadas por base de datos (Good Job, Solid Queue), la fila del job es parte de la transacción, por lo que se revierte junto con todo lo demás. Con colas respaldadas por Redis (Sidekiq), el job ya está en Redis antes de que la transacción se complete, creando jobs huérfanos. La solución es usar callbacks `after_commit`: ```ruby # Patrón correcto para Sidekiq class Order < ApplicationRecord after_commit :enqueue_confirmation, on: :create private def enqueue_confirmation OrderConfirmationJob.perform_later(id) end end ``` ### P3: ¿Cuándo Good Job superaría a Sidekiq? Good Job supera a Sidekiq cuando el costo operacional de Redis excede sus beneficios de rendimiento. Para aplicaciones que procesan menos de 50,000 jobs por día, las colas respaldadas por PostgreSQL evitan la complejidad de infraestructura, monitoreo y failover de un cluster Redis separado. Good Job también proporciona batches y jobs únicos sin costos de licencia, lo cual importa para equipos que necesitan estas funcionalidades con presupuesto limitado. ### P4: Explicar la idempotencia de jobs y por qué es importante para los retries Idempotencia significa que ejecutar el mismo job múltiples veces produce el mismo resultado que ejecutarlo una vez. Los jobs deben ser idempotentes porque las fallas de red, crashes de procesos o manejo de timeouts pueden causar ejecuciones duplicadas. Las técnicas incluyen restricciones de unicidad en la base de datos, verificar resultados existentes antes de procesar y usar claves de idempotencia externas para APIs de pago. ```ruby # Procesamiento de pago idempotente 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 ``` ### P5: ¿Cómo manejar jobs que deben ejecutarse exactamente una vez en workers distribuidos? Los bloqueos distribuidos previenen la ejecución concurrente. Good Job usa bloqueos advisory de PostgreSQL. Sidekiq Enterprise ofrece jobs únicos. Para implementación manual, usar Redis `SETNX` o 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 ``` ## Elegir el backend correcto para jobs en segundo plano en Rails - Comenzar con Solid Queue para nuevos proyectos Rails 8 que aún no tienen Redis en el stack - Cambiar a Sidekiq cuando el volumen de jobs exceda 50,000 por día o cuando la latencia de recuperación inferior a 100ms sea importante - Elegir Good Job para despliegues solo PostgreSQL que necesiten batches, jobs únicos o programación cron sin costos de licencia - Usar callbacks `after_commit` al encolar jobs con Sidekiq para evitar jobs huérfanos en caso de rollback de transacción - Hacer cada job idempotente porque los retries y el procesamiento distribuido pueden causar ejecuciones duplicadas - Para entrevistas, demostrar comprensión del encolado transaccional de jobs, estrategias de retry y patrones de bloqueo distribuido --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/es/blog/ruby-on-rails/rails-background-jobs-sidekiq-good-job-comparison