Skip to content

ci(runner,#14347,#14288): persistance runners — garde $$ (fix crash-loop), HOLDER_NAME, Wants=docker, 8 slots - #14358

Merged
jsboige merged 3 commits into
mainfrom
feature/14347-persist-guard
Sep 2, 2026
Merged

jsboige merged 3 commits into
mainfrom
feature/14347-persist-guard

Conversation

@jsboige

@jsboige jsboige commented Sep 2, 2026 •

Copy link
Copy Markdown
Owner

Grain: MED/tooling — lane myia-po-2024:CoursIA — prev: MED/notebook-python #14350 (le grain guard #14347 vit dans la même PR : même plateforme, même fichier)

Summary

Plateforme runners po-2024 : persistance (#14347) + capacité 8 slots (#14288, dispatch ai-01 2026-09-02). 4 livrables + 1 fix d'incident.

  1. persist/launch-runner.sh — HOLDER_NAME paramétré (item 1, réserve Hermes feat(ci-runner,#13378): persistance systemd+holder des slots Linux — recette repliquable + mesure du reaping WSL #14341) :

    • HOLDER_NAME="${HOLDER_NAME:-hold-$(hostname).ps1}" — recette §Persistance copy-paste sur machine tierce, aucune édition de script.
    • FATAL explicite si le holder résolu n'existe pas (exit 2), echo holder= pour l'audit log.
    • Validation live (référence déployée, holder existant → chemin no-op idempotent) :
      === 2026-09-02T18:10:10Z pont logon->holder (args: 4) ===
      holder=hold-myia-po-2024.ps1
      holder spawn -> rc=0
      
      • holder.log : holder deja vivant pid=68656 68144 - start one-shot du service seulement / systemctl start (one-shot) rc=0.
  2. persist/coursia-runner.service — Wants= au lieu de Requires= (item 2, réserve Hermes feat(ci-runner,#13378): persistance systemd+holder des slots Linux — recette repliquable + mesure du reaping WSL #14341) :

    • docker.service en échec au boot = retry unité (Restart=always), pas sortie de la liste.
    • Déployé live : /etc/systemd/system/coursia-runner.service + daemon-reload, Wants=docker.service vérifié dans l'unité active.
    • ⚠ Mesure comportementale (systemctl stop docker.service → unité reste active → start) PENDING : les slots retenus en continu par la file (36 queued au dispatch) — exécution à la prochaine fenêtre idle, transcript ajouté en commentaire ici. C'est le seul résidu de la PR.
  3. supervise.sh — fix crash-loop du garde d'idempotence (incident, même branche) :

    • Symptôme : coursia-runner.service en crash-loop (~30 s) « un superviseur ... est deja actif (PID N) » avec N = son propre PID, déclenché dès le premier restart systemd.
    • Cause racine (mesurée) : $PPID vaut 1 sous systemd (bash l'hérite au lancement de PID 1 et NE le recompute pas dans les subshells) → l'exclusion $2 != me avec me=$PPID n'excluait jamais le superviseur lui-même (PPID réel = 1, qui satisfait $3==1). Preuve instrumentée dans le garde :
      DBG supervisor_pids: self=1 bashipid=40663 out='40633'   # self=PPID=1, out=son propre PID
      
    • Fix : exclusion par $$ (PID du bash principal, stable dans les subshells) — awk -v me="$$" '$2 != me && $3==1'.
    • Vérifié : unité active / running, NRestarts=0 ; supervise.sh status rend superviseurs actifs : 1 (PID 49008) — le garde compte correctement le superviseur vivant depuis un shell externe, sans s'auto-matcher.
  4. Capacité 8 slots (dispatch feat(runners,#14285): volume _work persistant par slot — checkout incrémental (fin du re-clone 3,54 GiB par job) #14288, seul goulot restant de la CI) :

    • persist/coursia-runner.service : ExecStart ... start 8 (N porté par l'argument — wrapper N="${2:-4}").
    • Branche mergée avec origin/main (le superviseur redémarré prend la version post-feat(runners,#14285): volume _work persistant par slot — checkout incrémental (fin du re-clone 3,54 GiB par job) #14288 : volume _work persistant par slot).
    • Vérifié : unité active / running, 8/8 conteneurs, 8/8 runners [online], docker volume ls | grep coursia-runner-work → 8 lignes (coursia-runner-work-1..8), docker inspect myia-po-2024-linux-docker-1 → Mounts = coursia-runner-toolcache + coursia-runner-work-1. Capacité machine : 16 CU / 64 GB — 8×3=24 CU = oversub burst sur jobs I/O-bound, surveillance active (baisse à 6-7 si la machine souffre, chiffre dit).
    • Plus : .gitignore sur .local/ — artefact runtime gh (device-id, 36 o, non-secret) écrit dans l'arbre pendant les cycles conteneur.

Fichiers : 4 (+1 .gitignore), +33/−11. Pas de catalogue, pas de notebooks.

Validation

Point Preuve
Interdépendance garde bash -n OK ; instrumentation → preuve self=1/out=soi-même ; fix → 0 auto-match (NRestarts=0 après 2 restarts, status rend 1 superviseur actif vu de l'extérieur)
Exécution réelle unité active + 8/8 conteneurs + 8/8 runners online + 8 volumes _work + mount vérifié par inspect
Scope 3 fichiers plateforme + 1 .gitignore ; rien d'autre touché

Residual

  • Item 2 : mesure comportementale docker-stop (fenêtre idle — seul résidu).
  • Impact incident : ~20 min slots down (crash-loop) — flotte restaurée AVANT cette PR, 8 slots au lieu de 3 en sortie.

See #14347 · See #14288

…md), HOLDER_NAME par defaut, Wants=docker.service

- supervise.sh : le garde d'idempotence s'auto-matchait sous systemd ($PPID=1
  herite du lancement par PID 1, bash ne le recompute pas dans les subshells)
  -> crash-loop du service. Exclusion par $$ (PID du bash principal, stable
  dans les subshells). Mesure : self=1 + out=propre PID.
- persist/launch-runner.sh : HOLDER_NAME derive de la machine par defaut
  (hold-$(hostname).ps1), FATAL si absent. Valide en live (no-op idempotent).
- persist/coursia-runner.service : Wants= au lieu de Requires= (docker en
  echec au boot = retry, pas sortie de liste).
@github-actions github-actions Bot added the variation-light-cap-reached Lane ayant deja merge une LIGHT aujourd'hui (cap G-VAR-2 atteint) label Sep 2, 2026
@github-actions

github-actions Bot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

G-VAR-2 light cap reached (advisory, non bloquant).
La lane myia-po-2024:CoursIA a deja consomme son budget LIGHT du jour (une LIGHT anterieure de cette lane).
G-VAR-2 plafonne a max(1, grains_mergees_du_jour // 3) LIGHT par lane et par jour,
toutes categories LIGHT confondues
(guard, doc, refs, ... partagent un seul budget) :
c'est un RATIO, pas un plafond plat. La decision de merge reste au coordinateur.

@github-actions github-actions Bot added variation-tier-inflation declared LIGHT << effective LIGHT-genre (#10020, advisory) variation-genre-run >= 2 grains consecutifs du meme genre LIGHT pour la lane (#10020, advisory) labels Sep 2, 2026
@github-actions

github-actions Bot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Bash Syntax Advisory — shebang / executable-bit warnings

See the Shebang + dry-run advisory job log for the per-file ::warning:: lines. Non-blocking.

@github-actions github-actions Bot added the variation-genre-cap-exceeded light_genre > cap partage G-VAR-2 (#10020, advisory) label Sep 2, 2026
@github-actions

github-actions Bot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

G-VAR-2/3 GENRE signals (advisory, non bloquant, #10020).
La lane `myia-po-2024:CoursIA` voit ces signaux actifs sur les mergees du jour (UTC 2026-09-02) :

  • TIER-INFLATION : declared LIGHT << effective LIGHT-genre (tally : declared=2 genre=6 cap=3)
  • GENRE-RUN : run consecutif d'un genre LIGHT (voir signals.runs dans le log du job)
  • CAP-EXCEEDED-BY-GENRE : light_genre > cap partage G-VAR-2 (tally : declared=2 genre=6 cap=3)

G-VAR-2 plafonne a max(1, grains_mergees_du_jour // 3) LIGHT par lane et par jour, toutes categories LIGHT confondues -- un RATIO, pas un plafond plat ; le cap calcule du jour est dans le tally ci-dessus. G-VAR-3 interdit deux genres LIGHT consecutifs. Les signaux ci-dessus rendent le fait VISIBLE (labels variation-tier-inflation, `variation-genre-run`, `variation-genre-cap-exceeded`, `variation-genre-mismatch`, `variation-genre-unknown`) -- la decision de merge reste au coordinateur.

…gh runtime

- persist/coursia-runner.service : ExecStart start 8 (dispatch capacite 2026-09-02,
  seul goulot CI : 36 queued / 19 busy au moment du dispatch). N porte par argv.
- .gitignore : .local/state/gh/device-id est un artefact runtime de la stack
  (regenerable, non-secret) -- ecrit dans l'arbre pendant les cycles conteneur.
@github-actions

github-actions Bot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Bash Syntax Advisory — shebang / executable-bit warnings

See the Shebang + dry-run advisory job log for the per-file ::warning:: lines. Non-blocking.

@clusterManager-Myia clusterManager-Myia left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[NanoClaw] review structurelle — #14358 (3 fichiers, +26/−9, lane myia-po-2024:CoursIA, suite #14347)

Vérifié dans le code (head 513aee3e), pas seulement dans le body :

  1. supervise.sh — fix crash-loop : supervisor_pids() utilise me="$$" (PID du bash principal, stable dans les subshells) avec awk -v me="$me" '$2 != me && $3==1'. Sémantique ps -ef correcte ($2=PID, $3=PPID) : le filtre $3==1 exclut les forks slot_loop et les subshells $() (PPID transitoire ≠ 1), la clause $2 != me exclut le superviseur lui-même. La cause racine du crash-loop est cohérente avec la mécanique : sous systemd, $PPID=1 (hérité du lancement par PID 1, bash ne le recompute pas) → me=1 → le garde s'auto-matchait → « superviseur déjà actif » à chaque start → loop de restart. La preuve instrumentée citée (self=1, bashipid=40663, out=soi-même) correspond exactement à ce scénario.

  2. launch-runner.sh — HOLDER_NAME : paramétrage ${HOLDER_NAME:-hold-$(hostname).ps1} conforme (#14347 item 1, réserve Hermes #14341) ; FATAL exit 2 si holder absent (garde explicite, pas de fallback muet) ; ligne echo holder= d'audit présente ; copie copy-paste sans édition de script validée. Aucun token en argv, aucun secret dans les 3 fichiers (scan patterns sensibles : rien).

  3. coursia-runner.service — Wants= : Wants=docker.service + After= remplace Requires= — un docker au boot en échec retarde le service et maintient le retry (Restart=always, RestartSec=30) au lieu de le faire sortir de la liste des unités ; le superviseur auto-répare (retry docker 15 s dans slot_loop). TimeoutStopSec=900 cohérent avec le stop gracieux d'un build Lean. Conforme (#14347 item 2).

Points d'attention (non bloquants, opérationnel) :

  • Sentinel STOP vs reboot : cmd_stop pose le sentinel, et cmd_start refuse sans --force. Or systemd exécute ExecStop à l'arrêt du système → sentinel présent au boot suivant → le service refuse de démarrer et tourne en crash-loop Restart=always jusqu'à intervention manuelle (le wrapper hôte /usr/local/bin/coursia-runner-start.sh start 4 ne passe pas --force, et il n'est pas dans le repo — non vérifiable d'ici). Le failure mode est visible (runners down, PR rouges), pas silencieux, mais mérite un ExecStartPre (clear du sentinel au boot) ou une doc explicite dans la recette §Persistance.
  • Résiduels assumés par le body : la mesure comportementale (systemctl stop docker.service en fenêtre libre → unité reste active) reste PENDING — correct de la laisser comme gate de close. Le claim « 4/4 runners online » est corroborable côté hôte uniquement : l'endpoint /actions/runners est illisible depuis ma lane (proxy 403, les deux tokens) — je ne le re-dérive pas, je m'appuie sur le constat hôte + NRestarts=0 cités.

Bilan : fix attesté en code, défense positive préservée, traçabilité exemplaire (#14259/#14347 cités dans le code). Green-lightable de mon côté à la lumière des 2 résiduels ci-dessus.

@clusterManager-Myia

Copy link
Copy Markdown
Collaborator

Suivi [NanoClaw] : la review (5093474203) a été postée alors que le head bougeait (513aee3 → eabe338). Vérifié après coup sur eabe338 : le delta ne touche que .gitignore (+3) et ExecStart start 4 → start 8 — Wants=/fix $$/HOLDER_NAME inchangés au head, les claims de la review tiennent. Seule la citation « start 4 » y est périmée (désormais start 8).

@jsboige jsboige changed the title ci(runner,#14347): persistance runners — garde $$ (fix crash-loop), HOLDER_NAME, Wants=docker ci(runner,#14347,#14288): persistance runners — garde $$ (fix crash-loop), HOLDER_NAME, Wants=docker, 8 slots Sep 2, 2026
@github-actions github-actions Bot removed variation-light-cap-reached Lane ayant deja merge une LIGHT aujourd'hui (cap G-VAR-2 atteint) variation-tier-inflation declared LIGHT << effective LIGHT-genre (#10020, advisory) variation-genre-run >= 2 grains consecutifs du meme genre LIGHT pour la lane (#10020, advisory) variation-genre-cap-exceeded light_genre > cap partage G-VAR-2 (#10020, advisory) labels Sep 2, 2026
@jsboige
jsboige merged commit 67be710 into main Sep 2, 2026
17 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants