Tout le monde sait que Magento contient un système de cron, mais personne ne sait vraiment comment s’en servir. Et si on mettait les choses à plat ?
L’idée originelle est simple mais puissante : Permettre la mise en place et la programmation de tâches répétitives et automatiques depuis l’applicatif. Ainsi, des modules peuvent travailler de manière autonome, et les intégrateurs peuvent même proposer une interface pour en gérer la programmation.
Retour sur Terre
En réalité, Magento à besoin d’une vraie tâche cron pour faire fonctionner les siennes. C’est le rôle que remplissent 2 fichiers, situés à la racine : cron.sh et cron.php. C’est d’ailleurs normal, le PHP ne s’exécutant pas tout seul. Mais vous verrez assez vite que ça complique un peu les choses…
Cron.sh, le chef d’orchestre
C’est cron.sh qui va vérifier que cron.php (1) et lui-même ne sont pas déjà en cours d’exécution. Si la voie est libre, il lance cron.php (1). Et c’est tout !
Attention : c’est donc lui, et lui seul, qui doit être programmé pour être exécuté par le serveur.
Techniquement, rien n’empêche de mettre cron.php en crontab. Mais pourquoi ne faut-il quand même pas le faire ?
Une tâche un peu longue (plus longue que l’intervalle entre 2 crons) va se superposer à celle en cours de traitement, et risquer
- d’accaparer les ressources du système (entraînant le rallongement du temps de réponse, voire un blocage complet de la boutique), ou
- de faire une nouvelle fois le boulot (par ex. donner des points de fidélité à ses clients comme cadeau de Noël)
Nous allons donc éditer la crontab de notre serveur (2) et ajouter l’entrée suivante :
*/15 * * * * sh chemin/vers/magento/cron.sh > /dev/null 2>&1
(Cette tâche cron va exécuter cron.sh toutes les 15 minutes )
Cron.php, le détonateur
Après quelques vérifications de base, cron.php instancie Magento et dispatche un event à tous les observers qui ont décidés d’être à son écoute. (Nous verrons plus bas comment un module décide d’écouter cet event). Et c’est tout 🙂
Les conséquences de ce système
- Vous ne pourrez jamais exécuter un code plus fréquemment que la fréquence de cron.sh 🙁
- Vous pourrez faire semblant d’exécuter un code plus fréquemment que la fréquence de cron.sh 🙂
Distributions des points
Pour illustrer mes propos, imaginons que nous mettons en place un module de distribution de points de fidélités. (Nous aurions évidement pu envoyer des newsletter, des rappels de paniers abandonnés, faire de la synchro de prix avec un PIM, etc…)
Notre module, pour écouter l’event lancé par cron.php, doit déclarer un observer. Nous avons donc l’arborescence suivante :
L’observer contient un code plutot classique :
<?php class JBCreation_PointsManager_Model_Observer{ const POINTS_QUANTITY = 100; public function givePoints($observer){ /** * Give self::POINTS_QUANTITY to all my customers */ // (...) } }[/sourcecode] La partie intéressante : la déclaration xml : [sourcecode language="xml"] <?xml version="1.0"?> <config> <!-- (...) --> <crontab> <jobs> <pointsmanager_give_points> <!-- Le nom (unique) du job que l'on nommera A dans la suite --> <schedule> <cron_expr>0 * * * *</cron_expr> </schedule> <run> <model>pointsmanager/observer::givePoints</model> </run> </pointsmanager_give_points> <pointsmanager_give_points> <!-- Le nom (unique) du job que l'on nommera B dans la suite --> <schedule> <cron_expr>* * * * *</cron_expr> <!-- La fréquence d'execution --> </schedule> <run> <model>pointsmanager/observer::givePoints</model> <!-- La méthode à exécuter --> </run> </pointsmanager_give_points> </jobs> </crontab> </config>
Nous voyons que la configuration utilise également le format crontab pour planifier ses exécutions. Les rôles des autres balises étant plutôt faciles à deviner, je me pencherai sur ce qui nous intéresse vraiment : quand est-ce que le code sera exécuté ?
Nous avons dans notre exemple 2 planifications. La première déclare « toutes les heures » (A), la seconde « toutes les minutes » (B).
Pour rappel, le PHP ne s’exécute pas de manière autonome, et nous avons vu plus haut qu’il était exécuté par cron.php. Or ce cron est lancé par le serveur toutes les 15 minutes… On voit facilement comment la tâche A pourra tenir ses « engagements », mais comment va être gérée la tâche B par Magento ?
La configuration à connaître
C’est ici que tout se joue, et que l’intrigue se dénoue :
Administration : Système > Configuration > Avancé > Système
Magento, d’après cette configuration, va alimenter la table « cron_schedule » avec des jobs, correspondants au paramétrage xml de nos modules, et ce, de la manière suivante (3) :
- Lorsque cron.php est lancé, Magento va vérifier que cela fait 15 minutes ou plus que la dernière écriture en base des jobs a été faite. (Mage_Cron_Model_Observer->generate() )
- Si le test 1) est concluant, il pourra alors écrire en base tous les jobs qui doivent être exécutés dans les 20 prochaines minutes
- Si le moment d’exécuter les jobs (c-à-d un nouveau passage de cron.php) dépasse de 15 minutes la date planifiée par le job, alors il sera considéré comme « manqué » et jamais ré-executé (missed)Magento a pensé à l’archivage des jobs, afin d’éviter une table vite trop grosse :
- Si le nettoyage de l’historique n’a pas été fait depuis au moins 10 minutes, alors il le lance…(Mage_Cron_Model_Observer->cleanup() )
- …afin de conserver 60 minutes de jobs dont le code retour est positif.
- …afin de conserver 600 minutes de jobs qui ont générés des exceptions.
Cela donnera cela après 2 exécutions de cron.php :
Au final, il se passe quoi ?
Pour répondre à la question du moment d’exécution de nos tâches A et B, nous devons donc obligatoirement prendre en compte les 3 informations suivantes :
- La fréquence du cron serveur
- Le paramétrage du cron du module
- La configuration générale des crons
Et selon ces informations :
- La tâche A sera exécutée toutes les 15 minutes, comme prévu
- La tâche B sera exécutée 15 fois toutes les 15 minutes !
Pour conclure
Il faut avouer qu’il n’y a rien de très facile dans la mise en place des taches cron avec Magento, et que pour les paramétrer correctement, il est indispensable d’avoir connaissance de ces « subtilités », sans quoi vous pourriez vous retrouver dans des situations fâcheuses. Mais une fois maîtrisées, elles pourrons vous emmener loin dans vos développements.
Vos commentaires et retours d’expériences seront les bienvenus !
Notes :
- cron.php n’est pas le seul script à pouvoir être lancé par cron.sh. Il suffit pour cela d’appeler cron.sh suivi du nom du script à exécuter.
- Si vous ne pouvez pas modifier vous même la crontab, demandez à votre hébergeur
- La configuration est celle fournie avec l’installation de base
think you for information
Merci pour ton article. Il m’a permis de comprendre que mes scripts se superposaient. Je configurai ma tache cron sur cron.php… 🙂
Est ce que tu conseillerais de garder la configuration de base dans le backoffice, avec une tache cron serveur toutes les 15min ? Ou aurais tu une autre proposition ?