- Rails 8 elimina la dependencia de Redis de su stack por defecto y cambia a procesar todo el trabajo sobre una base de datos relacional (RDB) mediante SolidQueue, SolidCache y SolidCable
- Redis es rápido y estable, pero también introduce complejidad operativa en configuración, seguridad, gestión de clústeres, respaldos, etc.
- SolidQueue usa la función
FOR UPDATE SKIP LOCKED de PostgreSQL para implementar procesamiento paralelo sin contención
- Ofrece gratis funciones de Redis + Sidekiq de pago, como trabajos periódicos, control de concurrencia y panel de monitoreo (Mission Control)
- Para la mayoría de las aplicaciones Rails, SolidQueue es suficiente, y solo en algunos casos que requieren procesamiento ultrarrápido o en tiempo real conviene mantener Redis
El costo oculto de Redis
- Además del simple costo de hosting, Redis implica una carga continua de administración, como instalación, mantenimiento, configuración de seguridad y gestión de clústeres HA
- Se necesita conexión de red y configuración de firewall entre Rails y Redis, autenticación del cliente y orquestación de procesos de Sidekiq
- Cuando ocurre una falla, hay que depurar al mismo tiempo Redis y el RDBMS, y también se requiere una estrategia de respaldos duplicada
- En cambio, con un stack Rails sin Redis, se puede simplificar la operación administrando solo PostgreSQL
Cómo funciona SolidQueue
- Usa
FOR UPDATE SKIP LOCKED de PostgreSQL para que varios workers tomen trabajos al mismo tiempo sin conflictos de bloqueo (lock contention)
- Estructura principal de tablas
solid_queue_jobs: almacena los metadatos de los trabajos
solid_queue_scheduled_executions: espera de trabajos programados
solid_queue_ready_executions: cola de trabajos listos para ejecutar
- Los procesos de worker, dispatcher, scheduler y supervisor hacen polling periódico sobre distintas tablas y colaboran entre sí
- Gracias al diseño MVCC y al autovacuum de PostgreSQL, puede manejar de forma estable grandes volúmenes de inserciones y eliminaciones
Programación de tareas repetitivas
- SolidQueue incluye por defecto trabajos recurrentes estilo cron, configurados en el archivo
config/recurring.yml
- El scheduler pone en cola los trabajos cuando llega su hora de ejecución y programa automáticamente la siguiente ejecución
- Usa la librería Fugit para interpretar horarios en lenguaje natural y Concurrent::ScheduledTask para crear hilos
- Adopta el enfoque de programación determinista de GoodJob, para mantener el calendario incluso tras reinicios del proceso
Función de control de concurrencia
- SolidQueue usa el patrón de semáforo POSIX para admitir límites de ejecución concurrente por unidad de trabajo
- Ejemplo: con
limits_concurrency to: 1, key: ->(user) { user.id }, solo se ejecuta 1 trabajo por usuario
- Se puede definir el vencimiento del semáforo (
duration) para evitar conflictos y deadlocks
- Tablas relacionadas
solid_queue_semaphores: seguimiento de límites de concurrencia
solid_queue_blocked_executions: almacena trabajos en espera
Monitoreo con Mission Control
- Mission Control Jobs es un panel gratuito y open source para Rails 8, que puede montarse fácilmente en la ruta
/jobs
- Funciones principales
- Estado de colas en tiempo real, seguimiento de trabajos fallidos y control de reintentos/descartes
- Visualización de la línea de tiempo de trabajos programados y recurrentes
- Gráficas de throughput y métricas por cola
- Permite consultas basadas en SQL, por lo que se puede analizar directamente en la base de datos sin herramientas adicionales
Migración de Sidekiq a SolidQueue
- Paso 1: configurar
config.active_job.queue_adapter = :solid_queue
- Paso 2: ejecutar
bundle add solid_queue, luego rails solid_queue:install y db:migrate
- Paso 3: convertir el cron schedule de
sidekiq.yml a recurring.yml
- Paso 4: agregar
jobs: bundle exec rake solid_queue:start al Procfile
- Paso 5: eliminar los gems relacionados con Redis y Sidekiq
- El código existente de ActiveJob sigue funcionando sin cambios
Casos en los que Redis sigue siendo necesario
- Procesamiento sostenido de miles de trabajos por segundo
- Sistemas en tiempo real donde la latencia menor a 1 ms es indispensable
- Necesidad de estructuras pub/sub complejas o de rate limiting y operaciones de contador sofisticadas
- Como ejemplo, Shopify opera con 833 solicitudes por segundo y 1,172 procesos worker usando infraestructura Redis
Guía de implementación real
- Al crear una nueva app en Rails 8, SolidQueue, SolidCache y SolidCable se configuran automáticamente
- Se recomienda definir una conexión de base de datos separada para la cola en
config/database.yml
- Agregar autenticación a Mission Control y montar la ruta
/jobs
- Agregar
jobs: bundle exec rake solid_queue:start a Procfile.dev y ejecutar todo con bin/dev
- Tras crear trabajos de prueba, se puede verificar su estado en Mission Control
Problemas frecuentes y soluciones
- También se puede usar una configuración de base de datos única, pero con menos flexibilidad operativa
- En Mission Control en producción, es indispensable agregar autenticación
- El valor por defecto del intervalo de polling es 1 segundo para trabajos programados y 0.2 segundos para trabajos inmediatos, adecuado para la mayoría de las apps
- Si se usa ActionCable/Turbo Streams, hay que configurar
SolidCable con una conexión de base de datos separada
Escalabilidad y rendimiento
- SolidQueue puede escalar lo suficiente para la mayoría de las apps Rails
- Basado en PostgreSQL, permite procesar 200–300 trabajos por segundo, y 37signals procesa 20 millones de trabajos al día sin Redis
- Tabla comparativa
| Ítem |
Redis + Sidekiq |
SolidQueue |
| Complejidad de configuración |
Requiere servicio aparte |
Usa DB integrada |
| Lenguaje de consulta |
Comandos de Redis |
SQL |
| Monitoreo |
Panel aparte |
Mission Control |
| Escenarios de falla |
6 o más |
2 |
| Throughput |
Miles por segundo |
200–300 por segundo |
| Ideal para |
99.9% de apps |
95% de apps |
Conclusión
- Redis y Sidekiq son tecnologías excelentes, pero para la mayoría de las aplicaciones Rails generan una complejidad y un costo excesivos
- SolidQueue hace posible simplificar la operación, reducir costos y mejorar el mantenimiento con una base de datos única
- En la era de Rails 8, se recomienda migrar a SolidQueue como opción predeterminada
2 comentarios
Redis está bueno, pero.
Opiniones en Hacker News
Creo que todo autor de código abierto tiene derecho a controlar el alcance de su proyecto
Pero nuestro equipo se arrepiente de haber cambiado de good_job a SolidQueue
Basecamp está centrado en MySQL, así que no acepta consultas específicas del motor RDBMS. Si ves los issues en GitHub, se nota que solo se enfocan en el rendimiento con MySQL
Además, todavía no tiene soporte para trabajos por lotes (PR relacionada)
En JOINs complejos, MySQL muchas veces arma mal el plan de ejecución, así que yo fuerzo el orden con STRAIGHT_JOIN. Es una forma de cubrirse a futuro
Estoy comparando ambos como candidatos para migrar desde resque. GoodJob no es compatible con el modo transaction de pgbouncer por sus funciones exclusivas de pg
Es molesto porque requiere persistencia de sesión, pero la mejora de rendimiento no significa mucho en la mayoría de las escalas
Aun así, el modelo de desarrollo y la legibilidad del código de GoodJob inspiran mucha más confianza
Si puede simplificar el entorno de producción, siempre es algo bueno
En Rails, creo que el escenario ideal es una arquitectura que permita cambiar fácilmente a Redis
Estaría bien empezar con SolidQueue y, si luego aparece un límite de escalabilidad, poder migrar a Redis
La mayoría de las apps Rails no tienen tanto tráfico, así que mantener dos sistemas más bien agrega complejidad
Claro, hay apps que dependen de una implementación específica de cola, pero en general basta con cambiar la configuración
Quisiera saber si también usa snapshots para evitar que el log crezca demasiado, y si esto funciona también en modo distribuido
Sobre todo cuando la creación de trabajos ocurre junto con otros cambios en la BD, porque ahí se pierde esa garantía
Redis tenía la ventaja de ser un almacenamiento de estado ligero e independiente en este aspecto
SolidQueue no parece dejar clara esa separación (riverqueue.com)
Probé SolidQueue en mi proyecto personal
Mi conclusión fue que, si no tienes problemas con Sidekiq, no hay razón para cambiar
Solo vale la pena considerarlo si quieres eliminar la infraestructura de Redis
Para un proyecto nuevo, GoodJob me parece más maduro y con mejor comunidad
La UI de SolidQueue es demasiado básica, y eso me resultó incómodo. Como los índices no estaban optimizados, cuando crecían los datos la página se quedaba congelada
También hay que considerar que usar un RDBMS agrega el costo de gestionar el pool de conexiones
Para quienes se preocupan por la escalabilidad, si miran el benchmark de Oban en Elixir,
procesa un millón de trabajos por minuto en un solo nodo. La mayoría de las apps manejan mucho menos que eso
Mete 5000 trabajos por lote de una sola vez, así que el TPS real es como de 200
Si insertas trabajos individuales sin lotes, la carga de transacciones SQL se vuelve mucho mayor
Nosotros ya guardábamos los trabajos en la BD desde antes de SolidQueue
La ventaja es que puedes hacer un snapshot del estado de producción tal cual hacia el entorno de desarrollo
Pero dejamos el rate limiter en Redis para evitar carga extra en la BD
El límite de una cola basada en BD está en los payloads grandes
Si metes JSON grandes en la cola, se vuelve ineficiente por el overhead de escritura en la BD
Redis (Sidekiq) es mucho más rápido en esos casos
SolidQueue+SQLite puede servir simplemente para pasar la primary key
Pero si varios workers hacen polling sobre la misma BD, el cuello de botella aparece rápido
Creo que es mejor dejar los datos grandes en un almacenamiento externo como S3 y pasar solo la referencia
Me pregunto si existe algún material que resuma resultados de benchmarks
En SolidQueue se menciona SKIP LOCKED, pero mantener una transacción abierta para un trabajo de 15 minutos es arriesgado
Las transacciones abiertas durante mucho tiempo destruyen el rendimiento de la BD y además son vulnerables a cortes de red
Ese tipo de estructura puede llevar a un antipatrón. Después vi que parece usar un esquema de lease
Me identifico con la filosofía de Postgres for everything
Me parece bien unificar todo en PostgreSQL por simplicidad
No sé bien cómo rebatir esa analogía
Me cuesta ver por qué valdría la pena usar Redis si eso solo aumenta la complejidad
“Un negocio donde importa una latencia menor a 1 ms”; ¿o sea que hacen HFT con Rails?
Postgres se va a comer el mundo