Solid Queue es un backend de colas basado en base de datos para Active Job, diseñado teniendo en cuenta la simplicidad y el rendimiento.
Además de la puesta en cola y el procesamiento de trabajos regulares, Solid Queue admite trabajos retrasados, controles de concurrencia, trabajos recurrentes, colas en pausa, prioridades numéricas por trabajo, prioridades por orden de cola y puesta en cola masiva ( enqueue_all
para perform_all_later
de Active Job).
Solid Queue se puede utilizar con bases de datos SQL como MySQL, PostgreSQL o SQLite, y aprovecha la cláusula FOR UPDATE SKIP LOCKED
, si está disponible, para evitar bloqueos y esperas de bloqueos al sondear trabajos. Se basa en Active Job para reintentos, descartes, manejo de errores, serialización o retrasos, y es compatible con el subproceso múltiple de Ruby on Rails.
Solid Queue está configurado de forma predeterminada en las nuevas aplicaciones Rails 8. Pero si estás ejecutando una versión anterior, puedes agregarla manualmente siguiendo estos pasos:
bundle add solid_queue
bin/rails solid_queue:install
Esto configurará Solid Queue como el backend de producción de trabajos activos, creará los archivos de configuración config/queue.yml
y config/recurring.yml
y creará db/queue_schema.rb
. También creará un contenedor ejecutable bin/jobs
que puede usar para iniciar Solid Queue.
Una vez que haya hecho eso, deberá agregar la configuración para la base de datos de la cola en config/database.yml
. Si estás usando SQLite, se verá así:
production :
primary :
<< : *default
database : storage/production.sqlite3
queue :
<< : *default
database : storage/production_queue.sqlite3
migrations_paths : db/queue_migrate
...o si estás usando MySQL/PostgreSQL/Trilogy:
production :
primary : &primary_production
<< : *default
database : app_production
username : app
password : <%= ENV["APP_DATABASE_PASSWORD"] %>
queue :
<< : *primary_production
database : app_production_queue
migrations_paths : db/queue_migrate
Nota: Llamar bin/rails solid_queue:install
agregará automáticamente config.solid_queue.connects_to = { database: { writing: :queue } }
a config/environments/production.rb
, por lo que no se necesita ninguna configuración adicional allí (aunque debe asegurarse (¡Que utilice el nombre queue
en database.yml
para que coincida!). Pero si desea utilizar Solid Queue en un entorno diferente (como prueba o incluso desarrollo), deberá agregar manualmente esa línea config.solid_queue.connects_to
al archivo de entorno respectivo. Y, como siempre, asegúrese de que el nombre que está usando para la base de datos en config/database.yml
coincida con el nombre que usa en config.solid_queue.connects_to
.
Luego ejecute db:prepare
en producción para asegurarse de que se cree la base de datos y se cargue el esquema.
Ahora está listo para comenzar a procesar trabajos ejecutando bin/jobs
en el servidor que está haciendo el trabajo. Esto comenzará a procesar trabajos en todas las colas usando la configuración predeterminada. Consulte a continuación para obtener más información sobre cómo configurar Solid Queue.
Para proyectos pequeños, puede ejecutar Solid Queue en la misma máquina que su servidor web. Cuando esté listo para escalar, Solid Queue admite el escalado horizontal listo para usar. Puede ejecutar Solid Queue en un servidor separado de su servidor web, o incluso ejecutar bin/jobs
en varias máquinas al mismo tiempo. Dependiendo de la configuración, puede designar algunas máquinas para que ejecuten solo despachadores o solo trabajadores. Consulte la sección de configuración para obtener más detalles al respecto.
Nota : los cambios futuros en el esquema se realizarán en forma de migraciones periódicas.
Se recomienda ejecutar Solid Queue en una base de datos independiente, pero también es posible utilizar una única base de datos tanto para la aplicación como para la cola. Simplemente sigue estos pasos:
db/queue_schema.rb
en una migración normal y elimine db/queue_schema.rb
config.solid_queue.connects_to
de production.rb
bin/jobs
No tendrá varias bases de datos, por lo que database.yml
no necesita tener una base de datos primaria y de cola.
Si planea adoptar Solid Queue de forma incremental cambiando un trabajo a la vez, puede hacerlo dejando config.active_job.queue_adapter
configurado en su antiguo backend y luego configurando queue_adapter
directamente en los trabajos que está moviendo:
# app/jobs/my_job.rb
class MyJob < ApplicationJob
self . queue_adapter = :solid_queue
# ...
end
Solid Queue fue diseñado para el mayor rendimiento cuando se usa con MySQL 8+ o PostgreSQL 9.5+, ya que admiten FOR UPDATE SKIP LOCKED
. Puede usarlo con versiones anteriores, pero en ese caso, podría encontrarse con esperas de bloqueo si ejecuta varios trabajadores para la misma cola. También puedes usarlo con SQLite en aplicaciones más pequeñas.
Disponemos de varios tipos de actores en Solid Queue:
solid_queue_ready_executions
.solid_queue_scheduled_executions
a la tabla solid_queue_ready_executions
para que los trabajadores puedan recogerlos. Además de eso, realizan algunos trabajos de mantenimiento relacionados con los controles de concurrencia.El supervisor de Solid Queue bifurcará un proceso separado para cada trabajador/despachador/programador supervisado.
De forma predeterminada, Solid Queue intentará encontrar su configuración en config/queue.yml
, pero puede establecer una ruta diferente usando la variable de entorno SOLID_QUEUE_CONFIG
o usando la opción -c/--config_file
con bin/jobs
, así:
bin/jobs -c config/calendar.yml
Así es como se ve esta configuración:
production :
dispatchers :
- polling_interval : 1
batch_size : 500
concurrency_maintenance_interval : 300
workers :
- queues : " * "
threads : 3
polling_interval : 2
- queues : [ real_time, background ]
threads : 5
polling_interval : 0.1
processes : 3
Todo es opcional. Si no se proporciona ninguna configuración, Solid Queue se ejecutará con un despachador y un trabajador con la configuración predeterminada. Si desea ejecutar solo despachadores o trabajadores, solo necesita incluir esa sección en la configuración. Por ejemplo, con la siguiente configuración:
production :
dispatchers :
- polling_interval : 1
batch_size : 500
concurrency_maintenance_interval : 300
el supervisor ejecutará 1 despachador y ningún trabajador.
A continuación se ofrece una descripción general de las diferentes opciones:
polling_interval
: el intervalo de tiempo en segundos que los trabajadores y despachadores esperarán antes de buscar más trabajos. Este tiempo predeterminado es 1
segundo para los despachadores y 0.1
segundos para los trabajadores.
batch_size
: el despachador enviará trabajos en lotes de este tamaño. El valor predeterminado es 500.
concurrency_maintenance_interval
: el intervalo de tiempo en segundos que el despachador esperará antes de verificar si hay trabajos bloqueados que se puedan desbloquear. Lea más sobre los controles de simultaneidad para obtener más información sobre esta configuración. El valor predeterminado es 600
segundos.
queues
: la lista de colas de las que los trabajadores elegirán trabajos. Puede usar *
para indicar todas las colas (que también es el valor predeterminado y el comportamiento que obtendrá si lo omite). Puede proporcionar una única cola o una lista de colas como una matriz. Los trabajos se sondearán de esas colas en orden, por lo que, por ejemplo, con [ real_time, background ]
, no se tomarán trabajos del background
a menos que no haya más trabajos esperando en real_time
. También puede proporcionar un prefijo con un comodín para hacer coincidir las colas que comienzan con un prefijo. Por ejemplo:
staging :
workers :
- queues : staging*
threads : 3
polling_interval : 5
Esto creará un trabajador que buscará trabajos de todas las colas comenzando con staging
. El comodín *
sólo se permite solo o al final del nombre de una cola; no puede especificar nombres de cola como *_some_queue
. Estos serán ignorados.
Finalmente, puede combinar prefijos con nombres exactos, como [ staging*, background ]
, y el comportamiento con respecto al orden será el mismo que con solo nombres exactos.
Consulte las secciones siguientes sobre cómo se comporta el orden de las colas combinado con las prioridades y cómo la forma en que especifica las colas por trabajador podría afectar el rendimiento.
threads
: este es el tamaño máximo del grupo de subprocesos que cada trabajador tendrá para ejecutar trabajos. Cada trabajador obtendrá esta cantidad de trabajos de su(s) cola(s), como máximo, y los publicará en el grupo de subprocesos para su ejecución. Por defecto, esto es 3
. Sólo los trabajadores tienen esta configuración.
processes
: esta es la cantidad de procesos de trabajo que serán bifurcados por el supervisor con la configuración dada. De forma predeterminada, esto es 1
, solo un proceso. Esta configuración es útil si desea dedicar más de un núcleo de CPU a una cola o colas con la misma configuración. Sólo los trabajadores tienen esta configuración.
concurrency_maintenance
: si el despachador realizará el trabajo de mantenimiento de concurrencia. Esto es true
de forma predeterminada y es útil si no utiliza ningún control de concurrencia y desea deshabilitarlo o si ejecuta varios despachadores y desea que algunos de ellos simplemente distribuyan trabajos sin hacer nada más.
Como se mencionó anteriormente, si especifica una lista de colas para un trabajador, estas se sondearán en el orden indicado, como para la lista real_time,background
, no se tomarán trabajos del background
a menos que no haya más trabajos esperando en real_time
.
Active Job también admite prioridades enteras positivas al poner en cola trabajos. En Solid Queue, cuanto menor sea el valor, mayor será la prioridad. El valor predeterminado es 0
.
Esto es útil cuando ejecuta trabajos con diferente importancia o urgencia en la misma cola. Dentro de la misma cola, los trabajos se seleccionarán en orden de prioridad, pero en una lista de colas, el orden de la cola tiene prioridad, por lo que en el ejemplo anterior con real_time,background
, los trabajos en la cola en real_time
se seleccionarán antes que los trabajos en background
cola, incluso si aquellos en la cola background
tienen una prioridad más alta (valor más pequeño) establecida.
Recomendamos no mezclar el orden de la cola con las prioridades, sino elegir uno u otro, ya que eso hará que el orden de ejecución del trabajo sea más sencillo para usted.
Para mantener el rendimiento del sondeo y garantizar que siempre se utilice un índice de cobertura, Solid Queue solo realiza dos tipos de consultas de sondeo:
-- No filtering by queue
SELECT job_id
FROM solid_queue_ready_executions
ORDER BY priority ASC , job_id ASC
LIMIT ?
FOR UPDATE SKIP LOCKED;
-- Filtering by a single queue
SELECT job_id
FROM solid_queue_ready_executions
WHERE queue_name = ?
ORDER BY priority ASC , job_id ASC
LIMIT ?
FOR UPDATE SKIP LOCKED;
El primero (sin filtrado por cola) se utiliza cuando especifica
queues : *
y no hay colas en pausa, ya que queremos apuntar a todas las colas.
En otros casos, necesitamos tener una lista de colas para filtrar, en orden, porque solo podemos filtrar por una única cola a la vez para asegurarnos de usar un índice para ordenar. Esto significa que si especifica sus colas como:
queues : beta*
Primero necesitaremos obtener una lista de todas las colas existentes que coincidan con ese prefijo, con una consulta que se vería así:
SELECT DISTINCT (queue_name)
FROM solid_queue_ready_executions
WHERE queue_name LIKE ' beta% ' ;
Este tipo de consulta DISTINCT
en una columna que es la columna más a la izquierda en un índice se puede realizar muy rápido en MySQL gracias a una técnica llamada Loose Index Scan. PostgreSQL y SQLite, sin embargo, no implementan esta técnica, lo que significa que si su tabla solid_queue_ready_executions
es muy grande porque sus colas son muy profundas, esta consulta se volverá lenta. Normalmente su tabla solid_queue_ready_executions
será pequeña, pero puede suceder.
De manera similar al uso de prefijos, sucederá lo mismo si tiene colas en pausa, porque necesitamos obtener una lista de todas las colas con una consulta como
SELECT DISTINCT (queue_name)
FROM solid_queue_ready_executions
y luego eliminar los pausados. La pausa en general debería ser algo poco frecuente, utilizada en circunstancias especiales y por un período corto de tiempo. Si ya no desea procesar trabajos de una cola, la mejor manera de hacerlo es eliminarlo de su lista de colas.
En resumen, si desea garantizar un rendimiento óptimo en las encuestas , la mejor manera de hacerlo es especificar siempre nombres exactos para ellos y no pausar ninguna cola.
Haz esto:
queues : background, backend
en lugar de esto:
queues : back*
Los trabajadores de Solid Queue utilizan un grupo de subprocesos para ejecutar el trabajo en varios subprocesos, configurable mediante el parámetro de threads
anterior. Además de esto, el paralelismo se puede lograr mediante múltiples procesos en una máquina (configurables mediante diferentes trabajadores o el parámetro processes
anterior) o mediante escalamiento horizontal.
El supervisor es el encargado de gestionar estos procesos y responde a las siguientes señales:
TERM
, INT
: inicia la terminación elegante. El supervisor enviará una señal TERM
a sus procesos supervisados y esperará hasta el tiempo SolidQueue.shutdown_timeout
hasta que finalicen. Si para entonces aún hay procesos supervisados, les enviará una señal QUIT
para indicar que deben salir.QUIT
: inicia la terminación inmediata. El supervisor enviará una señal QUIT
a sus procesos supervisados, provocando que salgan inmediatamente. Al recibir una señal QUIT
, si los trabajadores todavía tienen trabajos en curso, estos se devolverán a la cola cuando se cancelen los procesos.
Si los procesos no tienen posibilidad de limpiarse antes de salir (por ejemplo, si alguien tira de un cable en algún lugar), los procesos que los ejecutan pueden seguir reclamando trabajos en curso. Los procesos envían latidos y el supervisor verifica y elimina los procesos con latidos vencidos, lo que liberará cualquier trabajo reclamado a sus colas. Puedes configurar tanto la frecuencia de los latidos como el umbral para considerar un proceso muerto. Consulte la sección siguiente para esto.
Puede configurar la base de datos utilizada por Solid Queue a través de la opción config.solid_queue.connects_to
en los archivos de configuración config/application.rb
o config/environments/production.rb
. De forma predeterminada, se utiliza una única base de datos para escritura y lectura llamada queue
para que coincida con la configuración de la base de datos que configuró durante la instalación.
Aquí se pueden utilizar todas las opciones disponibles para Active Record para múltiples bases de datos.
En la cola Solid, puede conectarse a dos puntos diferentes en la vida del supervisor:
start
: después de que el supervisor haya terminado de arrancar y justo antes de bifurcar a los trabajadores y despachadores.stop
: después de recibir una señal ( TERM
, INT
o QUIT
) y justo antes de iniciar el apagado elegante o inmediato.Y en dos puntos diferentes de la vida de un trabajador:
worker_start
: después de que el trabajador haya terminado de iniciar y justo antes de que comience el ciclo de sondeo.worker_stop
: después de recibir una señal ( TERM
, INT
o QUIT
) y justo antes de iniciar el apagado elegante o inmediato (¡que es simplemente exit!
).Puede utilizar los siguientes métodos con un bloque para hacer esto:
SolidQueue . on_start
SolidQueue . on_stop
SolidQueue . on_worker_start
SolidQueue . on_worker_stop
Por ejemplo:
SolidQueue . on_start { start_metrics_server }
SolidQueue . on_stop { stop_metrics_server }
Se pueden llamar varias veces para agregar varios enlaces, pero esto debe ocurrir antes de que se inicie Solid Queue. Un inicializador sería un buen lugar para hacer esto.
Nota : La configuración de esta sección debe establecerse en su config/application.rb
o en la configuración de su entorno de esta manera: config.solid_queue.silence_polling = true
Hay varias configuraciones que controlan cómo funciona Solid Queue y que también puedes configurar:
logger
: el registrador que desea que utilice Solid Queue. El valor predeterminado es el registrador de aplicaciones.
app_executor
: el ejecutor de Rails utilizado para envolver operaciones asincrónicas, el valor predeterminado es el ejecutor de la aplicación
on_thread_error
: lambda/Proc personalizado para llamar cuando hay un error dentro de un subproceso de Solid Queue que toma la excepción planteada como argumento. El valor predeterminado es
-> ( exception ) { Rails . error . report ( exception , handled : false ) }
Esto no se utiliza para errores generados durante la ejecución de un trabajo . Los errores que ocurren en los trabajos son manejados por retry_on
o discard_on
de Active Job y, en última instancia, darán como resultado trabajos fallidos. Esto es para errores que ocurren dentro de Solid Queue.
use_skip_locked
: si se debe utilizar FOR UPDATE SKIP LOCKED
al realizar lecturas de bloqueo. Esto se detectará automáticamente en el futuro y, por ahora, solo deberá configurarlo en false
si su base de datos no lo admite. Para MySQL, serían versiones <8, y para PostgreSQL, versiones <9.5. Si usa SQLite, esto no tiene ningún efecto, ya que las escrituras son secuenciales.
process_heartbeat_interval
: el intervalo de latido que seguirán todos los procesos; el valor predeterminado es 60 segundos.
process_alive_threshold
: cuánto tiempo esperar hasta que un proceso se considere muerto después de su último latido; el valor predeterminado es 5 minutos.
shutdown_timeout
: tiempo que esperará el supervisor desde que envió la señal TERM
a sus procesos supervisados antes de enviarles una versión QUIT
solicitando la terminación inmediata; el valor predeterminado es 5 segundos.
silence_polling
: si se deben silenciar los registros de Active Record emitidos al sondear tanto para los trabajadores como para los despachadores; el valor predeterminado es true
.
supervisor_pidfile
: ruta a un archivo pid que el supervisor creará al iniciar para evitar ejecutar más de un supervisor en el mismo host, o en caso de que desee usarlo para una verificación de estado. Es nil
por defecto.
preserve_finished_jobs
: si se deben mantener los trabajos terminados en la tabla solid_queue_jobs
; el valor predeterminado es true
.
clear_finished_jobs_after
: período para mantener los trabajos terminados, en caso de que preserve_finished_jobs
sea verdadero; el valor predeterminado es 1 día. Nota: En este momento, no existe una limpieza automática de los trabajos terminados. Deberá hacer esto invocando periódicamente SolidQueue::Job.clear_finished_in_batches
, pero esto sucederá automáticamente en un futuro cercano.
default_concurrency_control_period
: el valor que se utilizará como predeterminado para el parámetro duration
en los controles de simultaneidad. El valor predeterminado es 3 minutos.
Solid Queue generará un SolidQueue::Job::EnqueueError
por cualquier error de registro activo que ocurra al poner en cola un trabajo. La razón para no generar ActiveJob::EnqueueError
es que este es manejado por Active Job, lo que hace que perform_later
devuelva false
y establezca job.enqueue_error
, entregando el trabajo a un bloque que debe pasar a perform_later
. Esto funciona muy bien para sus propios trabajos, pero hace que las fallas sean muy difíciles de manejar para trabajos puestos en cola por Rails u otras gemas, como Turbo::Streams::BroadcastJob
o ActiveStorage::AnalyzeJob
, porque no controla la llamada a perform_later
en esos casos.
En el caso de tareas recurrentes, si se genera dicho error al poner en cola el trabajo correspondiente a la tarea, se manejará y registrará, pero no aparecerá.
Solid Queue amplía Active Job con controles de concurrencia, que le permiten limitar la cantidad de trabajos de un determinado tipo o con determinados argumentos que se pueden ejecutar al mismo tiempo. Cuando se limitan de esta manera, se bloqueará la ejecución de los trabajos y permanecerán bloqueados hasta que otro trabajo finalice y los desbloquee, o después de que transcurra el tiempo de vencimiento establecido ( duración del límite de concurrencia). Los trabajos nunca se descartan ni se pierden, sólo se bloquean.
class MyJob < ApplicationJob
limits_concurrency to : max_concurrent_executions , key : -> ( arg1 , arg2 , ** ) { ... } , duration : max_interval_to_guarantee_concurrency_limit , group : concurrency_group
# ...
key
es el único parámetro requerido y puede ser un símbolo, una cadena o un proceso que recibe los argumentos del trabajo como parámetros y se usará para identificar los trabajos que deben limitarse juntos. Si el proceso devuelve un registro activo, la clave se creará a partir de su nombre de clase e id
.to
es 1
por defecto.duration
está establecida en SolidQueue.default_concurrency_control_period
de forma predeterminada, que a su vez tiene un valor predeterminado de 3 minutes
, pero que también puedes configurar.group
se utiliza para controlar la simultaneidad de diferentes clases de trabajo juntas. El valor predeterminado es el nombre de la clase de trabajo. Cuando un trabajo incluye estos controles, nos aseguraremos de que, como máximo, la cantidad de trabajos (indicados to
) que generan la misma key
se realicen simultáneamente, y esta garantía tendrá una duration
para cada trabajo en cola. Tenga en cuenta que no hay garantía sobre el orden de ejecución , solo sobre las tareas que se realizan al mismo tiempo (superpuestas).
Los límites de concurrencia utilizan el concepto de semáforos al poner en cola y funcionan de la siguiente manera: cuando un trabajo se pone en cola, verificamos si especifica controles de concurrencia. Si es así, verificamos el semáforo para buscar la clave de simultaneidad calculada. Si el semáforo está abierto lo reclamamos y configuramos el trabajo como listo . Listo significa que los trabajadores pueden recogerlo para su ejecución. Cuando el trabajo termina de ejecutarse (ya sea con éxito o sin éxito, lo que resulta en una ejecución fallida), le indicamos al semáforo e intentamos desbloquear el siguiente trabajo con la misma clave, si corresponde. Desbloquear el siguiente trabajo no significa ejecutar ese trabajo de inmediato, sino pasarlo de bloqueado a listo . Dado que puede suceder algo que impida que el primer trabajo libere el semáforo y desbloquee el siguiente trabajo (por ejemplo, que alguien desconecte la máquina donde se está ejecutando el trabajador), tenemos la duration
como un mecanismo de seguridad. Los trabajos que han estado bloqueados por más tiempo son candidatos para ser liberados, pero solo tantos como lo permitan las reglas de concurrencia, ya que cada uno necesitaría pasar por la verificación de baile del semáforo. Esto significa que la duration
no se trata realmente del trabajo que está en cola o en ejecución, sino de los trabajos que están bloqueados en espera.
Por ejemplo:
class DeliverAnnouncementToContactJob < ApplicationJob
limits_concurrency to : 2 , key : -> ( contact ) { contact . account } , duration : 5 . minutes
def perform ( contact )
# ...
Donde contact
y account
son registros ActiveRecord
. En este caso, nos aseguraremos de que como máximo dos trabajos del tipo DeliverAnnouncementToContact
para la misma cuenta se ejecuten simultáneamente. Si, por algún motivo, uno de esos trabajos tarda más de 5 minutos o no libera su bloqueo de concurrencia (señala el semáforo) dentro de los 5 minutos posteriores a su adquisición, un nuevo trabajo con la misma clave podría obtener el bloqueo.
Veamos otro ejemplo usando group
:
class Box :: MovePostingsByContactToDesignatedBoxJob < ApplicationJob
limits_concurrency key : -> ( contact ) { contact } , duration : 15 . minutes , group : "ContactActions"
def perform ( contact )
# ...
class Bundle :: RebundlePostingsJob < ApplicationJob
limits_concurrency key : -> ( bundle ) { bundle . contact } , duration : 15 . minutes , group : "ContactActions"
def perform ( bundle )
# ...
En este caso, si tenemos un trabajo Box::MovePostingsByContactToDesignatedBoxJob
en cola para un registro de contacto con ID 123
y otro Bundle::RebundlePostingsJob
en cola simultáneamente para un registro de paquete que hace referencia al contacto 123
, solo uno de ellos podrá continuar. El otro quedará bloqueado hasta que termine el primero (o pasen 15 minutos, lo que ocurra primero).
Tenga en cuenta que la configuración duration
depende indirectamente del valor de concurrency_maintenance_interval
que configuró para su(s) despachador(es), ya que esa sería la frecuencia con la que se verifican y desbloquean los trabajos bloqueados. En general, debe establecer duration
de manera que todos sus trabajos finalicen muy por debajo de esa duración y pensar en la tarea de mantenimiento de concurrencia como una medida de seguridad en caso de que algo salga mal.
Los trabajos se desbloquean en orden de prioridad, pero el orden de la cola no se tiene en cuenta para desbloquear trabajos. Eso significa que si tiene un grupo de trabajos que comparten un grupo de simultaneidad pero están en colas diferentes, o trabajos de la misma clase que pone en cola en colas diferentes, el orden de cola que estableció para un trabajador no se tiene en cuenta al desbloquearlo. unos. La razón es que un trabajo que se ejecuta desbloquea el siguiente, y el trabajo en sí no conoce el orden de cola de un trabajador en particular (incluso podría tener diferentes trabajadores con diferentes órdenes de cola), solo puede conocer la prioridad. Una vez que los trabajos bloqueados se desbloquean y están disponibles para el sondeo, un trabajador los recogerá siguiendo su orden en la cola.
Finalmente, los trabajos fallidos que se reintentan automática o manualmente funcionan de la misma manera que los trabajos nuevos que se ponen en cola: entran en la cola para obtener un semáforo abierto y, cuando lo obtienen, se ejecutarán. No importa si ya habían obtenido un semáforo abierto en el pasado.
Solid Queue no incluye ningún mecanismo de reintento automático, depende de Active Job para esto. Los trabajos que fallen se mantendrán en el sistema y se creará una ejecución fallida (un registro en la tabla solid_queue_failed_executions
) para ellos. El trabajo permanecerá allí hasta que se descarte manualmente o se vuelva a poner en cola. Puedes hacer esto en una consola como:
failed_execution = SolidQueue :: FailedExecution . find ( ... ) # Find the failed execution related to your job
failed_execution . error # inspect the error
failed_execution . retry # This will re-enqueue the job as if it was enqueued for the first time
failed_execution . discard # This will delete the job from the system
Sin embargo, recomendamos echar un vistazo a Mission_control-jobs, un panel donde, entre otras cosas, puede examinar y reintentar/descartar trabajos fallidos.
Algunos servicios de seguimiento de errores que se integran con Rails, como Sentry o Rollbar, se conectan a Active Job e informan automáticamente los errores no manejados que ocurren durante la ejecución del trabajo. Sin embargo, si su sistema de seguimiento de errores no lo hace, o si necesita algún informe personalizado, puede conectarse a Active Job usted mismo. Una posible forma de hacer esto sería:
# application_job.rb
class ApplicationJob < ActiveJob :: Base
rescue_from ( Exception ) do | exception |
Rails . error . report ( exception )
raise exception
end
end
Tenga en cuenta que también tendrá que duplicar la lógica anterior en ActionMailer::MailDeliveryJob
. Esto se debe a que ActionMailer
no hereda de ApplicationJob
sino que usa ActionMailer::MailDeliveryJob
que hereda de ActiveJob::Base
.
# application_mailer.rb
class ApplicationMailer < ActionMailer :: Base
ActionMailer :: MailDeliveryJob . rescue_from ( Exception ) do | exception |
Rails . error . report ( exception )
raise exception
end
end
Proporcionamos un complemento de Puma si desea ejecutar el supervisor de Solid Queue junto con Puma y hacer que Puma lo supervise y administre. Solo necesitas agregar
plugin :solid_queue
a su configuración puma.rb
Debido a que esto puede ser bastante complicado y muchas personas no deberían preocuparse por eso, de manera predeterminada Solid Queue está configurado en una base de datos diferente a la aplicación principal, la puesta en cola de trabajos se difiere hasta que se confirme cualquier transacción en curso gracias a la función incorporada de Active Job. capacidad para hacer esto. Esto significa que incluso si ejecuta Solid Queue en la misma base de datos que su aplicación, no aprovechará esta integridad transaccional.
Si prefiere cambiar esto, puede configurar config.active_job.enqueue_after_transaction_commit
en never
. También puede configurar esto por trabajo.
Si configura eso en never
pero aún desea asegurarse de no estar inadvertidamente en la integridad transaccional, puede asegurarse de que:
Sus trabajos que dependen de datos específicos siempre se ponen en cola en devoluciones de llamada after_commit
o desde un lugar donde esté seguro de que cualquier dato que utilizará el trabajo se ha confirmado en la base de datos antes de que el trabajo se ponga en cola.
O puede configurar una base de datos diferente para Solid Queue, incluso si es la misma que su aplicación, asegurándose de que se use una conexión diferente en las solicitudes de manejo de subprocesos o trabajos en ejecución para su aplicación para poner en cola los trabajos. Por ejemplo:
class ApplicationRecord < ActiveRecord :: Base
self . abstract_class = true
connects_to database : { writing : :primary , reading : :replica }
config . solid_queue . connects_to = { database : { writing : :primary , reading : :replica } }
Solid Queue admite la definición de tareas recurrentes que se ejecutan en momentos específicos en el futuro, de forma regular, como trabajos cron. Estos son gestionados por el proceso del planificador y están definidos en su propio archivo de configuración. De forma predeterminada, el archivo se encuentra en config/recurring.yml
, pero puede establecer una ruta diferente usando la variable de entorno SOLID_QUEUE_RECURRING_SCHEDULE
o usando la opción --recurring_schedule_file
con bin/jobs
, así:
bin/jobs --recurring_schedule_file=config/schedule.yml
La configuración en sí se ve así:
production :
a_periodic_job :
class : MyJob
args : [ 42, { status: "custom_status" } ]
schedule : every second
a_cleanup_task :
command : " DeletedStuff.clear_all "
schedule : every day at 9am
Las tareas se especifican como un hash/diccionario, donde la clave será la clave de la tarea internamente. Cada tarea debe tener una class
, que será la clase de trabajo que se pondrá en cola, o un command
, que se evaluará en el contexto de un trabajo ( SolidQueue::RecurringJob
) que se pondrá en cola según su programación, en la cola solid_queue_recurring
.
Cada tarea debe tener también una programación, que se analiza utilizando Fugit, por lo que acepta cualquier cosa que Fugit acepte como cron. Opcionalmente, puede proporcionar lo siguiente para cada tarea:
args
: los argumentos que se pasarán al trabajo, como un argumento único, un hash o una matriz de argumentos que también puede incluir kwargs como último elemento de la matriz.El trabajo en la configuración de ejemplo anterior se pondrá en cola cada segundo como:
MyJob . perform_later ( 42 , status : "custom_status" )
queue
: una cola diferente que se utilizará al poner en cola el trabajo. Si no hay ninguno, la cola configurada para la clase de trabajo.
priority
: un valor de prioridad numérico que se utilizará al poner en cola el trabajo.
El programador pone las tareas en cola en los momentos correspondientes y cada tarea programa la siguiente. Esto está bastante inspirado en lo que hace GoodJob.
Es posible ejecutar varios programadores con la misma configuración recurring_tasks
, por ejemplo, si tiene varios servidores para redundancia y ejecuta el scheduler
en más de uno de ellos. Para evitar poner en cola tareas duplicadas al mismo tiempo, se crea una entrada en una nueva tabla solid_queue_recurring_executions
en la misma transacción en la que se pone en cola el trabajo. Esta tabla tiene un índice único en task_key
y run_at
, lo que garantiza que solo se creará una entrada por tarea cada vez. Esto solo funciona si tiene preserve_finished_jobs
configurado en true
(el valor predeterminado) y la garantía se aplica siempre que mantenga los trabajos.
Nota : se admite una única programación recurrente, por lo que puede tener varios programadores que utilicen la misma programación, pero no varios programadores que utilicen configuraciones diferentes.
Finalmente, es posible configurar trabajos que no son manejados por Solid Queue. Es decir, puedes tener un trabajo como este en tu app:
class MyResqueJob < ApplicationJob
self . queue_adapter = :resque
def perform ( arg )
# ..
end
end
Aún puedes configurar esto en Solid Queue:
my_periodic_resque_job :
class : MyResqueJob
args : 22
schedule : " */5 * * * * "
y el trabajo se pondrá en cola a través de perform_later
para que se ejecute en Resque. Sin embargo, en este caso no realizaremos un seguimiento de ningún registro solid_queue_recurring_execution
y no habrá ninguna garantía de que el trabajo se ponga en cola solo una vez cada vez.
Solid Queue se ha inspirado en resque y GoodJob. Recomendamos consultar estos proyectos, ya que son excelentes ejemplos de los que hemos aprendido mucho.
La gema está disponible como código abierto según los términos de la licencia MIT.