Bonjour à tous. Je fais des recherches sur la récupération après des défaillances partielles dans n8n et je veux confirmer le comportement au-delà de la documentation.
La documentation (Understand executions) indique qu’une nouvelle tentative reprend à partir de l’état d’exécution enregistré au lieu de recommencer à partir du déclencheur, les nœuds terminés conservent leurs résultats, et le nœud défaillant traite chaque élément à nouveau, de sorte qu’un nœud qui a envoyé certains messages avant défaillance les envoie à nouveau. Elle indique également qu’un nœud Execute Sub-workflow défaillant relance la totalité du sous-flux de travail.
Ce que j’aimerais confirmer à partir de l’expérience :
Un appel unique qui atteint le service mais retourne une erreur ou expire : l’action est-elle exécutée deux fois à la nouvelle tentative ?
Un nœud multi-élément qui échoue à l’élément 3 : les éléments 1 et 2 sont-ils répétés à la nouvelle tentative ?
Nouvelle tentative d’API publique (POST /executions/{id}/retry, loadWorkflow vrai ou faux) : même point de redémarrage que l’interface utilisateur ?
Flux de travail d’erreur qui déclenche une nouvelle tentative : un moyen de la plafonner à une seule tentative ?
Paramètre du nœud Retry On Fail par rapport à la nouvelle tentative d’exécution : même effet sur les actions répétées ?
Veuillez indiquer la version et le mode de n8n que vous avez testés (régulier ou en file d’attente), et si vous l’avez observé ou si vous citez la documentation. Tout modèle de protection que vous utilisez (clé d’idempotence, vérification d’existence avant l’action, table des identifiants traités) serait également utile.
Bonjour @faridd692, bienvenue dans la communauté n8n.
Oui, réessayer un appel arrivé à expiration peut dupliquer l’action si le service l’a confirmée avant le délai d’expiration. Je cite ici la source n8n@2.45.7, ce n’est pas un signalement de reproduction régulière/en file d’attente. Pour un nœud HTTP/message ordinaire qui s’arrête en cas d’erreur, l’invocation échouée reçoit à nouveau son entrée, y compris les éléments 1 et 2. Retry On Fail réessaye au sein de la même exécution; l’exécution retry crée une nouvelle exécution en utilisant l’état enregistré.
L’interface utilisateur et l’appel API public font appel au même service de réessai. loadWorkflow: true remplace les définitions de workflow/nœud utilisées avec cet état enregistré; false conserve les définitions d’origine. Cela ne change pas le point de redémarrage vers le déclencheur.
Dans votre Error Workflow, appelez retry uniquement quand execution.id existe et execution.retryOf est absent. Si ce retry échoue, alertez au lieu de réessayer à nouveau. Gardez Retry On Fail désactivé si vous voulez seulement une tentative de nœud supplémentaire. Utilisez une clé d’idempotence prise en charge par le fournisseur par action prévue, par exemple l’ID d’événement d’origine plus l’action et le destinataire/élément, et réutilisez-la à chaque tentative. Ne basez pas cette clé sur $execution.id, qui change lors du retry d’exécution. Un nœud de vérification d’existence séparé avant le nœud échoué ne s’exécutera pas à nouveau avant cette invocation réessayée.
Ajout d’une reproduction à l’explication de @Deepankar : j’ai testé n8n 2.45.7, mode régulier, SQLite, HTTP Request 4.2, contre un service fictif en localhost. Le mode queue n’a pas été testé.
Résultats observés :
Le service a enregistré une écriture, puis a retourné 500 ou s’est expirée : la retry d’exécution a produit une deuxième écriture.
Échec à l’élément 3 : le récepteur a enregistré 1,2,3,1,2,3. Le nœud amont terminé s’est exécuté une seule fois.
Avec un graphique inchangé, API publique loadWorkflow: true, false, et le point de terminaison de retry de l’UI ont préservé ce même point de redémarrage.
Retry On Fail avec maxTries: 2 a répété le batch dans une seule exécution. La retry d’exécution a créé une nouvelle exécution.
Un nœud Execute Sub-workflow échoué a réexécuté l’enfant, y compris son marqueur déjà complété.
Un Error Workflow vérifiant que execution.id existe et execution.retryOf est absent a permis une exécution supplémentaire. La retry échouée a invoqué le gestionnaire à nouveau, mais l’IF a arrêté une autre retry. Les appelants de retry concurrents ont toujours besoin d’une revendication atomique.
Avoir le récepteur fictif appliquer la même clé d’idempotence par action a donné deux requêtes mais une seule écriture.
Un détail supplémentaire de 2.45.7 : j’ai dû publier l’Error Workflow avant qu’il ne s’exécute. Le gestionnaire non publié n’a produit aucune exécution du gestionnaire ; le chargeur de cette version nécessite des données de workflow publiées.
Un cas que je modéliserais explicitement est un résultat ambigu : l’API externe expire, mais l’écriture a peut-être déjà été validée
Je maintiendrais cette opération dans un état « résultat inconnu » plutôt que de la marquer immédiatement comme échouée. La récupération concilierait d’abord le fournisseur en utilisant une référence commerciale stable, puis ne relancerait l’opération que si elle est sûre. Si le fournisseur ne dispose pas d’une recherche fiable ou du support de l’idempotence, je l’acheminerais pour examen plutôt que de risquer une mise à jour CRM dupliquée ou une action sortante
Cette distinction entre l’échec d’exécution et l’échec de l’opération commerciale est très importante dans les flux de travail en production
@faridd692 concernant la partie du mode queue, j’ai lu le côté worker à n8n@2.45.7 (source à nouveau, pas une reproduction queue). Le worker charge l’exécution de nouvelle tentative depuis la base de données et l’exécute à partir de la même pile de nœuds sauvegardée, donc le point de redémarrage et les éléments répétés sont identiques au mode normal.
Les deux événements queue-only ne rejouent pas non plus un appel terminé. Bull est configuré avec maxStalledCount: 0, donc un worker qui s’arrête au milieu d’un nœud échoue le travail plutôt que de le passer à un autre worker, et un worker n’exécutera pas une exécution déjà marquée comme plantée. Un arrêt gracieux du worker peut garer une nouvelle tentative en cours, mais seulement à une limite de nœud : le nœud actuel se termine et un autre worker poursuit à partir du suivant.