Un cron job bloqué ne veut plus dire attendre le timeout : hermes cron cancel arrête l'exécution en cours


Trois heures du matin, votre téléphone vibre : un collecteur horaire est coincé sur une API amont qui a cessé de répondre. Il tourne toujours, attend toujours un timeout, écrit toujours la même ligne d’erreur dans le log. Vous ouvrez un terminal et lancez hermes cron pause — pour découvrir que rien ne s’est arrêté. Cela signifie seulement qu’il n’y aura pas de nouveau déclenchement à l’heure suivante. L’exécution en cours est toujours là, à brûler des tokens ligne après ligne.

C’est la différence sur laquelle tout le monde trébuche : pause régit la planification, pas l’exécution déjà en cours. Jusqu’ici, vos options étaient « attendre le timeout » ou « supprimer le job et le recréer ». La v0.22.6 ajoute la troisième : hermes cron cancel.

Ce que cancel fait vraiment

hermes cron cancel <job_id_or_name>
hermes cron cancel <job_id_or_name> --reason "wedged on the staging API"

La référence du job accepte un ID hexadécimal ou un nom (insensible à la casse). Les noms ambigus sont refusés avec la liste des ID candidats, de sorte que vous ne pouvez pas arrêter le mauvais job par accident.

La planification n’est pas touchée : le job reste activé et son prochain déclenchement a lieu comme prévu. Seule l’exécution courante change.

La façon dont elle s’arrête dépend du type de job :

  • Script jobs (--script, y compris le mode sans agent) : tout l’arbre de processus est terminé — SIGTERM envoyé au groupe de processus du script, puis SIGKILL après un délai de grâce, petits-enfants passés en arrière-plan compris. C’est le même démontage qu’un timeout de script, donc rien ne continue de tourner derrière le job.
  • Agent jobs : le tour en cours est interrompu à son prochain checkpoint.

Point crucial, il s’agit d’une demande, pas d’un kill : l’annulation est enregistrée contre l’ID d’exécution de cette exécution et prise en compte au prochain point de contrôle d’annulation (bien moins d’une seconde pour un script). L’exécution inscrit alors une ligne cancelled portant votre motif, si bien que hermes cron runs <job_id> affiche ensuite « cancelled — wedged on the staging API » au lieu de laisser un timeout inexpliqué ou un échec nu.

Deux détails qui comptent en pratique : une exécution annulée n’envoie aucune notification d’échec et ne compte pas dans la série d’échecs du job. Ce que vous avez arrêté volontairement ne devrait pas continuer à dégrader le signal de santé du job.

Quatre portes, un seul arrêt

  • hermes cron cancel <job> (CLI)
  • POST /api/cron/jobs/{id}/cancel (HTTP)
  • l’outil cronjob_manage de l’agent lui-même, avec action: "cancel"
  • l’application desktop : un job dont une exécution est en cours affiche Stop dans son en-tête ; il indique Stopping… jusqu’à ce que la ligne cancelled soit écrite, puis un toast Run cancelled le confirme

Supprimer un job arrête aussi son exécution en cours. hermes cron remove (ainsi que la suppression depuis le dashboard ou le desktop, et l’action remove de l’agent) annule une exécution en cours, inscrite cancelled avec le motif « job removed while running ». Auparavant, elle continuait et produisait un résultat que personne n’aurait gardé.

Annuler un job qui n’a rien en cours est une opération sans effet, sûre, qui le dit ; et une annulation qui arrive après la fin de l’exécution ne change rien.

Ce qu’il ne peut pas faire : revenir en arrière

Soyons clairs. Cancel ne peut pas défaire les effets de bord déjà produits par l’exécution — les fichiers écrits, les API appelées, les messages envoyés restent. Il arrête l’exécution ; il ne restaure pas le monde.

Il existe aussi une limite de timing : l’interruption se produit au checkpoint suivant. Si un appel d’outil est bloqué dans un syscall non interruptible, le moment de l’arrêt dépend du retour de cet appel.

L’autre moitié : que se passe-t-il pour un déclenchement manqué

Une fois une exécution arrêtée, la question suivante arrive aussitôt — si le job est arrivé à échéance pendant que vous faisiez de la maintenance, rattrape-t-il son retard au retour du gateway ?

Par défaut, oui, une fois : après le retour du gateway, un créneau manqué pendant la fenêtre de redémarrage se déclenche exactement une fois, et une longue panne se résume à une seule exécution au lieu d’une par créneau manqué. Vous pouvez désactiver ce comportement global par défaut :

hermes config set cron.catch_up_missed false

Depuis la v0.22.6, un job individuel peut remplacer le réglage global :

hermes cron create "0 7 * * 1-5" "Morning brief" --catch-up off   # un brief périmé ne sert à rien : on saute
hermes cron edit <job> --catch-up on                              # toujours exécuter une fois au retour

Dans l’éditeur de jobs du desktop, cela s’appelle Missed runs avec trois choix : utiliser la valeur par défaut, exécuter une fois au retour, les ignorer. La fenêtre de grâce vaut par défaut la moitié de la période du job, bornée entre deux minutes et deux heures ; --grace-minutes la remplace job par job (0 signifie ne jamais rattraper, jusqu’à 1440). Un rattrapage est visible, pas silencieux : hermes cron list le marque ⚠ late ou ⚠ catch-up after missed fire.

pause, cancel et la pause globale

Trois commandes sont souvent confondues ; la séparation est nette :

  • hermes cron pause <job> — garde le job, arrête sa planification ; ne touche pas à une exécution en cours.
  • hermes cron cancel <job> — arrête uniquement l’exécution courante ; la planification continue.
  • hermes pause (sans cron) — arrêt d’urgence global : aucun déclenchement planifié ne démarre nulle part, mais les exécutions déjà en cours ne sont jamais tuées, et le travail arrivé à échéance rattrape son retard après hermes resume.

Pour construire une planification fiable de bout en bout, consultez notre guide complet de l’automatisation cron. Si votre problème récurrent est « le job aurait dû se déclencher et ne l’a pas fait », l’article sur le cron doctor déroule le chemin de diagnostic, et l’article sur grace-minutes approfondit la fenêtre de rattrapage évoquée ici.

Tout ce qui précède est livré dans le tag v0.22.6 — une mise à jour suffit pour en profiter.