Le scénario est classique. Tout tourne bien depuis des mois, tu as des workers Laravel qui consomment ta queue Redis, et puis un jour tu actives Redis Cluster (ou tu passes sur ElastiCache Serverless / Valkey) et là, c’est la fête : tes workers tombent sur un CROSSSLOT en pleine prod. Le pire, c’est que ça ressemble à un bug « aléatoire ». En réalité, Redis Cluster est juste en train d’appliquer ses règles, et Laravel (avant 13.5) pouvait les casser sans le vouloir via ses scripts Lua.
Ici je te donne la version terrain : ce que signifie vraiment CROSSSLOT, pourquoi les queues et le Concurrency Limiter sont des candidats parfaits pour se planter, et comment aborder un upgrade vers Laravel 13.5 sans découvrir les pièges au moment où ça brûle.
CROSSSLOT en Redis Cluster : ce n’est pas une « erreur Redis », c’est une règle
En Redis Cluster, les clés sont réparties sur des nœuds via des hash slots. Quand tu envoies une commande qui touche plusieurs clés, Redis exige que ces clés soient dans le même slot. Sinon il ne sait pas exécuter l’opération de manière atomique sur un seul nœud, et il te renvoie l’erreur qui fait mal : CROSSSLOT Keys in request don't hash to the same slot.
Et c’est là que le piège est vicieux : toi, en tant que dev Laravel, tu n’écris pas forcément de commandes multi-key. Tu utilises une queue, Horizon, un limiter, et Laravel envoie des opérations « plus complexes » sous le capot. Typiquement, des scripts Lua qui orchestrent plusieurs clés (liste, zset de delayed, compteur de réservations, mutex, etc.). Un script Lua, côté Redis Cluster, n’a pas de passe-droit. Si le script touche des clés qui ne tombent pas dans le même slot, c’est CROSSSLOT, même si « sur Redis standalone » ça aurait marché toute la journée.
Sponsorisé par Le Scribouillard
Besoin de contenu optimisé SEO ?
Utilisez la meilleure plateforme française de création de contenu assistée par IA ! Et générez des articles pour moins de 1€ !
Pourquoi les queues Laravel déclenchent CROSSSLOT (et pourquoi ça arrive d’un coup)
Une queue Redis n’est pas « une clé ». C’est généralement une petite famille de clés. Même si tu ne connais pas le détail exact selon versions et options, l’idée est toujours la même : il y a la structure principale (liste), et des structures annexes (delayed, reserved, notifications, verrous). Dès que Laravel veut faire une opération atomique qui doit déplacer un job d’une structure à une autre (pop + réserve, release + delay, etc.), il a une bonne raison d’utiliser Lua.
Tant que tu es sur un Redis non clusterisé, Redis s’en fiche : toutes les clés sont sur le même serveur. Dès que tu passes en Cluster, tu introduces une contrainte de routage. Et si tes clés associées à une même queue se retrouvent à hasher différemment (parce qu’elles ont des suffixes différents, des prefixes différents, ou parce que ton application mélange plusieurs conventions), Redis refuse l’exécution atomique.
Le côté « ça arrive d’un coup » vient souvent de là : la même application, le même code, mais une infra qui passe de « une instance Redis » à « Redis Cluster », ou un provider managé qui te met un cluster derrière. En dev, sur ton Redis local, tu ne verras jamais le problème. En prod, tu le vois immédiatement… quand les workers commencent à consommer pour de vrai.
Les symptômes en prod : workers qui crashent, retries qui explosent, Horizon qui ment par omission
Le signe le plus obvious, c’est l’erreur CROSSSLOT dans les logs des workers. Parfois c’est noyé sous des retries, parfois c’est un job qui « disparaît » puis revient parce qu’il n’a jamais été correctement réservé. Quand c’est moche, tu vois la latence de queue monter, des timeouts applicatifs, et des jobs qui repassent en boucle parce que l’ack / le move atomique n’a pas pu se faire.
Horizon peut te donner l’illusion que « ça tourne », parce que le process vit, mais il passe son temps à échouer sur certaines opérations. Et si tu as des backoffs ou des delays, l’impact n’est pas forcément immédiat : tu peux te faire réveiller plus tard, quand le volume augmente ou quand un type de job particulier (avec release/delay) devient fréquent.
Le vrai mécanisme Redis Cluster à connaître : les hash tags qui forcent le même slot
Redis Cluster a un mécanisme prévu pour ça : les hash tags. Si une clé contient un fragment entre accolades, Redis ne hash que ce fragment pour déterminer le slot. Du coup, tu peux garantir que plusieurs clés « liées » tombent dans le même slot, même si elles ont des préfixes/suffixes différents.
Exemple simple, juste pour voir l’idée :
# Sans hash tag : ces clés peuvent tomber dans des slots différents
queues:emails
queues:emails:reserved
queues:emails:delayed
# Avec hash tag : tout ce qui a {emails} tombe dans le même slot
queues:{emails}
queues:{emails}:reserved
queues:{emails}:delayedSi tu retiens un truc de l’article, retiens ça. CROSSSLOT, dans 90% des cas, c’est « des clés censées vivre ensemble, mais qui n’ont pas un hash tag commun ».
Laravel 13.5 : ce qui change vraiment (support “cluster-aware” des queues et du limiter)
Laravel 13.5 s’attaque enfin à ce problème côté framework : le comportement Redis utilisé pour les queues (et le Concurrency Limiter) devient plus compatible Redis Cluster. Concrètement, l’objectif est d’éviter les scripts Lua qui manipulent plusieurs clés réparties sur plusieurs slots, en s’assurant que les clés associées à une même opération « tombent ensemble » dans le cluster.
Je le dis comme je le pense : c’était un pain récurrent sur des setups modernes (AWS ElastiCache Serverless / Valkey, clusters managés, etc.). Et c’est le genre de bug qui donne une mauvaise réputation à une stack, alors que le problème est surtout une inadéquation entre un modèle « single Redis » et une contrainte « cluster ».
Par contre, ne sur-vends pas la release dans ta tête. Laravel 13.5 peut corriger le comportement du driver, mais il ne peut pas deviner que toi tu as deux apps qui partagent le même cluster avec des prefixes différents, ou que tu utilises une config Redis « hybride » selon les environnements. Si ton naming de clés est incohérent, tu peux encore te tirer une balle.
Ce que Laravel 13.5 ne corrigera pas à ta place : prefixes, connexions, et “database index”
Le piège numéro un que je vois, c’est le prefix Redis. En standalone, tu peux te permettre un prefix différent entre apps, ou un prefix injecté par environnement, sans trop réfléchir. En Redis Cluster, si tes opérations Lua / multi-key reposent sur des clés qui doivent partager un hash tag, tu veux que ce hash tag reste stable. Si ton prefix « sort » du pattern attendu, ou si tu as des clés générées par deux libs qui n’ont pas la même convention, tu reviens au CROSSSLOT.
Deuxième piège : plusieurs connexions Redis qui pointent en réalité vers des backends différents selon l’environnement. Exemple typique : en dev tu as redis en standalone, en prod tu as redis en cluster, mais tu as aussi une connexion cache qui n’est pas alignée, ou un client différent (phpredis vs predis) selon les machines. Résultat, tu debugges « un CROSSSLOT » alors que tu es juste en train de taper des nœuds/clusters différents selon le code path.
Troisième piège, plus brutal : le database index (le fameux SELECT 1, database => 1 côté config). Redis Cluster ne fonctionne pas comme un Redis standalone multi-DB. En cluster, dans la pratique, tu veux rester sur DB 0, point. Si tu as des restes de config hérités d’une époque où tu séparais « cache DB1, queue DB2 », tu es en terrain glissant. Ça ne produit pas toujours un CROSSSLOT directement, mais ça produit du bizarre, et le bizarre en prod c’est rarement un bon plan.
Audit rapide avant upgrade : comment savoir si tu es exposé à CROSSSLOT
Je préfère les audits simples et bêtes. D’abord, cherche CROSSSLOT dans tes logs applicatifs et ceux des workers. Si tu en as déjà, tu as un signal. Ensuite, regarde si tes opérations « à risque » existent : jobs avec delay, release, retry, rate limiting et concurrency limiting côté Redis. Les queues « happy path » (push, pop sans options) peuvent parfois passer entre les gouttes, puis casser quand un cas plus rare arrive.
Si tu veux être un peu plus méthodique, fais un test sur un environnement staging réellement clusterisé. Pas un Redis local, pas un container unique. Un vrai cluster, même petit. Lance un volume de jobs qui force des transitions : jobs qui échouent et sont retry, jobs release() avec delay, jobs avec middleware de throttling, etc. Si ça doit casser, ça cassera là.
Stratégie de déploiement Laravel 13.5 : canary, rollback, et métriques qui parlent
Le « bon » déploiement ici, c’est celui où tu évites de changer tout le comportement de ta queue d’un coup sur 30 workers. Fais un canary : tu upgrades un petit pool de workers (ou une seule instance), tu les pointes sur la même queue, et tu regardes si le monde reste cohérent. L’objectif n’est pas juste « plus de CROSSSLOT », c’est aussi « pas de double-consommation », « pas de jobs fantômes », « pas de latence qui explose ».
Niveau métriques, garde les yeux sur le taux d’échec des jobs, le nombre de retries, et le temps moyen de traitement. Si tu as Horizon, surveille la latence par queue et le throughput. Si tu as un APM, regarde les spans Redis et les timeouts. Je te conseille aussi d’avoir un rollback simple côté infra: pouvoir redéployer l’ancienne version de workers rapidement, sans avoir à rejouer une migration ou à toucher à Redis.
ElastiCache Serverless / Valkey : les détails qui te font perdre une heure (TLS, timeouts, clients)
Sur AWS, le combo « cluster managé + sécurité + latence réseau » rend certains défauts plus visibles. Déjà, valide ton client Redis. Que tu sois sur phpredis ou predis, assure-toi que ton mode cluster et ta config TLS correspondent à ce que fournit le service (endpoints, ports, vérification de certificat si activée, etc.). Une partie des incidents « Redis Cluster est instable » sont juste des timeouts mal réglés ou des connexions qui churnent.
Ensuite, sois cohérent sur les timeouts. Entre le connect timeout, le read timeout, les retry policies, et la charge côté workers, tu peux te retrouver avec un faux positif : tu crois que CROSSSLOT est la cause principale, alors que tu as surtout des timeouts Redis et des scripts Lua interrompus parce que ton worker est à la ramasse. Redis Cluster + queues, ça aime bien les réglages boring mais sérieux.
Les erreurs fréquentes que je vois (et qui reviennent même après la “fix”)
La plus fréquente, c’est de mélanger des conventions de clés entre plusieurs apps qui partagent le même cluster, ou entre « queue » et « cache » via des prefixes bricolés. La deuxième, c’est de tester sur un Redis standalone puis de considérer que « c’est bon ». Non. Un cluster se comporte différemment, et le but n’est pas d’avoir un code qui marche « chez toi », mais un code qui respecte les contraintes du backend.
Et la dernière, plus subtile : faire l’upgrade Laravel, puis oublier que des composants autour (Horizon, packages de rate limiting, libs maison) envoient aussi des commandes Redis multi-key ou des scripts. Laravel peut améliorer sa partie, mais si tu as un package qui fait un EVAL multi-key sans hash tags, tu peux te retrouver avec un CROSSSLOT « après upgrade » et accuser la mauvaise chose.
Conclusion : Redis Cluster, c’est solide… tant que tu joues selon ses règles
Redis Cluster n’est pas fragile. Il est strict. Et il a raison : la distribution des clés impose de réfléchir à l’atomicité et au routage. Laravel 13.5 va clairement dans le bon sens en rendant les queues et le concurrency limiting plus adaptés au monde clusterisé. Mais la meilleure manière de profiter de la fix, c’est d’arriver avec un setup propre : conventions de clés cohérentes, prefixes maîtrisés, staging clusterisé, et déploiement progressif.
Si tu as déjà vécu un réveil CROSSSLOT à 2h du mat, tu sais le vrai confort : celui où tu n’as plus besoin d’y penser. C’est exactement l’objectif. Pas « comprendre Redis dans l’absolu ». Juste arrêter de te faire surprendre.