Schedule Trigger cesse silencieusement de se déclencher après plusieurs jours (Kubernetes + mode file d'attente)

Décrivez le problème/l’erreur/la question

J’ai un workflow qui utilise un Schedule Trigger et s’exécute plusieurs fois par jour. Après plusieurs jours de fonctionnement correct, le workflow cesse simplement de se déclencher. Il n’y a pas d’exécutions échouées et aucun message d’erreur dans les journaux. Le workflow reste actif, mais les exécutions planifiées ne démarrent plus.

Pendant le dépannage, j’ai remarqué que si je réduis le nombre d’exécutions planifiées par jour, le workflow continue de fonctionner pendant quelques jours supplémentaires avant de s’arrêter à nouveau. Cela me fait penser que le problème pourrait être lié au planificateur, au mode de file d’attente ou à une limitation de ressources qui s’accumule au fil du temps.

Quelqu’un d’autre a-t-il observé des workflows avec Schedule Trigger qui cessent silencieusement de fonctionner dans un déploiement Kubernetes fonctionnant en mode de file d’attente ?

Quel est le message d’erreur (le cas échéant) ?

Il n’y a pas de message d’erreur. Le Schedule Trigger cesse simplement de se déclencher.

Partagez votre workflow

Le problème n’est pas spécifique à un workflow particulier. Il se produit avec les workflows qui commencent par un Schedule Trigger et s’exécutent correctement jusqu’à ce que le déclencheur cesse de se déclencher.

Schedule Trigger → (various nodes)

Partagez la sortie renvoyée par le dernier nœud

Il n’y a pas de sortie quand le problème se produit, car le workflow ne démarre jamais. Quand il s’exécute, le workflow se termine avec succès.

Informations sur votre configuration n8n

  • Version de n8n : Chart Helm n8n version 1.16.39 (veuillez me faire savoir si la version réelle de l’application est plus pertinente)
  • Base de données : PostgreSQL (externe)
  • Paramètre n8n EXECUTIONS_PROCESS : Mode de file d’attente
  • Exécution de n8n via : Kubernetes avec le Community Helm Chart
  • Système d’exploitation : Cluster Kubernetes (nœuds Linux)

Configuration pertinente

redis:
  enabled: true

worker:
  mode: queue

webhook:
  mode: queue
  url: https://<my-domain>

db:
  type: postgresdb

externalPostgresql:
  host: <external-postgres>

Observations supplémentaires :

  • Les workflows s’arrêtent sans aucune erreur ou exécution échouée.
  • La réduction du nombre d’exécutions planifiées par jour retarde le problème mais ne l’élimine pas.
  • Le redémarrage du déploiement restaure le Schedule Trigger temporairement.
  • Redis est activé et PostgreSQL est externe.

Could cela être lié au mode de file d’attente, à l’élection du leader ou au processus du planificateur dans un déploiement Kubernetes multi-pods ?

Bonjour @thun

Il existe un problème documenté (référencé dans les issues GitHub #30220 et #30256) où le Schedule Trigger s’arrête silencieusement de se déclencher après un certain nombre d’exécutions (souvent autour de 700+ ticks pour les workflows haute fréquence).

  • Le Symptôme : Le workflow reste « Actif » dans l’interface utilisateur, les exécutions manuelles fonctionnent parfaitement, mais le déclenchement automatisé s’arrête simplement.
  • La Cause : Une erreur de récursion/débordement de pile se produit dans observable-object.ts pendant la logique d’activation du déclenchement.
  • Détail Crucial : Cette erreur n’apparaît pas dans les journaux d’exécution du workflow car l’arrêt se produit dans la boucle interne du planificateur avant qu’une exécution ne soit créée. Elle n’apparaît que dans les journaux stdout/stderr du conteneur n8n Main​.

Un autre problème connu (#28423) impliquait le Schedule Trigger utilisant une vérification d’égalité stricte (===) pour déterminer s’il était temps de se déclencher.

  • Le Symptôme : Si le processus main redémarre ou qu’un changement de leadership se produit exactement quand un déclenchement était censé se produire, l’horodatage lastExecution devient « obsolète ».
  • La Cause : En raison de la vérification stricte, le planificateur attendrait le exact prochain tick. Si l’état était corrompu ou manqué, le workflow pouvait être silencieusement bloqué pendant des jours ou des semaines.
  • La Solution : Cela a été mis à jour vers une vérification « temps écoulé » (>=) pour permettre au planificateur de « s’auto-réparer » et de se déclencher immédiatement s’il détecte une fenêtre manquée.

En mode file d’attente, le Schedule Trigger est possédé exclusivement par le processus Main (Leader).

  • Déclenchements Fantômes : Si votre chart Helm est configuré pour permettre plus d’un réplica du processus main (ou si un ancien pod persiste lors d’une mise à jour progressive), vous pouvez rencontrer des « déclenchements fantômes » ou des conflits d’enregistrement dans Redis qui finissent par bloquer le planificateur.
  • Rôle Worker : Si les pods worker exécutent accidentellement la commande n8n start au lieu de n8n worker, ils tenteront d’agir comme des processus main, concourant pour le planificateur et causant un comportement erratique.

L’une de ces situations s’applique-t-elle ?

Pouvez-vous également faire ce qui suit ?

  1. Vérifier les Journaux du Conteneur (Pas les Journaux de l’Interface Utilisateur) : Inspectez les journaux de votre pod n8n main en utilisant kubectl logs <pod-name>. Cherchez spécifiquement la chaîne : RangeError: Maximum call stack size exceeded Si vous la voyez, vous avez confirmé le bug observable-object.

  2. Vérifier les Réplicas du Pod Main : Assurez-vous que votre déploiement main est strictement configuré avec replicas: 1. En mode file d’attente, avoir plusieurs mains sans une solide configuration d’élection de leader conduit souvent à une instabilité du planificateur.

  3. Vérifier les Commandes Worker : Vérifiez la section args ou command de vos pods worker. Ils doivent exécuter : n8n worker (et NON n8n start).

  4. Mettre à Jour la Version n8n : Les correctifs pour le RangeError et le planificateur « Auto-réparable » ont été déployés dans les mises à jour récentes (spécifiquement traités dans les versions autour de 2.20.9 et ultérieures dans la branche @next, et rétroportés vers la version stable). Comme vous êtes sur 1.16.39, vous exécutez probablement une version susceptible à ces bugs. La mise à jour vers la dernière version stable est le correctif principal recommandé.

Merci pour votre réponse !

J’ai consulté les logs principaux du pod et j’ai vu beaucoup de « Maximum call stack size exceeded ». J’ai réalisé que je suis en retard de plusieurs versions, je vais essayer de mettre à jour vers des versions plus récentes et voir si cela aide.

Merci !

Bien que tu aies confirmé l’erreur « Maximum call stack size exceeded » — c’est le bug de #30220. Une chose à vérifier deux fois dans tes valeurs Helm avant et après la mise à niveau : replicaCount pour le déploiement principal doit être exactement 1. Exécuter plus d’un replica principal provoque des conflits d’enregistrement de planificateur dans Redis qui peuvent reproduire le même symptôme d’arrêt silencieux même après la mise à niveau. Les workers peuvent être mis à l’échelle librement, mais le principal reste à 1.

Content du grep de log confirmé — un flot de « Maximum call stack size exceeded » dans le pod principal est le bug d’accumulation de ticks #30220, et cela correspond exactement à vos propres indices : moins d’exécutions/jour = plus de temps pour atteindre la limite, et un redémarrage réinitialise le compteur à zéro. La mise à niveau est la bonne solution, et la vérification replicaCount: 1 de nguyenthieutoan vaut le coup d’être faite en même temps. Une petite chose pendant que vous choisissez vers quelle version mettre à niveau : « 1.16.39 » est la version du chart Helm, pas la version de l’application n8n — elles sont numérotées indépendamment, alors vérifiez à quoi votre image.tag se résout réellement et visez la dernière version stable de l’application, pas un numéro de chart.

Le point que je signaliserais parce qu’il perdure au-delà de la mise à niveau : cette classe d’échec est invisible pour n8n par conception. Aucune exécution n’est jamais créée, le Trigger d’erreur ne se déclenche donc jamais — il n’y a rien auquel attacher une erreur. C’est pourquoi cela s’est exécuté silencieusement pendant des jours. Même complètement corrigé, un planificateur qui « s’arrête simplement, Actif dans l’interface utilisateur, pas d’erreur » est quelque chose que vous apprendrez toujours tard, lorsqu’un humain remarque des données manquantes. La solution durable est un mécanisme de surveillance indépendant : avoir le workflow planifié pousser une impulsion cardiaque à chaque exécution réussie (une clé Redis avec un TTL, une ligne horodatée, ou un ping vers un moniteur de style healthchecks), et avoir quelque chose indépendant de n8n qui alerte lorsque l’impulsion cardiaque devient obsolète. Cela transforme « s’arrêté silencieusement pendant des jours » en « alerté en minutes », quelle que soit la cause première qui se manifeste la prochaine fois.

Si la mise à niveau ne le guérit pas complètement — vous avez confirmé le RangeError, mais s’il reste silencieux sur une version corrigée avec replicas: 1 et que les workers exécutent véritablement n8n worker — c’est la branche spécifique au déploiement, et je serais heureux de vous aider à l’explorer ici.

Un suivi avant la fermeture de ce fil, car il y a un détail spécifique à Kubernetes que les correctifs ci-dessus ne couvrent pas, et cela explique pourquoi cet échec est si discret sur votre cluster en particulier.

Votre sonde de vivacité ne peut pas détecter cet échec. Le point de terminaison de santé n8n répond à partir du serveur web, et le serveur web fonctionne bien tandis que la boucle du planificateur est bloquée, donc Kubernetes continue de signaler un pod sain et ne le redémarre jamais. C’est pourquoi la panne dure des jours : le seul mécanisme intégré pour redémarrer un pod cassé lit un signal qui n’exerce pas le composant cassé. La santé du processus et la santé du planificateur sont des choses différentes, et chaque sonde que vous avez actuellement mesure la première.

Since a restart reliably restores the trigger, you can close the gap while you plan the upgrade with a canary: one trivial scheduled workflow that does nothing but write a timestamp somewhere cheap every few minutes, a Redis key, a Postgres row, or a ping to a free monitor like Healthchecks.io. When the timestamp goes stale, the scheduler is wedged regardless of what the health endpoint says. If you want it fully automatic, a small CronJob that checks the canary’s freshness and does a rollout restart of the main deployment when it goes stale turns a multi day outage into a few minutes of blip, which is an acceptable stopgap for a bug that only the upgrade actually fixes.

Puisqu’un redémarrage restaure de manière fiable le déclenchement, vous pouvez combler l’écart pendant que vous planifiez la mise à niveau avec un canari : un workflow planifié trivial qui ne fait que d’écrire un horodatage quelque part à faible coût toutes les quelques minutes, une clé Redis, une ligne Postgres, ou un ping vers un moniteur gratuit comme Healthchecks.io. Quand l’horodatage devient obsolète, le planificateur est bloqué peu importe ce que le point de terminaison de santé dit. Si vous le voulez entièrement automatique, une petite CronJob qui vérifie la fraîcheur du canari et effectue un redémarrage de déploiement lorsqu’il devient obsolète transforme une panne de plusieurs jours en quelques minutes de clignotement, ce qui est un palliatif acceptable pour un bug que seule la mise à niveau résout réellement.

Deux vérifications à faire à la prochaine impasse, avant de redémarrer, afin que la décision de mise à niveau soit fondée. Récupérez les logs du pod principal et recherchez RangeError: Maximum call stack size exceeded, comme kjooleng l’a dit. Et faites les calculs de ticks : votre propre observation que moins d’exécutions par jour retarde l’échec est exactement ce qu’un bug de seuil de ticks prédit, donc si les exécutions par jour multipliées par les jours avant l’échec donne à peu près une constante à travers vos incidents, vous avez la confirmation sans même attendre la ligne de log.