veilletech.fr
10 oct. Feed du jour
#06 LARAVEL Article

Laravel 13.35 : alwaysOnOneServer pour le scheduler

Une closure sans nom tourne partout. En silence.

Laravel 13.35 ajoute Schedule::alwaysOnOneServer(), qui exécute chaque tâche planifiée une seule fois quand schedule:run tourne sur plusieurs serveurs, et Schedule::hasBeenInterruptedSince(), qui laisse une commande longue s'arrêter proprement lors d'un déploiement. Deux pièges : les closures sans name() sont ignorées en silence, et le cache doit être partagé entre serveurs.

2 min de lectureintermédiairevidéo 1:27
Partager
Sommaire4 sections
  1. Ce qui se passe
  2. Une seule exécution, pour tout le planning
  3. Arrêter une commande longue au déploiement
  4. À retenir

Ce qui se passe

Dès que schedule:run tourne sur plus d'un serveur, chaque tâche planifiée s'exécute une fois par serveur. Sans importance pour vider un cache local, gênant pour un rapport quotidien envoyé par e-mail. onOneServer() existait, mais il fallait y penser tâche par tâche, et l'oubli ne se voit qu'au premier doublon.

Laravel 13.35 ajoute deux méthodes sur Schedule : alwaysOnOneServer() et hasBeenInterruptedSince().

Une seule exécution, pour tout le planning

À appeler une fois, dans boot() d'un service provider :

PHP
use Illuminate\Support\Facades\Schedule;

public function boot(): void
{
    Schedule::alwaysOnOneServer();
    // Store partagé par tous les serveurs, déclaré dans config/cache.php
    Schedule::useCache('redis');
}

Le drapeau s'applique quand le planning lit ses événements : les tâches déclarées plus tard dans routes/console.php sont couvertes. Il est désactivé par défaut ; Schedule::alwaysOnOneServer(false) le coupe. Le mécanisme reste celui de onOneServer() : un verrou dans le cache, le premier serveur à démarrer la tâche l'emporte.

Deux pièges, tous deux silencieux :

PHP
// tourne sur chaque serveur, même avec alwaysOnOneServer()
Schedule::call(fn () => Rapport::envoyer())->daily();

// tourne sur un seul serveur
Schedule::call(fn () => Rapport::envoyer())->name('rapport-quotidien')->daily();

Il n'y a pas d'exclusion par tâche : si certaines doivent tourner partout (vider des fichiers locaux), laissez le drapeau éteint et mettez onOneServer() sur les autres.

Arrêter une commande longue au déploiement

Avant 13.35, schedule:interrupt posait un drapeau valable jusqu'à la fin de la minute, que seul schedule:run lisait. Une commande lancée avec runInBackground() ne le voyait jamais : elle finissait sur l'ancien code ou se faisait tuer. L'heure de l'interruption est maintenant stockée, comme queue:restart le fait pour les workers :

PHP
$debut = now();

foreach (Produit::lazyById(500) as $produit) {
    if ($schedule->hasBeenInterruptedSince($debut)) {
        return self::SUCCESS;
    }
    $this->synchroniser($produit);
}

hasBeenInterruptedSince() ne renvoie true que pour une interruption à l'heure passée ou après : celle d'un déploiement précédent n'arrête pas un nouveau run. La commande doit pouvoir s'arrêter là où elle vérifie, et le passage suivant reprendre le travail restant. Autres réserves : l'heure vit dans le store par défaut, pas celui de useCache(), et avec Schedule::withoutInterruptionPolling() la méthode renvoie toujours false.

Source : Running Laravel's Scheduler on Multiple Servers, Laravel News, 9 octobre 2026. Contributions de Jack Bayliss (#61789) et de cyppe (#61764).