🍪🥋 Les tâches de fonds et la concurrence
Les tâches de fond ou en "arrière-plan", gérées la plupart du temps avec Sidekiq ou nativement par ActiveJobs sur les applications Rails, permettent d'alléger nos plateformes en déchargeant des actions potentiellement longues ou non urgentes. Ces actions vont dans une file d’attente de traitement en arrière-plan, libérant ainsi le cycle original de requête/réponse. Cependant, ces tâches peuvent parfois nous poser des problèmes de concurrence nous allons voir pourquoi et je vais vous présenter deux Gem que nous utilisons pour y remédier.
Attention : liste non exhaustive.
Au programme :
Pourquoi la concurrence des tâches peut causer des soucis ?
Deux librairies qui permettent de contrôler la concurrence
Bonjour et bienvenue sur la 7ème édition de Ruby Biscuit.
Vous êtes maintenant 80 abonnés 🥳 Si vous n’êtes pas déjà inscrit :
Les tâches de fond, que vous connaissez aussi peut être sous le nom de Worker ou de Job en fonction des librairies que vous utilisez sont des tâches qui peuvent faire des calculs, envoyer des e-mails, générer des fichiers, modifier des données, faire des exports ou n'importe qu'elle autre action qui ne nécessite pas une réponse immédiate.
Lorsque ces tâches sont lancées, elles sont mises dans des files d'attentes et s'exécutent uniquement lorsque notre serveur a suffisamment de place pour les traiter.
Chez Capsens, nous utilisons la gem Sidekiq pour gérer nos tâches de fond. Sidekiq permet - entre autre - d'allouer plusieurs threads pour traiter les jobs en parallèle.
Pourquoi la concurrence des tâches peut causer des soucis ?
Risque sur l'intégrité des données
Imaginez deux personnes dans un supermarché qui achètent la même boite de biscuit. Ces deux personnes se présentent en caisse pour payer leur article. Leurs paquets de biscuits sont scannés à l'exact même moment sur deux caisses différentes. Au moment où l'article est scanné une tâche est déclenchée. Cette tâche va chercher le nombre de paquets de biscuits restants en stock, disons 100 et on lui soustrait le nombre de paquet acheté par le client. Si la donnée du nombre de paquet en stock est récupérée au même moment, alors les deux caisses soustrairont 1 à 100. Nous nous retrouverons alors avec un stock à 99 alors que 2 paquets de biscuits ont été vendus.
Risque de duplication des tâches
Il peut arriver pour x raison qu'une tâche ait été lancée deux fois. Si par exemple un utilisateur s'acharne sur un bouton (qui n'a malheureusement pas été protégé) qui lance une tâche ou si notre process Sidekiq se relance et démarre tous les jobs qui étaient en attente.
En environnement de développement, il n'est pas rare de lancer son serveur rails sans le process Sidekiq, puis lorsque que vous démarrez ce dernier, toutes les tâches se lancent en même temps. La duplication d'une tâche pourrait générer des erreurs ou des comportements non attendus.
Risque de saturation du serveur
Prenons une plateforme qui doit récupérer très régulièrement une liste de fichiers contenant des données à mettre à jour et les déposer sur un File Transfer Protocol (FTP).
Chaque enregistrement déclenche une tâche. Je rappelle, il peut y en avoir plusieurs à la fois. Chaque tâche peut durer plusieurs dizaines de minutes et demander un volume de mémoire important. Si les tâches tournaient toutes en parallèle nous pourrions avoir des soucis de performance du serveur et également empêcher l'exécution d'autres tâches plus urgentes pendant un temps.
Deux librairies qui permettent de contrôler la concurrence
Je parlerais de "Jobs" car ces gems sont faites pour Sidekiq.
sidekiq-unique-jobs
pour rendre les tâches uniques
Chez Capsens depuis plusieurs mois nous utilisons la gem sidekiq-unique-jobs
. Cette dernière permet de verrouiller un Job en lui ajoutant des contraintes d'unicité. Si un Job est en train d'être exécuté et qu'au même moment on essaye de créer de nouveau ce même Job, le deuxième sera directement "tué" et mis dans la "dead queue", la queue morte.
Cette gem offre énormément d'options pour s'adapter à différents besoins et cas d'usage, il vous suffira de préciser au minimum le moment où vous voulez verrouiller le Job.
sidekiq-throttle
pour créer une file d'attente ordonnée
À la différence de sidekiq-unique-jobs
, la gem sidekiq-throttle
ne tue pas les tâches dupliquées, mais elle permet de les mettre en file d'attente dans leur ordre d'arrivée pour controller leur exécution. Nous utilisons par exemple sur notre CD, outil interne de déploiement continue. Grâce à la gem nous nous assurons de ne jamais lancer 2 déploiements en même temps. Ils seront simplement lancés les uns après les autres.
À savoir : La version 7 de Sidekiq introduit la fonctionnalité de "capsule" qui permet de gérer plus finement le nombre de threads alloués par Sidekiq. 😉
Pour découvrir des nouveaux tips Ruby on Rails ne cherche plus, tu es au bon endroit !
Mélanie 😊