Votre cron d'export comptable tourne « toutes les heures ». Un matin, la compta reçoit deux fichiers identiques avec des écritures dupliquées. En creusant : le job de 08:00 n'avait pas fini quand celui de 09:00 a démarré — même script, même table temporaire, aucun mutex.
Sur un VPS unique, c'est classique. Sur une infra qui scale, c'est pire : deux serveurs exécutent le même crontab parce que personne n'a centralisé le planificateur.
Pourquoi les crons se chevauchent
| Cause | Scénario | Symptôme |
|---|---|---|
| Job trop long | Import > intervalle | Doublons, deadlocks DB |
| Relance manuelle | Admin + cron | Deux processus parallèles |
| Multi-serveur | crontab copié sur 2 VPS | Double facturation clients |
| Retry hébergeur | Timeout PHP kill + relance | Partial commit × 2 |
Un cron n'est pas « idempotent » par défaut — c'est vous qui devez le prouver ou le verrouiller.
flock : verrou fichier sur un seul hôte
* * * * * flock -n /var/lock/sync.lock /usr/local/bin/sync.sh >> /var/log/sync.log 2>&1
-n: non-blocking — skip si lock pris (préférable à une file d'attente infinie).- Lock sur disque local partagé si plusieurs utilisateurs — pas NFS instable pour locks critiques.
Alternative PHP :
$fp = fopen('/var/lock/job.lock', 'c');
if (!flock($fp, LOCK_EX | LOCK_NB)) {
exit(0);
}
// travail...
Lock distribué : Redis et PostgreSQL
Redis (pattern court, TTL obligatoire) :
SET lock:export $(uuid) NX EX 3600
Libérez avec script Lua compare-and-del pour éviter de supprimer le lock d'un autre processus.
PostgreSQL advisory lock — pratique si le job touche déjà la DB :
SELECT pg_try_advisory_lock(12345);
Le lock libère à la fin de la session — pratique pour jobs SQL longs.
Architecture : cron déclencheur vs worker unique
Pour jobs lourds, le cron ne fait qu'enqueue :
- Cron minute → push job ID dans Redis queue.
- Un seul worker consumer traite la file.
- Visibilité timeout sur le message si crash.
Laravel Horizon, Sidekiq, Celery beat — même principe. Le cron devient un réveil, pas l'exécution elle-même.
Surveillance et journaux
Loggez début et fin avec PID, durée et statut. Corrélez avec les métriques base de données — deadlocks et lignes dupliquées sont souvent le premier signal. Sur mutualisé, vérifiez le fuseau horaire serveur et les limites PHP avant d'accuser le code.
Le sommet : l'idempotence ne remplace pas le verrou — elle le complète
Les deux ensemble tiennent la nuit du Black Friday.
Décider et avancer sans angle mort
Auditez chaque cron planifié : comparez la durée maximale observée à l'intervalle configuré. Ajoutez flock immédiatement sur tout VPS mono-nœud. Centralisez le planificateur si vous avez plusieurs serveurs — un seul nœud cron, ou lock Redis partagé. Loggez début et fin avec PID pour corréler les doublons. Consultez notre guide hébergement pour les limites cron et PHP selon l'offre.
Questions fréquentes
flock suffit-il pour verrouiller un cron bash ?
Oui sur un seul serveur : flock -n /var/lock/monjob.lock /chemin/script.sh empêche une seconde instance concurrente. Le lock disparaît si le processus meurt — vérifiez que le script ne fork sans libérer le lock.
Comment verrouiller un cron sur plusieurs serveurs ?
Utilisez un lock distribué : Redis SET NX EX, PostgreSQL advisory lock, ou DynamoDB lock. flock ne fonctionne pas entre machines.
Que faire si le job précédent dure plus longtemps que l'intervalle cron ?
Soit augmentez l'intervalle, soit autorisez le skip (flock -n qui exit 0), soit queuez le travail (worker unique). Ne laissez jamais deux imports massifs se chevaucher.
Les crons mutualisés ont-ils des pièges spécifiques ?
Oui : heure serveur vs heure locale, chemins PHP différents, timeout kill silencieux, et plusieurs nœuds sans coordination si l'hébergeur scale l'infrastructure sans vous le dire.
Un cron sans verrou, c'est une roulette : un jour la tâche finit deux fois — le jour où vous ne regardez pas les logs.
