Créer des tâches CRON : les 5 erreurs à éviter
Lorsqu’on développe un site web (ou autre application), dès lors qu’il a besoin de traitements récurrents, il est intéressant de voir comment les automatiser. Il existe pour cela des tâches automatisées, qui permettent d’effectuer une action de manière régulière sur un serveur, sans intervention humaine.
Les tâches CRON, ou tâches automatisées, permettent de lancer une commande sur un serveur à intervalle régulier (toutes les heures, chaque jour à minuit, toutes les 2 minutes, les dimanches de 4 à 8h).
Lorsqu’un site est conceptualisé, on a généralement recours aux tâches CRON pour différentes choses : synchroniser des données entre différentes applications, planifier des tâches quotidiennes pour optimiser les performances du serveur, etc.
Au travers d’un cas d’exemple, je vais essayer de donner des pistes quant aux erreurs à éviter lors de la création d’une tâche CRON.
Cas d’exemple
Pour que les erreurs à éviter soient plus faciles à identifier, utilisons un cas simple qu'on peut rencontrer sur de nombreux projets.
Sur le site pour lequel nous créons une tâche CRON, les données provenant d’un autre site (tel qu’un CRM) doivent être récupérées pour les synchroniser avec la base de donnée des utilisateurs. Cela permet notamment aux utilisateurs d'accéder à certains contenus en fonction des informations indiquées dans le CRM.
Les utilisateurs ajoutés ou modifiés sur le CRM doivent donc être répercutés sur le site.
Pour gérer ce cas, on peut créer une tâche CRON qui chaque jour, à minuit, lit les données des utilisateurs ayant subit une modification (sur la base de la date de dernière modification) et les traite.
Erreur n°1 : Utiliser une tâche CRON quand ce n’est pas nécessaire
Dans notre cas d’exemple, nous avons un système dit de “pull” : le site web récupère à intervalle régulier des données depuis un autre service et se sert des données qu’il récupère, en les filtrant de manière très grossière.
Durant la journée, les données ne sont pas synchronisées. Ainsi, un utilisateur ajouté dans le CRM n’aura accès au site que le lendemain.
De plus, lors des tests de la tâche CRON, il est nécessaire d’avoir d’ores-et-déjà accès aux données du CRM (qui peut ne pas autoriser les appels depuis un environnement de test).
Une autre solution, qui permettrait de se passer de tâche CRON, serait de passer par un système de “push”. Au lieu de récupérer des données du CRM, ce dernier peut appeler automatiquement, à chaque création / modification / suppression, un point d’API créé sur le site pour gérer chaque cas en temps réel. Ceci permet également, lors des tests, de générer des appels depuis une autre source que le CRM avec le même payload afin d’en simuler le fonctionnement.
Toutefois, le principal frein à cette méthode est généralement l’impossibilité à implémenter des appels depuis le service distant.
Dans notre exemple, admettons que le CRM ne permet pas de fournir un point d’API à appeler lors des modifications...
Erreur n°2 : Traiter en un seul processus toutes les données
Nous allons donc rester sur le principe de faire une tâche CRON. Dans le choix initial, nous prévoyons de traiter lors de la tâche tous les utilisateurs modifiés dans les 24 dernières heures.
Lors de nos tests, nous allons travailler sur des modifications effectuées pour l’occasion, où chaque appel travaille sur une dizaine d’utilisateurs. Au lancement du site, la base de clients étant petite, on table sur un travail sur une centaine d’utilisateurs par jour.
Problème : qui nous dit que d’ici 2 ans, le volume traité par jour ne sera pas de 1.000 utilisateurs par jour, sinon plus ? De plus, en admettant que le script rencontre une erreur en gérant le 3ème utilisateur, que deviennent les modifications sur les suivants ?
La solution pour avoir un traitement robuste est de simplifier la responsabilité de la tâche CRON. Celle-ci va se contenter de lister les éléments à traiter (avec leurs données, afin d’éviter de générer de nouveaux appels au service distant) dans une queue.
Un autre système s’occupe de travailler chaque ligne unitairement. Ainsi, la tâche CRON est rapide (simple listing et mise en queue des données), simple à comprendre (peu de logique) et présente peu de risque d’erreur.
Le système gérant les entrées de la queue une à une a de son côté une responsabilité limitée (pas besoin de travailler avec le CRM, ni de savoir quels sont les autres éléments en traitement). Le système peut donc être testé plus facilement (création d’éléments factices dans la queue pour tests) et gérer plusieurs lignes d’un coup en parallélisant le travail sur plusieurs processus.
Erreur n°3 : Traiter les données selon une période
Maintenant que nous avons réduit la responsabilité de notre tâche CRON, qui ne gère alors que la mise en queue des utilisateurs, il est décidé de récupérer les utilisateurs dont la date de dernière modification est à NOW() - 24h.
Un problème survient quelques semaines après la mise en production : le serveur responsable de lancer la tâche CRON a un souci suite à une modification de sa configuration. Pendant 4 jours, la tâche n’est pas lancée. La tâche est relancée en urgence … et on récupère les données des 24 dernières heures, sans récupérer celles des 3 jours précédents !
La solution pour se prémunir d’un tel souci est de se servir d'un système de pointeur sur les données. Au lieu de récupérer les données des 24 dernières heures, nous stockons localement l’information de la date de dernière modification la plus élevée qui a été mise en queue. Au lancement suivant, nous récupérons les données depuis le service distant à partir de cette date, plutôt qu’une date arbitrairement calculée.
Ainsi, dans notre cas, la tâche CRON récupère simplement les modifications effectuées sur les 4 derniers jours (depuis sa dernière exécution) et les met en queue pour traitement.
Erreur n°4 : Ne pas prévoir les conséquences d’une relance
En raison du problème de lancement de la tâche CRON, nous avions donc relancé la tâche et récupéré les données d’une seule journée dans la précipitation.
Problème : nous avons maintenant des données qui sont non seulement incomplètes, mais ont en plus certaines modifications récentes.
Une solution rapide pour récupérer les données non traitées peut être de changer la date de NOW() - 24h à NOW() - 96h, afin de tout traiter. Mais dans ce cas, que se passe-t-il lorsqu’un utilisateur a été ajouté lors de la première relance ? Est-ce qu’un doublon va être créé ? Est-ce que sa date d’ajout va être faussée ?
De la même façon, un bug dans le code aurait pu être découvert suite à un cas particulier dans les données, et la tâche CRON pourrait être relancée après correction du cas.
Pour ne pas subir ce genre de problème, il faut toujours prévoir dans une tâche la possibilité qu’elle soit rejouée sans conséquence négative :
- Une donnée ajoutée ne doit pas être créée en doublon ;
- Un élément qui est déjà à jour n’a pas besoin de générer une mise à jour (si les changements liés à la mise à jour ont peu d'impact, il peut toutefois être plus rapide de mettre à jour dans tous les cas) ;
- La suppression d’un élément déjà supprimé ne devrait pas générer d’erreur.
Erreur n°5 : Ne pas prévoir de logs détaillés
Lors du développement d’une tâche CRON, le test de celle-ci va pousser à placer des points de logs pour voir comment le traitement se déroule (à moins d’utiliser une autre solution telle que XDebug).
Toutefois, moins souvent, des logs nombreux, clairs et précis sont mis en place sur la tâche pour sa mise en production.
Le but de ces logs est d’avoir la possibilité, en cas de problème, de suivre le traitement tel qu’il a été effectué sans avoir besoin de rejouer la tâche CRON.
Ces logs doivent être préparés dès la création de la tâche et doivent évoluer à chaque fois qu'un log existant s'est avéré insuffisant.
⚠️ Afin de ne pas saturer l’espace disque du serveur, il convient de mettre en place un logrotate sur ces logs, puisque la plupart des autres logs ne nécessitent pas de conservation permanente.
Les tâches CRON représentent un outil souple et puissant pour réduire la charge de nombreuses tâches associées à l'administration des systèmes. Ces tâches automatisées doivent malgré tout être utilisées avec précaution et uniquement lorsqu'elles sont nécessaires ! Une tâche CRON mal exécutée pourrait vous demander plus de temps pour corriger les erreurs que si vous aviez effectué la tâche manuellement ...