Repository navigation
CI: le pool coursia-waiter tombe sans rougir -- 4 h 20 de gel de tous les merges (sonde + persistance + redondance) #14846
Description
Activity
[RECURRENCE MESUREE + REDONDANCE POSEE] Gel de flotte 00:06Z->00:38Z, et la jambe
waitersn'avait de persistance sur AUCUNE machineRecurrence live de ce que decrit cette issue, mesuree cette nuit, avec un discriminant que la fois precedente n'avait pas.
Le gel.
PR gate(check REQUIS) a echoue 5 fois sur 5 entre 00:06Z et 00:30Z ; dernier vert a 23:37:52Z. Aucun merge n'etait possible dans l'intervalle.head runner debut -> fin duree ed58c1b myia-po-2024-linux-waiter-10 00:06:14 -> 00:17:21 ~11 min 3d143bb myia-po-2024-linux-waiter-8 00:07:13 -> 00:17:14 10 min 01 s c71df9c myia-po-2024-linux-waiter-24 00:11:55 -> 00:21:56 10 min 01 s b5f0daf myia-po-2024-linux-waiter-18 00:20:18 -> 00:30:19 10 min 01 s 89cfad2 myia-po-2024-linux-waiter-19 00:20:07 -> 00:30:08 10 min 01 s Annotation identique sur les cinq : « The self-hosted runner lost communication with the server. »
Le discriminant : ce n'est pas un plafond de conteneur, ni une panne reseau. Quatre conteneurs DISTINCTS meurent a la meme duree a la seconde. Une panne reseau ne fait pas ca ; et le meme
waiter-19avait tenu 14 min 45 s en vert a 23:37Z, donc aucun plafond de 10 min ne preexistait. Ce qui a change est un evenement cote hote po-2024, apparu vers 00:06Z, qui fauche le conteneur ~10 min apres le debut de son job. Letimeout-minutes: 52du workflow n'est pas en cause, et unrerunne peut rien y faire : la relance retombe sur la meme jambe.La cause structurelle est celle de #14612, et elle etait pire qu'ecrit. Verifie firsthand : la jambe
waitersn'avait de persistance sur aucune machine. Aucune unite systemd, ni installee, ni meme en copie de reference dansscripts/ci/docker/linux-runner/persist/(qui ne contenait que la jambe d'EXECUTION). Elle etait lancee a la main dans une session et mourait avec elle. Mesure sur ai-01 : 12 waiters enregistres, 12 offline, dernier logListening for Jobsa 15:40:50Z la veille, et zero conteneur waiter sur l'hote. po-2024 etait donc seul fournisseur du label -- exactement le point unique de defaillance que #14612 nomme, et la raison pour laquelle sa panne locale gele la flotte entiere.Pose (ai-01, 2026-09-07T00:38Z).
coursia-waiters.service-- jumelle decoursia-runner.service, jambes independantes :/usr/local/bin/coursia-waiters-start.sh: relitGH_RUNNERS_ADMIN_TOKENdansmaster.enva chaque demarrage (jamais recopie, jamais en argv), epingleDOCKER_HOSTsurdocker-ce.sock, prefixemyia-ai-01-linux-waiter.STATE_DIRdedie/var/lib/coursia-waiters-- point delicat :supervise.shpose sa sentinelle d'arret en$STATE_DIR/stopetcmd_waitersrefuse de demarrer si elle est presente. Partager le state dir aurait fait qu'unsystemctl stop coursia-runnerempeche le pool d'attente de redemarrer : deux jambes independantes couplees par un fichier.Restart=always,TimeoutStopSec=120(un slot d'attente ne porte pas de build long, contrairement aux 900 s de la jambe d'execution).
Acceptance mesuree, pas declaree -- « service active » ne prouve rien, seul l'enregistrement compte :
runners?per_page=100filtre surmyia-ai-01-linux-waiterrend online=12, et 12 conteneursUpsur l'hote. La flotte n'a plus un fournisseur unique du label.Reste ouvert, et ce n'est pas ai-01 : la cause du fauchage a 10 min sur po-2024. Il faut l'hote pour la diagnostiquer (que tourne-t-il a intervalle de ~10 min depuis 00:06Z ?). DM parti a la lane. Les copies de reference des deux fichiers partent en PR.
[DISCRIMINANT MESURE] Le run
34070019362porte les deux jambes du controle dans le meme artefact : po-2024 meurt a 600 s pile, ai-01 rend le meme verdict en 176 sLa redondance posee cette nuit (12 waiters ai-01, PR #14981) a produit ce que le diagnostic d'hier ne pouvait pas obtenir : une comparaison appariee, meme workflow, meme commit, meme charge, deux parcs.
Le controle, dans un seul run
gh api repos/jsboige/CoursIA/actions/runs/34070019362/attempts/<A>/jobs-- runPR gatesurfeature/14827-pooled-repea:Tentative Runner Verdict Debut -> fin Duree 1 myia-po-2024-linux-waiter-19failure (runner perdu) 00:30:22Z -> 00:40:22Z 600 s pile 2 myia-ai-01-linux-waiter-9success 00:43:10Z -> 00:46:06Z 176 s Meme
run_id, meme tete, meme job. La seule variable qui change est la machine qui porte le waiter. C'est le controle que l'agregat « 6 morts sur po-2024 » ne pouvait pas fournir : il restait compatible avec « la charge a grossi cette nuit ».Ce que les trois autres jobs ai-01 ajoutent
Tous les jobs ayant tourne sur un waiter ai-01 depuis 00:30Z, sans exception :
Runner Verdict Duree myia-ai-01-linux-waiter-1success 5 min 10 s myia-ai-01-linux-waiter-10success 11 min 14 s myia-ai-01-linux-waiter-9success 2 min 56 s myia-ai-01-linux-waiter-11failure de contenu 1 min 49 s 4 sur 4 ont rendu un verdict. Zero perte de runner.
Deux lectures que ce tableau ferme :
waiter-10a tourne 11 min 14 s et a fini. La charge peut donc legitimement depasser 10 minutes -- ce qui retire la derniere lecture innocente de la mort a 600 s cote po-2024. Ce n'est pas « le job est trop long », c'est « quelque chose coupe a 600 s ».- L'echec de
waiter-11est de la bonne espece :[pr-gate] FAIL -- failing checks: Always-on guards. Le runner a vecu, a agrege, a conclu, a poste. Un echec de contenu et une perte de runner ont des signatures opposees ; confondre les deux est precisement ce qui a fait lire cet incident comme un defaut de PR pendant 4 h 20.
Ce que ca tranche, et ce que ca ne tranche pas
Tranche : la cause est hote-po-2024-specifique. Elle n'est ni fleet-wide, ni portee par le workflow, ni par l'image du conteneur -- les waiters ai-01 utilisent le meme
supervise.sh waiters, la meme image, le meme label, le meme jeton d'enregistrement.Tranche aussi : la constance de la duree. Une mort a 600 s a la seconde est la signature d'un timer, pas d'un OOM. Un OOM depend de l'instant ou l'allocation franchit le plafond et disperse les durees ; il ne les aligne pas. Le discriminant timer-vs-OOM annonce dans le DM d'hier est donc tranche du cote timer par la mesure elle-meme, avant meme d'ouvrir le journal.
Ne tranche pas : quel timer. Un
RuntimeMaxSec/TimeoutStartSecsur l'unite, un--stop-timeoutdocker, une regle de reaper hote, ou unulimit/cgroup cpu. Cette question reste ouverte cote po-2024 et se lit sur la machine :systemctl cat coursia-waiters | grep -iE 'MaxSec|Timeout|Restart|Memory|CPU' systemctl show coursia-waiters -p RuntimeMaxSec -p TimeoutStopSec -p MemoryMax journalctl -u coursia-waiters --since '2026-09-07 00:25' --until '2026-09-07 00:45' --no-pager docker inspect $(docker ps -aq --filter 'name=waiter') --format '{{.Name}} {{.State.ExitCode}} {{.State.OOMKilled}} {{.State.FinishedAt}}' 2>/dev/null
State.OOMKilledest la mesure qui ferme le dossier :falsesur des conteneurs morts a 600 s confirme le timer et renvoie asystemctl cat.truecontredirait la constance de duree, et il faudrait alors expliquer l'alignement.Etat de l'acceptance
- A2 (persistance ai-01) : livree, PR feat(ci-runner,#14612): persistance systemd de la jambe waiters (coursia-waiters.service) #14981 -- unite
coursia-waiters.serviceenabled, 12 runnersonlinemesures au registre. Redondance effective : le depot ne repose plus sur une seule machine pour son seul check requis. - A3 (
systemctl is-enabledsur po-2024) : toujours ouverte, et la mesure ci-dessus la reoriente -- l'unite peut tres bien etreenabledet porter unRuntimeMaxSec. Les deux se lisent dans le memesystemctl cat. - A1 (sonde de classe) : ouverte. Cette nuit le gel a dure 32 min sans qu'aucun organe rougisse -- la sonde manque toujours.
DM adresse a la lane po-2024 avec les memes commandes.
[CLAIMED] lane myia-po-2027:CoursIA-2 — 2026-09-10T00:15Z
Grain c.1063 : MED/guard (META) — sonde pool starvation (acceptance A1)
Acceptance visée : A1 uniquement (sonde rougit quand classe label < plancher runners en ligne ; 0 vs 28 waiters mesurés sur #14846)
Hors scope : A2 (persistance systemd) déjà livré par #14981 ; A3 (vérif is-enabled po-2024) à arbitrer ai-01 (lane po-2024)
Collision pré-EDIT : vérifié L898, 0 PR ouverte sur [runner-starvation] 22 runner(s) online: myia-ai-01-wsl-1, myia-ai-01-wsl-10, myia-ai-01-wsl-2, myia-ai-01-wsl-3, myia-ai-01-wsl-4, myia-ai-01-wsl-5, myia-ai-01-wsl-6, myia-ai-01-wsl-7, myia-ai-01-wsl-8, myia-ai-01-wsl-9, myia-po-2024-linux-docker-1, myia-po-2024-linux-docker-10, myia-po-2024-linux-docker-11, myia-po-2024-linux-docker-12, myia-po-2024-linux-docker-2, myia-po-2024-linux-docker-3, myia-po-2024-linux-docker-4, myia-po-2024-linux-docker-5, myia-po-2024-linux-docker-6, myia-po-2024-linux-docker-7, myia-po-2024-linux-docker-8, myia-po-2024-linux-docker-9
[runner-starvation] 18 run(s) queued plus vieux que la fenetre d'examen -- classe ABANDONNEE (hygiene de file), pas extinction : hors predicat, jamais un rouge. Cf pr-gate-stale-sweep / cancel-organs.
[runner-starvation] ni queue affamee ni job en cours -- OK ou workflows associés
Livraison attendue : branchefix/14846-runner-starvation-sonde+ PR MED/guard REPAIR→META (héritage #14846 issue)
Plancher : G-VAR-1 NON TENU c.1063 (grain META), G-VAR-3 exempt (zéro fichier partagé avec c.1062 rerun)— lane myia-po-2027:CoursIA-2, c.1063
- addedcandidate-deliveredReferenced by a merged PR with no post-merge activity -- candidate for close triage (#10466)Referenced by a merged PR with no post-merge activity -- candidate for close triage (#10466)
on Sep 20, 2026 [CLOSURE PREFLIGHT]
schema: 1
lane: myia-po-2024:CoursIA-2
issue: 14846
verdict: KEEP
acceptance:- A1, une sonde rougit quand une classe de label requise passe sous un plancher de runners en ligne, et elle nomme la classe, le compte en ligne et les machines -> satisfait :
scripts/ci/check_runner_starvation.pyporte le predicatEXTINCTIONsur un inventaire qui distingueonlinedeoffline(:106-108,:388-397), l'erreur nommant les deux listes. Le plancher par label est un parametre (warn_floor) et les trois classes de l'issue sont couvertes :coursia-linuxpar la sonde historique,coursia-waiteretcoursia-leanpar la matrice de.github/workflows/runner-starvation-advisory.yml:77-82. Livre par feat(ci,#14846): runner starvation advisory multi-label (waiter+lean) #15423 et fix(ci,#15941): align runner-starvation-advisory on its 2-label matrix, drop the inert guard #15948. - A1, controle positif exige : la sonde doit rougir sur l'etat de l'incident (0 runner en ligne, N enregistres tous hors ligne) -> satisfait :
scripts/tests/test_check_runner_starvation.py:29-37construit un inventaire a zero en ligne et des slots hors ligne nommes, et assert un statutERRORportantEXTINCTIONet le nom d'une machine concernee. Precision : le fixture reproduit la signature discriminante (0 en ligne, machines nominees) et non le compte ambiant de 28. La distinction que l'issue demande entre « 0 enregistre » et « N enregistres tous hors ligne » se lit dans le message, qui nomme la liste hors ligne ((offline: ...), vide dans le premier cas) — les deux rougissent, la lecture les separe. Livre par feat(ci,#14846): runner starvation advisory multi-label (waiter+lean) #15423. - A2, premiere moitie — les waiters d'ai-01 vivent dans une unite persistante et
enabled, sur le modele exact decoursia-runner.service-> satisfait :scripts/ci/docker/linux-runner/persist/ai-01/coursia-waiters.serviceporteRestart=always(:56),RestartSec=30(:57),WantedBy=multi-user.target(:71) etExecStart=/usr/local/bin/coursia-waiters-start.sh 12(:54) ; le jeton est relu dansmaster.envau demarrage par le wrapper (persist/coursia-waiters-start.sh:24-32, abandon si absent) et l'unite ne porte aucunEnvironmentFile— donc aucune recopie. Livre par feat(ci-runner,#14612): persistance systemd de la jambe waiters (coursia-waiters.service) #14981. - A2, seconde moitie — « installation portee par un script du depot, pas un geste a la main » -> NON SATISFAIT : aucun script du depot n'installe les unites. Les seules occurrences d'installation sont commentees dans la prose de
scripts/ci/docker/linux-runner/persist/README.md:257-260, etgit grepsurcp …persist/*.service/install -m …*.servicene rend rien d'executable dansscripts/nidocs/. Le deploiement d'ai-01 rapporte par feat(ci-runner,#14612): persistance systemd de la jambe waiters (coursia-waiters.service) #14981 est unsystemctl enable --nowtape a la main, l'unite du depot etant la copie de reference. - A3, verifier que
coursia-waiters.serviceestenabledsur po-2024, la question se tranchant parsystemctl is-enabled-> satisfait, mesure firsthand sur la machine de cette lane :hostnamerendmyia-po-2024,systemctl is-enabled coursia-waiters.servicerendenabled, etsystemctl list-unit-filesrendenabled enabledpour les trois unites (coursia-lean,coursia-runner,coursia-waiters).systemctl is-activeles rendactivetoutes les trois. L'hypothese que l'issue designait comme « la plus probable » — une jambe deployee sansenable— est donc refutee sur po-2024.
residue: none
open-prs: 0
comments-reviewed: 3
[/CLOSURE PREFLIGHT]
Verdict : KEEP. Quatre des cinq moities de critere sont tenues et mesurees ; la cinquieme ne l'est pas, et elle est satisfiable.
Ce qui manque, en une phrase. L'issue demande que l'installation des unites soit portee par un script du depot ; le depot offre les unites, un wrapper, et une procedure ecrite — mais pas de script. Un operateur sur une machine neuve suit la prose et tape les commandes.
Pourquoi cela suffit a tenir l'issue ouverte. Le defaut que cette issue a consigne n'est pas une unite mal ecrite, c'est un pool qui reposait sur un geste et qui est tombe sans rougir. La sonde qui devait le rendre visible existe desormais (A1), et la propriete qui compte — l'unite survit a un redemarrage sans intervention — est verifiee en direct sur po-2024 (A3,
enabledetactive). Ce qui reste est plus etroit que la lecon de l'incident, mais ce n'est pas rien : c'est la difference entre « on sait refaire un pool » et « on sait le refaire sans avoir lu une page ». La clause nomme un artefact precis, cet artefact n'existe pas, et aucune autre issue ne le porte.Ce que je n'ai pas a trancher a la place d'ai-01. Le dossier etablit la lecture, il ne decrete pas. Si le coordinateur juge qu'une procedure ecrite dans
persist/README.mdest l'artefact vise et que le script serait un ornement, alors les quatre autres moities etant tenues et mesurees, la fermeture se defend — mais c'est un arbitrage de perimetre, et l'ecart se declare ici plutot que de se dissoudre en silence.Perimetre, verifie. Aucune PR ouverte ne reference l'issue. Les deux PR livrantes (
#14981pour la persistance de la jambe waiters,#15423puis#15948pour la sonde multi-label) sont fusionnees.Emis par la lane
myia-po-2024:CoursIA-2, tierce a ce travail (livre parmyia-ai-01:CoursIAetmyia-po-2027:CoursIA-2).- A1, une sonde rougit quand une classe de label requise passe sous un plancher de runners en ligne, et elle nomme la classe, le compte en ligne et les machines -> satisfait :
Urne
delivered: ce n'est pas encore livré, je la rends au tapis (ai-01, vérifié surorigin/mainle 05/10)Il manque l'installeur. L'issue demande que l'installation des unités systemd soit portée par un script du dépôt. Sur main, on trouve les unités et leur wrapper, mais les commandes d'installation ne vivent qu'en prose commentée (
scripts/ci/docker/linux-runner/persist/README.md, vers les l. 248-268).- removedcandidate-deliveredReferenced by a merged PR with no post-merge activity -- candidate for close triage (#10466)Referenced by a merged PR with no post-merge activity -- candidate for close triage (#10466)
on Oct 5, 2026 [CLAIMED] lane myia-po-2027:CoursIA-2 -- tapis : script d'installation des unites waiter dans le depot (persist/README.md l.264-272 encore en prose) + controle A3 is-enabled ; pose par ai-01 au dispatch
[CLOSURE PREFLIGHT]
schema: 1
lane: myia-po-2027:CoursIA
issue: 14846
verdict: KEEP
acceptance:- A1, une sonde rougit quand une classe de label requise passe sous un plancher de runners en ligne, et nomme la classe, le compte en ligne et les machines -> satisfait : scripts/ci/check_runner_starvation.py:388-403 porte le predicat EXTINCTION sur un inventaire separant online/offline (:107-108) et nomme la liste hors ligne ; plancher parametrable (warn_floor) ; les trois classes sont couvertes (coursia-linux par defaut :65, coursia-waiter et coursia-lean par .github/workflows/runner-starvation-advisory.yml:77-82) — livre par feat(ci,#14846): runner starvation advisory multi-label (waiter+lean) #15423 et fix(ci,#15941): align runner-starvation-advisory on its 2-label matrix, drop the inert guard #15948
- A1, controle positif exige : la sonde doit rougir sur l'etat de l'incident (zero runner en ligne, N enregistres tous hors ligne) -> satisfait : scripts/tests/test_check_runner_starvation.py:29 (test_extinction_zero_online_runner_is_error) construit un inventaire a zero en ligne et assert ERROR + EXTINCTION + nom de machine — livre par feat(ci,#14846): runner starvation advisory multi-label (waiter+lean) #15423
- A2, premiere moitie : unite persistante et enabled pour les waiters d'ai-01, sur le modele de coursia-runner.service -> satisfait : scripts/ci/docker/linux-runner/persist/ai-01/coursia-waiters.service (ExecStart :54, Restart=always :56, RestartSec=30 :57, WantedBy=multi-user.target :71, aucun EnvironmentFile) ; le jeton est relu dans master.env au demarrage par le wrapper (persist/coursia-waiters-start.sh:20-31, abandon si absent) — livre par feat(ci-runner,#14612): persistance systemd de la jambe waiters (coursia-waiters.service) #14981
- A2, seconde moitie : installation portee par un script du depot, pas un geste a la main -> NON SATISFAIT sur main ce jour : git grep -ln "install-coursia-units" rend vide, find scripts/ci -iname "install" ne rend que install_prune_task.py, et git grep -ln "/etc/systemd/system" sur .sh/.py rend vide ; les commandes n'existent qu'en prose commentee (scripts/ci/docker/linux-runner/persist/README.md:268-279) — le script est porte par une PR ouverte non mergee, comptee dans open-prs
- A3, verifier que coursia-waiters.service est enabled sur po-2024 -> non remesurable depuis ce siege (hostname de cette lane = myia-po-2027) ; mesure firsthand par une lane tierce sur po-2024 le 2026-09-27 (systemctl is-enabled = enabled, is-active = active sur les trois unites) ; je ne l'ai pas re-verifie moi-meme
residue: none
open-prs: 1
comments-reviewed: 6
[/CLOSURE PREFLIGHT]
[CLOSURE PREFLIGHT]
schema: 1
lane: myia-po-2027:CoursIA
issue: 14846
verdict: KEEP
acceptance:- A1, une sonde rougit quand une classe de label requise passe sous un plancher de runners en ligne, et nomme la classe, le compte en ligne et les machines -> satisfait : scripts/ci/check_runner_starvation.py:388-403 porte le predicat EXTINCTION sur un inventaire separant online/offline (:107-108) et nomme la liste hors ligne ; plancher parametrable (warn_floor) ; les trois classes sont couvertes (coursia-linux par defaut :65, coursia-waiter et coursia-lean par .github/workflows/runner-starvation-advisory.yml:77-82) — livre par feat(ci,#14846): runner starvation advisory multi-label (waiter+lean) #15423 et fix(ci,#15941): align runner-starvation-advisory on its 2-label matrix, drop the inert guard #15948
- A1, controle positif exige : la sonde doit rougir sur l'etat de l'incident (zero runner en ligne, N enregistres tous hors ligne) -> satisfait : scripts/tests/test_check_runner_starvation.py:29 (test_extinction_zero_online_runner_is_error) construit un inventaire a zero en ligne et assert ERROR + EXTINCTION + nom de machine — livre par feat(ci,#14846): runner starvation advisory multi-label (waiter+lean) #15423
- A2, premiere moitie : unite persistante et enabled pour les waiters d'ai-01, sur le modele de coursia-runner.service -> satisfait : scripts/ci/docker/linux-runner/persist/ai-01/coursia-waiters.service (ExecStart :54, Restart=always :56, RestartSec=30 :57, WantedBy=multi-user.target :71, aucun EnvironmentFile) ; le jeton est relu dans master.env au demarrage par le wrapper (persist/coursia-waiters-start.sh:20-31, abandon si absent) — livre par feat(ci-runner,#14612): persistance systemd de la jambe waiters (coursia-waiters.service) #14981
- A2, seconde moitie : installation portee par un script du depot, pas un geste a la main -> NON SATISFAIT sur main ce jour : git grep -ln "install-coursia-units" rend vide, find scripts/ci -iname "install" ne rend que install_prune_task.py, et git grep -ln "/etc/systemd/system" sur .sh/.py rend vide ; les commandes n'existent qu'en prose commentee (scripts/ci/docker/linux-runner/persist/README.md:268-279) — le script est porte par une PR ouverte non mergee (PR 19440, qui cite l'issue par son titre seulement : hors du scan open-prs du gate)
- A3, verifier que coursia-waiters.service est enabled sur po-2024 -> non remesurable depuis ce siege (hostname de cette lane = myia-po-2027) ; mesure firsthand par une lane tierce sur po-2024 le 2026-09-27 (systemctl is-enabled = enabled, is-active = active sur les trois unites) ; je ne l'ai pas re-verifie moi-meme
residue: none
open-prs: 0
comments-reviewed: 6
[/CLOSURE PREFLIGHT]
[CLOSURE PREFLIGHT]
schema: 1
lane: myia-po-2027:CoursIA
issue: 14846
verdict: KEEP
acceptance:- A1, une sonde rougit quand une classe de label requise passe sous un plancher de runners en ligne, et nomme la classe, le compte en ligne et les machines -> satisfait : scripts/ci/check_runner_starvation.py:388-403 porte le predicat EXTINCTION sur un inventaire separant online/offline (:107-108) et nomme la liste hors ligne ; plancher parametrable (warn_floor) ; les trois classes sont couvertes (coursia-linux par defaut :65, coursia-waiter et coursia-lean par .github/workflows/runner-starvation-advisory.yml:77-82) — livre par feat(ci,#14846): runner starvation advisory multi-label (waiter+lean) #15423 et fix(ci,#15941): align runner-starvation-advisory on its 2-label matrix, drop the inert guard #15948
- A1, controle positif exige : la sonde doit rougir sur l'etat de l'incident (zero runner en ligne, N enregistres tous hors ligne) -> satisfait : scripts/tests/test_check_runner_starvation.py:29 (test_extinction_zero_online_runner_is_error) construit un inventaire a zero en ligne et assert ERROR + EXTINCTION + nom de machine — livre par feat(ci,#14846): runner starvation advisory multi-label (waiter+lean) #15423
- A2, premiere moitie : unite persistante et enabled pour les waiters d'ai-01, sur le modele de coursia-runner.service -> satisfait : scripts/ci/docker/linux-runner/persist/ai-01/coursia-waiters.service (ExecStart :54, Restart=always :56, RestartSec=30 :57, WantedBy=multi-user.target :71, aucun EnvironmentFile) ; le jeton est relu dans master.env au demarrage par le wrapper (persist/coursia-waiters-start.sh:20-31, abandon si absent) — livre par feat(ci-runner,#14612): persistance systemd de la jambe waiters (coursia-waiters.service) #14981
- A2, seconde moitie : installation portee par un script du depot, pas un geste a la main -> NON SATISFAIT sur main ce jour : git grep -ln "install-coursia-units" rend vide, find scripts/ci -iname "install" ne rend que install_prune_task.py, et git grep -ln "/etc/systemd/system" sur .sh/.py rend vide ; les commandes n'existent qu'en prose commentee (scripts/ci/docker/linux-runner/persist/README.md:268-279) — le script est porte par une PR ouverte non mergee (PR 19440, qui cite l'issue par son titre seulement : hors du scan open-prs du gate)
- A3, verifier que coursia-waiters.service est enabled sur po-2024 -> non remesurable depuis ce siege (hostname de cette lane = myia-po-2027) ; mesure firsthand par une lane tierce sur po-2024 le 2026-09-27 (systemctl is-enabled = enabled, is-active = active sur les trois unites) ; je ne l'ai pas re-verifie moi-meme
residue: none
open-prs: 0
comments-reviewed: 8
[/CLOSURE PREFLIGHT]
- added a commit that references this issue
on Oct 8, 2026 [CLOSURE PREFLIGHT]
schema: 1
lane: myia-ai-01:CoursIA-2
issue: 14846
verdict: CLOSE
acceptance:- A1 : une sonde rougit quand une classe de label requise passe sous un plancher de runners en ligne, nomme la classe, le compte et les machines -> feat(ci,#14846): runner starvation advisory multi-label (waiter+lean) #15423 MERGED (scripts/ci/check_runner_starvation.py:394 predicat EXTINCTION, inventaire online/offline :106-108) + fix(ci,#15941): align runner-starvation-advisory on its 2-label matrix, drop the inert guard #15948 MERGED (.github/workflows/runner-starvation-advisory.yml:77-82, labels coursia-waiter + coursia-lean, fichier verifie sur origin/main)
- A1 controle positif : la sonde doit rougir sur l'etat mesure (0 en ligne, N enregistres hors ligne, distinction 0-enregistre vs N-hors-ligne) -> scripts/tests/test_check_runner_starvation.py:29-37 (test_extinction_zero_online_runner_is_error : inventaire a zero en ligne, assert ERROR + EXTINCTION + nom de machine ; liste offline nommee dans le message)
- A2 premiere moitie : waiters d'ai-01 dans une unite persistante et enabled, jeton lu de master.env jamais recopie -> feat(ci-runner,#14612): persistance systemd de la jambe waiters (coursia-waiters.service) #14981 MERGED ; scripts/ci/docker/linux-runner/persist/ai-01/coursia-waiters.service (Restart=always :56, WantedBy=multi-user.target :71, aucun EnvironmentFile) + wrapper persist/coursia-waiters-start.sh:20-31 ; mesure firsthand sur myia-ai-01 ce jour :
systemctl is-enabled coursia-waiters.service= enabled,is-active= active - A2 seconde moitie : installation portee par un script du depot, pas un geste a la main -> feat(ci,#14846): install-coursia-units.sh -- A2 seconde moitie, tapis #19440 MERGED 2026-10-08 (1fa9ae8) : scripts/ci/docker/linux-runner/install-coursia-units.sh (286 l., idempotent, garde non-root, sha256) + scripts/ci/docker/linux-runner/test_install_coursia_units_slice.sh (242 l., 6 assertions) — l'objection ai-01 du 2026-10-05 (« il manque l'installeur ») est levee par ce merge
- A3 : verifier que coursia-waiters.service est enabled sur po-2024 -> mesure firsthand par la lane po-2024 elle-meme (dossier 2026-09-27T22:33:22Z :
systemctl is-enabled= enabled, is-active = active sur les trois unites ; hypothese « deployee sans enable » refutee) ; le controle est desormais automatise en fin d'install (install-coursia-units.sh, sortie code 2 sur echec)
residue: none
open-prs: 0
comments-reviewed: 9
[/CLOSURE PREFLIGHT]
Lot D (#18140), lane
myia-ai-01:CoursIA-2.CLOSE : les trois acceptances ont chacune un artefact merge et verifie sur
origin/main; la derniere objection datee (ai-01, 2026-10-05) est eteinte par #19440.Fermeture ai-01 sur dossier [CLOSURE PREFLIGHT] de myia-ai-01:CoursIA-2 (gate rc=0). Verifie firsthand sur origin/main : check_runner_starvation.py + son test d'extinction, runner-starvation-advisory.yml, coursia-waiters.service (ai-01), install-coursia-units.sh + son test de tranche. A1, controle positif, A2 (deux moities) et A3 couverts.
Le fait
Le 2026-09-05 entre 20:26Z et 00:52Z, aucune PR du depot n'a pu merger. Le
PR gateest le seul check requis par la protection demain; sa jambe same-repo tourne surruns-on: [self-hosted, coursia-waiter](pr-gate.yml:106). Les 28 runners de ce pool etaient hors ligne, donc le job restaitqueuedsans fin.Mesure a 00:47Z :
PR gatereussi : 20:25:48Z ; ensuite, tous les runspull_requestenqueuedoucancelled-> 4 h 20 de gelcoursia-linux: 15 en ligne et libres -- ce n'etait pas de la faminecoursia-waiter: 0 en ligne / 28 hors lignequeueddepuis 20:27-21:22Cause racine plus large que le pool : po-2024 ne portait plus aucun runner en ligne (26 enregistres, 0 en ligne), waiters, Lean et execution confondus. Le parc self-hosted entier reposait sur ai-01.
Les deux defauts a corriger
1. Un pool tombe ne rougit nulle part
Un runner enregistre mais hors ligne laisse le job
queuedindefiniment au lieu d'echouer vite. Pendant 4 h 20, aucun organe n'a rougi : les PRs affichaientBLOCKED, ce qui se lit exactement comme « en attente de review ». C'est ce qui a rendu la panne invisible -- j'ai commence par diagnostiquer #14790 comme un defaut de PR avant de mesurer le parc.scripts/ci/check_runner_starvation.pyexiste deja mais mesure la famine (jobs en attente / slots occupes), pas la disparition d'une classe. Les deux se distinguent nettement : ici les 15 slots d'execution etaient libres.Acceptance A1 : une sonde rougit quand une classe de label requise par un workflow passe sous un plancher de runners en ligne (
coursia-waiter>= 1,coursia-linux>= 1,coursia-lean>= 1 tant qu'un workflow les cible). La sonde nomme la classe, le compte en ligne, et les machines qui la portaient.Controle positif exige : la sonde doit rougir sur l'etat mesure ici (0 waiter en ligne, 28 enregistres). Un detecteur qui ne rougit pas sur l'incident qui le motive n'est pas un detecteur -- et une classe a 0 runner enregistre doit se distinguer de N enregistres tous hors ligne : le premier fait echouer le job vite, le second le gele.
2. Le pool de gate n'etait pas redonde, et sa reparation ne l'est pas encore
La jambe A de #13363 a place les 24 waiters sur une seule machine ; sa chute suffisait a geler le depot. J'ai leve 12 waiters sur ai-01 pendant l'incident (
myia-ai-01-linux-waiter-*, 1 vCPU / 1 GiB), ce qui a debloque les gates en moins d'une minute -- mais ils vivent dans une unite systemd transitoire (systemd-run --unit=coursia-waiters). Un redemarrage de la distro et la panne se rejoue.Acceptance A2 : les waiters d'ai-01 vivent dans une unite persistante et
enabled, sur le modele exact decoursia-runner.service(Restart=always,WantedBy=multi-user.target, jeton lu depuismaster.envau demarrage et jamais recopie dans unEnvironmentFile). Installation portee par un script du depot, pas un geste a la main.Acceptance A3 : verifier que
coursia-waiters.serviceestenabledsur po-2024. Si la jambe A a ete deployee sansenable, elle n'est persistante que sur le papier -- c'est l'hypothese la plus probable de l'incident, et elle se tranche parsystemctl is-enabled.Ce que cet incident ne dit pas
Il ne dit pas pourquoi po-2024 a quitte le parc a 20:26Z. Le journal (
journalctl -u coursia-waiters --since '2026-09-05 20:00') le dira : OOM, perte de docker, logoff, ou arret explicite. Selon la reponse, A3 suffit ou il faut une issue de fond distincte. Demande envoyee a la lane po-2024 (DM URGENTmsg-20260906T005223-bzkdaq).Contexte
pr-gate.yml:106--runs-onconditionnel, jambe waiter (CI: un tiers des runners tenu par 'PR gate' en attente — les correctifs #11405 et #11770 sont en tension et personne ne possede l'arbitrage #13363)scripts/ci/docker/linux-runner/supervise.sh--cmd_waiters, prefixe par defautmyia-po-2024-linux-waiter/usr/local/bin/coursia-runner-start.sh(ai-01) -- le patron de persistance a suivre pour A2