Salut à tous,
J’exécute n8n en mode queue avec plusieurs workers, et je rencontre un problème étrange après le redémarrage des workers ou des déploiements.
Ma configuration : Webhook → Queue → Worker → Process → Database
Parfois après un redémarrage d’un worker :
Les anciens jobs sont traités à nouveau
Certains jobs semblent « bloqués » et réessaient plus tard de manière inattendue
Quelques exécutions créent des écritures BD/appels API en doublon
J’utilise déjà les retries et la gestion d’erreurs basique, mais je pense que le problème est lié à la façon dont les jobs sont acquittés ou récupérés après un crash de worker.
Exemple de logique de traitement :
if ($json.status !== “processed”) {
// continuer le traitement
}
J’essaie de comprendre :
• Comment le mode queue de n8n gère les jobs inachevés après un redémarrage
• Si les jobs sont remis en queue automatiquement
• La meilleure façon de rendre les workflows sûrs contre la duplication d’exécution après les crashes
Pour les personnes utilisant le mode queue en production :
• Quel est le modèle recommandé pour la récupération après crash et le traitement idempotent ?
Décrivez le problème/l’erreur/la question
Quel est le message d’erreur (le cas échéant) ?
Veuillez partager votre workflow
(Sélectionnez les nœuds sur votre canevas et utilisez les raccourcis clavier CMD+C/CTRL+C et CMD+V/CTRL+V pour copier et coller le workflow.)
Salut @Decoure_Ryan Ce que tu vois est généralement un comportement normal en mode file d’attente. Si un worker s’arrête ou redémarre avant qu’une tâche soit complètement exécutée/reconnue, la file d’attente peut marquer cette tâche comme inachevée et la retraiter plus tard. C’est pourquoi tu vois d’anciennes tâches s’exécuter à nouveau.
Essaie de supposer que les tâches peuvent s’exécuter plusieurs fois et rends le traitement idempotent.
Par exemple, avant le traitement :
if ($json.status === “processed”) {
return ;
}
Et utilise une protection au niveau de la base de données comme :
ON CONFLICT DO NOTHING
ou des clés uniques pour éviter les insertions en double.
Moyens courants que tu peux appliquer en production
La file d’attente gère les tentatives/la récupération
La base de données gère la déduplication/l’idempotence
Les workers restent sans état
Bienvenue @Decoure_Ryan dans notre communauté ! Je m’appelle Jay et je suis un créateur vérifié n8n.
Pour ajouter à ce que Niffzy a dit - la cause racine est le mécanisme de récupération des « jobs stalled » (jobs figés) de Bull. Quand un worker redémarre sans terminer gracieusement un job, Bull marque ce job comme figé après QUEUE_BULL_STALLED_INTERVAL millisecondes (30000ms par défaut) et le remet en file d’attente. Vous pouvez ajuster cela avec QUEUE_BULL_MAX_STALLED_COUNT=1 pour limiter le nombre de fois qu’un job figé est réessayé, et QUEUE_BULL_STALLED_INTERVAL pour contrôler la fenêtre de détection. Pour l’idempotence au niveau de n8n, utilisez $getWorkflowStaticData ou une vérification du statut en base de données au tout début du workflow pour court-circuiter si l’execution_id a déjà été traité. Définir une contrainte d’unicité sur execution_id dans votre base de données est la protection la plus fiable.
Redis agit comme broker et les workers exécutent les jobs, mais je ne garantirais pas exactly-once; après un crash/restart, traitez comme at-least-once, révisez N8N_GRACEFUL_SHUTDOWN_TIMEOUT et concevez le workflow pour gérer le retraitement.
(je ne crie pas, c’est juste pour mettre l’accent )
UTILISEZ TOUJOURS UNE CLÉ UNIQUE
99% des problèmes pourraient être résolus avec cela
Excellente analyse de @syed_noor. J’ajouterais une chose : BullMQ possède aussi un paramètre lockDuration (30s par défaut) — si votre workflow prend plus de temps que ça, le verrou expire et le job est marqué comme figé même s’il est encore en cours d’exécution. Vous pouvez l’augmenter via QUEUE_BULL_STALLED_INTERVAL comme mentionné, mais assurez-vous aussi que lockDuration est configuré correctement dans votre configuration BullMQ.
À noter également — l’approche de clé d’idempotence Postgres est le pattern le plus fiable que j’ai vu en production. Combinez-la avec le nœud « Stop and Error » de n8n après la vérification INSERT pour quitter proprement les exécutions dupliquées sans polluer vos journaux d’erreurs.
Bonne remarque sur la distinction lockDuration — j’aurais dû le souligner séparément. QUEUE_BULL_STALLED_INTERVAL contrôle la fréquence d’exécution du vérificateur, mais lockDuration détermine la durée pendant laquelle un job peut rester actif avant d’être considéré comme bloqué. Les deux doivent dépasser votre temps d’exécution de workflow le plus long.
Le conseil sur le nœud Stop et Error est solide aussi. Je l’utilise après l’INSERT d’idempotence avec le message défini sur job_id — de cette façon, quand vous examinez les exécutions dans n8n, vous pouvez immédiatement voir lesquelles étaient des doublons légitimes par rapport à des défaillances réelles. Cela garde la liste des exécutions propre au lieu d’afficher des erreurs faussement positives.
Pour tous ceux qui implémentent ce motif, j’ai écrit une analyse plus détaillée de l’ensemble des six dimensions de préparation pour la production (l’idempotence n’en est qu’une) ici :
L’identifiant job_id dans les messages Stop et Error est une excellente idée - cela accélère beaucoup le triage quand vous parcourez les exécutions. Une autre chose qui vaut la peine d’être ajoutée à ce modèle : définir continueOnFail sur le nœud de vérification d’idempotence et acheminer le chemin « déjà traité » vers un nœud No-op avec un nom clair (par exemple « DUPLICATE - skipped »), plutôt que de vous fier uniquement au chemin d’erreur. Cela garde le graphe d’exécution lisible et sépare les sauts attendus des véritables défaillances en un coup d’œil.