Auto-hébergement

Dans les entrailles du moteur d'exécution de n8n : comment fonctionne réellement le planificateur de workflows

Une analyse technique du modèle d'exécution interne de n8n, couvrant ses objets d'exécution sérialisables, sa logique de planification et son architecture de file d'attente pour l'automatisation auto-hébergée.

Illustration des données circulant à travers les nœuds de workflow sur un serveur
Illustration créée pour cet article

Traduit automatiquement depuis l’original en anglais.

n8n a gagné une traction significative en tant qu'alternative auto-hébergée aux plateformes d'automatisation commerciales, mais ses mécanismes internes restent opaques pour de nombreux utilisateurs. Une récente analyse technique révèle que la plateforme repose sur un unique objet d'exécution sérialisable et une boucle déterministe, plutôt que sur une gestion complexe des threads. Comprendre cette architecture aide les ingénieurs à déboguer les workflows et à dimensionner efficacement les déploiements.

Ce qui s'est passé

L'article propose une plongée approfondie dans le cœur du moteur de n8n, examinant spécifiquement la classe WorkflowExecute écrite en TypeScript. Il explique que lorsqu'un utilisateur déclenche un workflow, le système ne génère pas de threads individuels pour chaque nœud. Au lieu de cela, il construit un objet d'état complet appelé IRunExecutionData. Cet objet contient l'intégralité du contexte de l'exécution, y compris la pile des nœuds en attente d'exécution, une carte pour gérer les jonctions multi-entrées et les résultats historiques des étapes terminées.

Cet objet d'état est conçu pour être portable. Il se déplace de l'interface éditeur vers le serveur, et potentiellement dans une file d'attente Redis pour les processus workers. Le moteur traite cet objet dans une boucle, dépile les nœuds de la pile d'exécution, les exécute, enregistre leur sortie et met en file d'attente les dépendances en aval. Cette approche linéaire simplifie le modèle mental pour les développeurs, éliminant la nécessité de raisonner sur des pools de threads cachés ou des conditions de course asynchrones au sein même du graphe.

Détails clés

  • État sérialisable : L'objet IRunExecutionData transporte tout l'état d'exécution, permettant aux exécutions de mettre en pause, reprendre ou se déplacer entre les processus sans perdre le contexte.
  • Paramètres d'ordre d'exécution : Les utilisateurs peuvent choisir entre v0 (parcours en largeur) et v1 (parcours en profondeur), ce qui change si le moteur privilégie l'achèvement de tous les nœuds frères à un niveau donné ou le suivi d'une seule branche jusqu'à sa complétion.
  • Carte d'attente pour les jonctions : Les nœuds avec plusieurs entrées ne s'exécutent pas tant que toutes les données amont requises ne sont pas disponibles. Les données partielles sont stockées dans une carte d'attente jusqu'à ce que chaque emplacement nécessaire soit rempli.
  • Terminaison de branche : Si un nœud renvoie des données vides, la branche en aval s'arrête sauf si le drapeau alwaysOutputData est activé, ce qui force le moteur à fabriquer un élément vide pour maintenir le flux actif.
  • Architecture en mode file d'attente : Dans les configurations de production, le processus principal gère l'UI et les webhooks, tandis que des workers sans état tirent les jobs depuis Redis, exécutent la même boucle du moteur et écrivent les résultats dans PostgreSQL.
  • Licence Fair-Code : n8n utilise la Sustainable Use License, autorisant l'utilisation commerciale interne gratuite et la modification, mais interdisant la revente en tant que produit SaaS hébergé concurrent.

Contexte

Pour comprendre la conception de n8n, il est utile de distinguer le parcours de graphe de la planification des tâches. De nombreux outils d'automatisation visuels abstraitent l'ordre des opérations, conduisant à des comportements imprévisibles lorsque les branches fusionnent ou se divisent. n8n expose cette logique via ses paramètres d'ordre d'exécution. L'exécution par parcours en largeur garantit que toutes les tâches parallèles à une profondeur donnée se terminent avant de passer plus profondément, ce qui est utile pour le traitement par lots. L'exécution par parcours en profondeur suit entièrement un chemin avant de revenir en arrière, ce qui peut réduire l'utilisation mémoire pour les longues chaînes.

Le concept de « fair-code » est également central à l'adoption de n8n. Contrairement aux licences purement open source, le fair-code permet aux entreprises d'utiliser et de modifier librement le logiciel pour leurs propres opérations internes. Cependant, il restreint les entités offrant le logiciel comme service managé à des tiers. Ce modèle soutient une communauté dynamique d'auto-hébergeurs tout en protégeant les intérêts commerciaux des mainteneurs principaux.

Pourquoi c'est important

Pour les équipes gérant leur propre infrastructure, la prévisibilité est plus précieuse que la vitesse brute. Savoir que n8n utilise une boucle mono-thread pour chaque exécution signifie que le débogage est déterministe. Si un workflow échoue, l'objet d'état capture exactement ce qui s'est passé à chaque étape. Les ingénieurs peuvent inspecter runData pour voir l'entrée et la sortie de chaque nœud, facilitant l'identification des erreurs de transformation de données. Cette visibilité fait souvent défaut dans les alternatives SaaS boîte noire.

La scalabilité est un autre facteur critique. Comme l'état d'exécution est sérialisable, n8n peut scaler horizontalement en utilisant le mode file d'attente. Le serveur d'application principal reste léger, ne gérant que les requêtes API et l'ingestion des webhooks, tandis que des processus workers dédiés prennent en charge le travail lourd de l'exécution des workflows. Cette séparation permet aux équipes DevOps d'ajuster les ressources en fonction de la profondeur de la file d'attente plutôt que de deviner combien de CPU le processus principal nécessite. Cela empêche également le trafic élevé des webhooks de bloquer l'interface utilisateur.

Ce que vous pouvez faire

  • Auditer l'ordre d'exécution : Passez en revue vos workflows complexes et basculez entre les ordres d'exécution v0 et v1 pour voir si les performances ou la logique s'améliorent pour des cas d'usage spécifiques.
  • Gérer les branches vides : Définissez explicitement alwaysOutputData: true sur les nœuds où les actions en aval doivent se produire même si l'étape précédente ne produit aucun résultat.
  • Surveiller la profondeur de la file d'attente : Si vous utilisez le mode file d'attente, configurez des alertes sur la longueur de la file d'attente Redis pour scaler automatiquement les pods workers à l'aide de Kubernetes HPA ou KEDA.
  • Inspecter les données d'exécution : Utilisez la visionneuse d'exécution intégrée pour examiner la lignée pairedItem lors du débogage des erreurs de mapping de données dans les jonctions complexes.
  • Vérifier la conformité de licence : Assurez-vous que votre utilisation est alignée avec la Sustainable Use License, particulièrement si vous construisez des automatisations personnalisées pour des clients externes.
  • Tester la résilience des webhooks : Simulez des événements webhook à haut débit pour vérifier que vos processeurs de webhooks dédiés ne bloquent pas le thread UI principal.

Autres actualités

Toutes les actualités