• Contenu
  • Bas de page
logo ouidoulogo ouidoulogo ouidoulogo ouidou
  • Qui sommes-nous ?
  • Offres
    • 💻 Applications métier
    • 🤝 Collaboration des équipes
    • 🛡️ Sécurisation et optimisation du système d’information
    • 🔗 Transformation numérique
  • Expertises
    • 🖥️ Développement logiciel
    • ♾️ DevSecOps
    • ⚙️ Intégration de logiciels et négoce de licences
      • Atlassian : Jira, Confluence, Bitbucket…
      • Plateforme monday.com
      • GitLab
      • SonarQube
    • 📚​ Logiciel de CRM et de gestion
    • 🎨 UX/UI design
    • 🌐 Accessibilité Numérique
    • 🗂️​ Démarches simplifiées
    • 📝 Formations Atlassian
  • Références
  • Carrières
    • 🧐 Pourquoi rejoindre Ouidou ?
    • ✍🏻 Nous rejoindre
    • 👨‍💻 Rencontrer nos collaborateurs
    • 🚀 Grandir chez Ouidou
  • RSE
  • Ressources
    • 🗞️ Actualités
    • 🔍 Articles techniques
    • 📖 Livres blancs
    • 🎙️ Interviews Clients
Nous contacter
✕
Image miniature Optimiser la comitologie
Optimiser la comitologie
14 novembre 2024
Découvrez ROVO l'IA révolutionnaire
Découvrez Rovo
19 novembre 2024
Ressources > Articles techniques > Introduction à Solid Queue

Introduction à Solid Queue

Article écrit par Valentin M.

Introduction à Solid Queue et son fonctionnement

Solid Queue ? C’est quoi ?

Rails 8 sortira bientôt et parmi ses fonctionnalités, on peut noter l’intégration par défaut de Solid Queue pour pouvoir gérer nos jobs asynchrones. Mais qu’est-ce que c’est Solid Queue ?

Annoncé au Rails World 2023, Solid Queue est un backend pouvant utiliser la base de données SQL de notre choix (MySQL, SQLite, PostgreSQL) pour gérer nos jobs, contrairement à Sidekiq ou Resque qui ne s’appuient que sur Redis.

L’idée derrière est de réduire au maximum la dépendance à de multiples services comme Redis pour se simplifier la vie et se concentrer sur l’essentiel. Redis est peut-être plus performant, mais est-ce que la différence mérite vraiment l’effort de configuration et maintenance d’un service en plus ? Pour une majorité de projets, les performances d’une base de données seront amplement suffisantes.

De plus, grâce à l’implémentation du SELECT ... FOR UPDATE SKIP LOCKED dans nos bases de données, l’utilisation de multiples workers exécutant nos jobs est possible. En effet le SKIP LOCKED passera outre des lignes des tables déjà utilisé par les autres workers sans être bloqué ni se marcher dessus.

Une petite démo ?

Faisons un petit projet rails avec un simple job. Je serai sur Rails 7.2 pour présenter la configuration de base, mais sachez que la gem et la configuration de base seront déjà prêtes sur Rails 8

rails new solidqueue-demo --database=postgresql
cd solidqueue-demo

bin/bundle add solid_queue
bin/rails solid_queue:install

La tâche install de la gem va générer les YML de configurations, les migrations sur la base de données et rajouter dans production.rb Solid Queue en tant qu’adapter pour ActiveJobs via deux lignes.

Répliquons les deux lignes dans config/environments/development.rb pour activer Solid Queue dans notre environnement de developement et ajoutons des logs.

 # config/environments/development.rb

 config.solid_queue.logger = Logger.new(STDOUT)
 config.active_job.queue_adapter = :solid_queue
 # indique quelle base de données utiliser pour queue 
 config.solid_queue.connects_to = { database: { writing: :queue } }

Ajoutons ensuite la base de données pour queue dans config/database.yml

# config/database.yml

development:
 primary:
   <<: *default
   database: solidqueue_demo_development
 queue:
   <<: *default
   database: solidqueue_demo_development_queue
   migrations_paths: db/queue_migrate

Il est fortement recommandé d’utiliser une base de données séparée de celle de l’application. Vous pouvez alors supprimer le connects_to et déplacer le contenu de db/queue_schema.rb dans une migration si vous souhaitez partir sur du tout-en-un

Il ne reste plus qu’à créer nos bases de données avec les schémas de Solid Queue

bin/rails db:prepare

Et voilà nous sommes prêts à envoyer nos jobs ! Faisons un job bateau pour tester

# app/jobs/bonjour_job.rb
class BonjourJob < ApplicationJob
  def perform(a)
    puts "Bonjour #{a} :)"
  end
end

et allons dans la console. Activons les logs de debug pour voir plus clairement ce qu’il va se passer puis mettons un job en queue

Rails.logger = Logger.new(STDOUT)
BonjourJob.perform_later("vous")

Grâce aux logs, nous pouvons alors voir ceci :

Tout d’abord, une ligne sera créée dans solid_queue_jobs avec le nom du jobs, ses arguments, sa queue (ici default car non définie) et des timestamps. Puis ensuite une deuxième ligne dans une autre table : solid_queue_ready_executions référençant notre première ligne. Nous verrons plus tard leur utilité

Faisons la même chose, mais avec une date future :

BonjourJob.set(wait: 2.days).perform_later("nous")

Nous obtenons alors:

La grosse différence étant la deuxième ligne créée qui ne sera plus sur solid_queue_ready_executions, mais solid_queue_scheduled_executions

Maintenant que les jobs sont mis dans la queue, il est temps de lancer les process de Solid Queue qui vont les exécuter. Il y a deux possibilités :

  • Utiliser le puma de notre serveur pour exécuter nos jobs
  • Lancer un process à part (lançable avec bin/jobs)

Ici, nous allons utiliser le process à part, mais retenez que si vous préférez utiliser puma, il vous suffit de rajouter un plugin puma comme ceci

# config/puma.rb

plugin :solid_queue

Lançons Solid Queue avec bin/jobs:

Notre premier job a été récupéré par le worker (process_id: 3) et executé.

Mais comment ça marche ?

Les process

Tout d’abord, parlons de ce qui a été exécuté au lancement de Solid Queue. Sur le screen plus haut on peut voir que 3 process ont démarré: Le Supervisor, le Dispatcher et le Worker :

  • le Dispatcher va sélectionner les jobs demandant d’être exécuté dans l’instant et les mettre à disposition pour exécution. C’est aussi lui qui va gérer l’exécution en simultanée ou non de certains jobs (Exemple: on veut que tout les jobs de types import ne s’exécutent qu’un à la fois, le dispatcher va s’assurer de cette règle)
  • le Worker va récupérer les jobs mis à disposition par le dispatcher et les exécuter. En fonction du résultat, il va écrire dans différentes tables
  • le Supervisor va gérer les deux premiers process et en créer/détruire si besoin et selon la configuration de Solid Queue

Les dispatchers et les workers sont tous deux configurables dans config/queue.yml en fonction de l’environnement. Voilà la configuration de base

# config/queue.yml

default: &default
  dispatchers:
    - polling_interval: 1
      batch_size: 500
  workers:
    - queues: "*"
      threads: 3
      processes: <%= ENV.fetch("JOB_CONCURRENCY", 1) %>
      polling_interval: 0.1

development:
  <<: *default

test:
  <<: *default

production:
  <<: *default

polling_interval indique à quel intervalle les dispatchers et workers vont ping la base de données en secondes pour surveiller les queues (les workers devant prendre en charge les jobs le plus vite possible, l’intervalle est donc très bas pour eux)

queues va définir les différentes queues (des namespaces pour les jobs en somme) possible pour les jobs. Leurs ordres définissant l’ordre de priorité auquel ses jobs seront traités (exemple queues: [import, default], les jobs d’import auront priorité par rapport à tout les autres jobs). La wildcard indique simplement toutes les autres queues possibles

batch_size indique le nombre de jobs que le dispatcher va traiter en un coup

threads et processes permettent simplement d’ajuster le nombre de workers et threads qui seront exécutés par Solid Queue

Cela vous permettra d’ajuster ces process en fonction de votre environnement.

Les tables

Lors de la mise en queue de nos jobs, nous avons pu voir de la lecture et écriture sur plusieurs tables, vous pouvez voir ces tables dans votre schema de queue ou directement dans votre base de données. Listons et expliquons l’utilité de ces différentes tables:

  • solid_queue_blocked_executions
  • solid_queue_claimed_executions
  • solid_queue_failed_executions
  • solid_queue_jobs
  • solid_queue_pauses
  • solid_queue_processes
  • solid_queue_ready_executions
  • solid_queue_recurring_executions
  • solid_queue_recurring_tasks
  • solid_queue_scheduled_executions
  • solid_queue_semaphores

Les jobs seront stockés dans solid_queue_jobs et référencés dans la plupart des autres tables. Une fois terminé, le job sera conservé dans cette table avec une date de fin (finished_at). En regardant en base, on peut voir 2 lignes dans cette table : nos deux jobs, dont un terminé !

Pour savoir quand les jobs doivent être lancés, une ligne dans solid_queue_scheduled_executions ou solid_queue_recurring_tasks (si un CRON) est créée. Lors de la création de notre deuxième job, une ligne a été créée dans solid_queue_scheduled_executions pour indiquer sa date d’exécution.

Le Dispatcher va alors récupérer les jobs disponibles dans ces deux tables et les mettre à disposition dans solid_queue_ready_executions pour les Workers qui les exécuteront. Dans le cas de notre premier job, une ligne dans solid_queue_ready_executions a directement été créé, car aucune attente n’a été spécifiée

Les workers vont alors piocher parmi ces jobs en fonction de leur priorité et créer une ligne dans solid_queue_claimed_executions pour indiquer quels process a pris quel jobs et à quel moment. Vous remarquerez que la table solid_queue_claimed_executions référence solid_queue_processes qui est tout simplement la liste de tous les process lancés par SolidQueue.

Une fois terminé, une date de fin sera ajoutée dans solid_queue_jobs si le job s’est bien déroulé ou bien dans solid_queue_failed_executions le cas échéant. À savoir que les jobs ne sont pas automatiquement relancés si échoués, vous devez le préciser à ActiveJob si besoin.

solid_queue_pauses nous permet de mettre en pause des queues entière si besoin (en cas de bug présent dans certains jobs par exemple pour éviter d’aggraver une situation)

solid_queue_semaphores et solid_queue_blocked_executions permettent quant à eux de gérer l’exécution en concurrence des jobs et aussi d’éviter que plusieurs jobs d’un même type soient lancés en même temps. Les jobs en attente de l’execution d’autres jobs étant placés dans solid_queue_blocked_executions et solid_queue_semaphores permettant d’indiquer la disponibilité d’exécution.

Et pour finir, solid_queue_recurring_executions qui sert dans le cas d’utilisation de plusieurs scheduler pour vos CRON. Cela indique si un CRON a déjà été pris en charge (et donc évitez les CRON en doublon)

Les CRON

Un autre avantage de Solid Queue c’est qu’il est tout en un, pas besoin de rajouter x plugins pour avoir accès aux CRON par exemple.

Vous pouvez configurer vos CRON dans config/recurring.yml, tout sera géré ensuite par le dispatcher.

Rajoutons un CRON pour notre job:

# config/recurring.yml

development:
  bonjour:
    class: BonjourJob
    args: ['la minute']
    schedule: every minute

On peut aussi exécuter du code au lieu d’un job

# config/recurring.yml

  au-revoir:
    command: "puts 'au revoir :('"
    schedule: every minute

Et comment on monitore tout ça ?

C’est bien beau de tout avoir dans sa base de données, mais cela pourrait manquer de visibilité pour certains.

Ça tombe bien, une gem est sorti peu après Solid Queue pour avoir un dashboard de Solid Queue: mission_control-jobs

Installons-la:

bundle add mission_control-jobs

et rajoutons l’endpoint du dashboard dans notre config/routes.rb

# config/routes.rb

Rails.application.routes.draw do
  mount MissionControl::Jobs::Engine, at: "/jobs"
end

Vous pouvez ensuite rajouter votre ApplicationController dans config/application.rb pour pouvoir ajouter votre système d’authentification préféré sur l’endpoint (ne laissez évidemment pas cet endpoint sans authentification)

# config/application.rb

config.mission_control.jobs.base_controller_class = "VotreSuperbeController"

Et vous voilà avec un super dashboard:

Vous pourrez mettre en pause les queues, relancer les jobs échoués, les supprimer et voir les différents workers en cours d’exécution !

Vous pourrez d’ailleurs voir nos 2 précédents jobs, dont celui qui est en attente pour dans 2 jours. En cliquant dessus, vous aurez accès aux données des différentes colonnes du job dont les arguments que vous avez envoyé.

image Bonjour Job et raw data

Les CRON seront comme vous vous en doutez dans Recurring tasks

Conclusion

Et voilà, nous avons maintenant nos jobs qui tournent en asynchrone avec des CRONs. Tout ça dans notre base de données et sans dépendre d’un Redis.

La simplicité de cette gem est vraiment satisfaisante et permet d’aller droit au but sans se contraindre à mettre en place une infra plus compliquée que nécessaire. L’axe de Rails 8 est de se débarrasser du superflu avec SolidQueue, mais aussi SolidCache et SolidCable, se basant sur le même principe de tout centraliser dans sa base de données.

Si vous voulez pousser la chose plus loin, n’hésitez pas à faire un tour sur le github de SolidQueue

À bientôt 🙂 !

À lire aussi

Fresque numérique miniature image
16 avril 2025

Fresque du Numérique

Lire la suite

intelligence artificielle Ouicommit miniature image
17 mars 2025

Ouicommit – L’intelligence artificielle en entreprise, on y est ! 

Lire la suite

Image miniature Hackathon Women in Tech
13 mars 2025

Hackathon Women in Tech :  un engagement pour une tech plus inclusive 

Lire la suite

image miniature les nouveautés Atlassian
26 février 2025

Les nouveautés Atlassian en 2025

Lire la suite

Articles associés

Fresque numérique miniature image
16 avril 2025

Fresque du Numérique


Lire la suite
intelligence artificielle Ouicommit miniature image
17 mars 2025

Ouicommit – L’intelligence artificielle en entreprise, on y est ! 


Lire la suite
Image miniature Hackathon Women in Tech
13 mars 2025

Hackathon Women in Tech :  un engagement pour une tech plus inclusive 


Lire la suite

À propos

  • Qui sommes-nous ?
  • Références
  • RSE
  • Ressources

Offres

  • Applications métier
  • Collaboration des équipes
  • Sécurisation et optimisation du système d’information
  • Transformation numérique

Expertises

  • Développement logiciel
  • DevSecOps
  • Intégration de logiciels et négoce de licences
  • Logiciel de CRM et de gestion
  • UX/UI design
  • Accessibilité Numérique
  • Démarches simplifiées
  • Formations Atlassian

Carrières

  • Pourquoi rejoindre Ouidou ?
  • Nous rejoindre
  • Rencontrer nos collaborateurs
  • Grandir chez Ouidou

SIEGE SOCIAL
70-74 boulevard Garibaldi, 75015 Paris

Ouidou Nord
165 Avenue de Bretagne, 59000 Lille

Ouidou Rhône-Alpes
4 place Amédée Bonnet, 69002 Lyon

Ouidou Grand-Ouest
2 rue Crucy, 44000 Nantes

Ouidou Grand-Est
7 cour des Cigarières, 67000 Strasbourg

  • Linkedin Ouidou
  • GitHub Ouidou
  • Youtube Ouidou
© 2024 Ouidou | Tous droits réservés | Plan du site | Mentions légales | Déclaration d'accessibilité
    Nous contacter