Protégez vos files d'attente avec Laravel Fuse
Stripe est down. Et vos 10 000 jobs Laravel continuent de s'exécuter, d'attendre 30 secondes en timeout, de retry, de re-timeout... jusqu'à ce que votre queue soit complètement figée pour les 25 prochaines heures.
C'est exactement le problème que résout Laravel Fuse.
Le problème : la cascade d'échecs
Dans une application Laravel en production, vos jobs communiquent souvent avec des services tiers : API de paiement, service d'emailing, microservices internes. Ces services peuvent tomber. Et quand ils tombent, sans protection, voici ce qui se passe :
- Chaque job tente d'appeler le service
- Il attend le timeout (souvent 30 secondes)
- Il échoue, retry, retimeout
- Des centaines de jobs s'accumulent dans la queue
- Vos workers sont saturés pour rien
- Vos vraies priorités ne passent plus
La solution connue dans le monde du génie logiciel s'appelle le Circuit Breaker pattern.
Le pattern Circuit Breaker
Le concept vient littéralement de l'électricité. Un disjoncteur surveille le courant et coupe le circuit si quelque chose cloche, pour éviter la surtension.
En logiciel, c'est pareil. Le circuit breaker surveille le taux d'échec de vos appels vers un service tiers et passe par trois états :
CLOSED — Tout fonctionne normalement. Les jobs s'exécutent, le courant passe.
OPEN — Trop d'échecs ont été détectés. Le circuit s'ouvre et les jobs échouent instantanément (en 1ms, pas 30 secondes) sans même appeler le service externe. Ils sont remis en attente automatiquement pour être rejoués plus tard.
HALF-OPEN — Après un délai configurable, le système envoie une requête "sonde" pour tester si le service est revenu. Si oui, le circuit repasse en CLOSED. Sinon, retour en OPEN.
Laravel Fuse implémente ce pattern directement dans votre stack Laravel, sans dépendance externe.
Installation
1composer require harris21/laravel-fuse2php artisan vendor:publish --tag=fuse-config
Intégration dans un job
L'intégration se fait via un middleware sur votre job :
1use Harris21\Fuse\Middleware\CircuitBreakerMiddleware; 2 3class ChargeCustomer implements ShouldQueue 4{ 5 public $tries = 0; // Unlimited releases 6 public $maxExceptions = 3; // Seules les vraies erreurs comptent 7 8 public function middleware(): array 9 {10 return [new CircuitBreakerMiddleware('stripe')];11 }12 13 public function handle(): void14 {15 Stripe::charges()->create([...]);16 }17}
C'est tout. Votre job est maintenant protégé.
Configuration
Le fichier config/fuse.php vous permet de paramétrer finement le comportement par service :
1return [ 2 'default_threshold' => 50, // % d'échec pour déclencher l'ouverture 3 'default_timeout' => 60, // Secondes avant de tester la reprise 4 'default_min_requests' => 10, // Minimum de requêtes avant d'évaluer 5 6 'services' => [ 7 'stripe' => [ 8 'threshold' => 50, 9 'timeout' => 30,10 'min_requests' => 5,11 12 // Tolérance plus haute en heures de pointe13 'peak_hours_threshold' => 60,14 'peak_hours_start' => 9,15 'peak_hours_end' => 17,16 ],17 'mailgun' => [18 'threshold' => 60,19 'timeout' => 120,20 'min_requests' => 10,21 ],22 ],23];
Chaque service dispose de son propre circuit breaker indépendant. Si Mailgun tombe, Stripe n'est pas impacté.
Classification intelligente des erreurs
Toutes les erreurs ne signifient pas que le service est down. Fuse fait la distinction :
✅ Erreurs comptabilisées — le service est probablement en cause :
500,502,503— le serveur a un problème- Timeout de connexion — le service est injoignable
❌ Erreurs ignorées — le service fonctionne, le problème vient d'ailleurs :
429 Too Many Requests— c'est vous qui spammez401 Unauthorized— problème de configuration403 Forbidden— problème de permissions
Concrètement : si vous atteignez la rate limit de Stripe, le circuit ne s'ouvre pas. Stripe fonctionne, c'est votre code qui envoie trop de requêtes.
Classification personnalisée
Certains services ont des comportements non-standard. Stripe, par exemple, retourne parfois un 500 pour des erreurs d'idempotence qui n'ont rien à voir avec une panne. Vous pouvez créer votre propre classifier :
1namespace App\Fuse; 2 3use Harris21\Fuse\Classifiers\DefaultFailureClassifier; 4use GuzzleHttp\Exception\ServerException; 5use Throwable; 6 7class StripeFailureClassifier extends DefaultFailureClassifier 8{ 9 public function shouldCount(Throwable $e): bool10 {11 if ($e instanceof ServerException) {12 $body = (string) $e->getResponse()?->getBody();13 14 if (str_contains($body, 'idempotency')) {15 return false; // Pas une vraie panne16 }17 }18 19 return parent::shouldCount($e);20 }21}
Puis dans la config :
1'stripe' => [2 'threshold' => 50,3 'failure_classifier' => \App\Fuse\StripeFailureClassifier::class,4],
Écouter les événements
Fuse dispatche des événements Laravel à chaque transition d'état. Pratique pour vos alertes Slack ou vos logs :
1use Harris21\Fuse\Events\CircuitBreakerOpened; 2 3class AlertOnCircuitOpen 4{ 5 public function handle(CircuitBreakerOpened $event): void 6 { 7 Log::critical("Circuit ouvert pour {$event->service}", [ 8 'failure_rate' => $event->failureRate, 9 'attempts' => $event->attempts,10 'failures' => $event->failures,11 ]);12 13 // Notifier l'équipe via Slack, PagerDuty, etc.14 }15}
Les trois événements disponibles sont CircuitBreakerOpened, CircuitBreakerHalfOpen et CircuitBreakerClosed.
Dashboard de monitoring en temps réel
Fuse embarque une page de statut intégrée. Activez-la dans votre .env :
1FUSE_STATUS_PAGE_ENABLED=true
Elle est accessible sur /fuse et affiche en temps réel l'état de chaque circuit, le taux d'échec, l'historique des transitions et l'heure de la prochaine tentative de reprise. Elle se rafraîchit automatiquement toutes les 2 secondes.
L'accès est protégé par une gate Laravel viewFuse, que vous pouvez surcharger dans votre AppServiceProvider :
1Gate::define('viewFuse', function ($user = null) {2 return $user?->isAdmin();3});
Utilisation directe (hors jobs)
Le circuit breaker peut aussi être utilisé en dehors de la queue, pour des appels synchrones :
1use Harris21\Fuse\CircuitBreaker; 2 3$breaker = new CircuitBreaker('stripe'); 4 5if (!$breaker->isOpen()) { 6 try { 7 $result = Stripe::charges()->create([...]); 8 $breaker->recordSuccess(); 9 return $result;10 } catch (Exception $e) {11 $breaker->recordFailure($e);12 throw $e;13 }14} else {15 // Stratégie de fallback16 return $this->fallbackResponse();17}
Stratégies de fallback
Quand le circuit s'ouvre, votre application a besoin d'un plan B. Quelques approches courantes :
Données en cache — Retourner la dernière valeur connue. Des données légèrement obsolètes valent mieux qu'une erreur 500.
Service de secours — Basculer sur un prestataire alternatif, ou marquer la commande "en attente" pour traitement ultérieur.
Dégradation gracieuse — Masquer la fonctionnalité si elle n'est pas critique. Une page qui charge sans les recommandations vaut mieux qu'une page blanche.
Re-queue différé — Fuse le fait déjà nativement avec release(). Vos jobs sont remis en attente, pas perdus définitivement.
Prérequis
- PHP 8.3+
- Laravel 11+
- Redis recommandé en production (le cache fichier peut avoir des race conditions lors de la phase HALF-OPEN)
Conclusion
Laravel Fuse apporte une solution élégante et native à un problème qui peut paralyser une application en production. Le setup est minimal, la configuration est flexible, et le comportement est automatique une fois en place.
Sans lui : 10 000 jobs × 30 secondes de timeout = plus de 25 heures pour vider la queue.
Avec lui : le circuit s'ouvre après quelques échecs, la queue se vide en quelques secondes, et la reprise est automatique dès que le service revient.
Le package a été créé par Harris Raftopoulos et présenté à Laracon India 2026. Le code source est disponible sur GitHub sous licence MIT.
1composer require harris21/laravel-fuse
Source : https://github.com/harris21/laravel-fuse
Tu veux commenter ? Crée un compte ou connecte-toi.
A lire
Autres articles de la même catégorie
Exploitez l'IA pour l'analyse et l'extraction de texte
Utilisez la puissance de l'IA pour analyser et extraire des informations à partir d'un document !
Mathieu De Gracia
Découverte de NativePHP
Transformez votre Laravel en application desktop !
Mathieu De Gracia
php-arguments-detector
Les arguments peuvent s'avérer problématique pour la complexité de vos méthodes, ce package vous aidera à garder le contrôle.
Mathieu De Gracia