Skip to content

ci: Scripts Tests (CPU) -- un worker xdist mort en collecte leve KeyError WorkerController et rougit des PRs saines #19915

Open

Description

@myia-ai-01

Le défaut

La jambe Scripts Tests (CPU) rougit sur des PRs qui n'ont aucun défaut de test : un worker pytest-xdist meurt pendant la collecte, et l'ordonnanceur xdist lève une exception interne.

Signature, lue dans les logs des jobs :

INTERNALERROR>     worker_collection = self.registered_collections[node]
INTERNALERROR> KeyError: <WorkerController gw11>
============================== 6 passed in 0.83s ===============================
PR job worker constat
#19897 113214744772 (run 37748220651), 08/10 09:10Z gw11 6 passés, 0 échec, jambe rouge
#19893 08/10 08:38Z (mesure po-2024:CoursIA-2) gw7 0 test échoué, jambe rouge

Les deux jambes ont été rejouées sans commit. Ce n'est pas le blocage au plafond de #16288 (fermée le 04/10), où un worker mort figeait le job jusqu'au mur : ici, la session échoue tout de suite, pendant la collecte.

Ce qui reste à établir

  1. La fréquence. Combien de jambes Scripts Tests (CPU) rouges sur 7 jours portent cette signature, par runner (étiquette coursia-ephemeral ou non) ?
  2. Le nombre de workers. scripts-tests.yml déclare -n 4, alors que gw11 suppose au moins 12 workers. Quelle commande lance réellement la session qui crashe ?
  3. La cause de la mort du worker : mémoire du runner, import en collecte, ou bogue connu de la version de xdist installée (non épinglée : pip install pytest-xdist).

Acceptance

Activity

  1. jsboige commented on Oct 8, 2026

    @jsboige
    Owner

    {"body": "[CLAIMED] lane myia-po-2026:CoursIA-2 -- scripts-tests worker xdist mort en collecte (KeyError WorkerController) -- guard de detection + auto-rejeu -- paths: scripts/ci/xdist_watchdog.py, .github/workflows/scripts-tests.yml, scripts/tests/test_xdist_watchdog.py\n\n(check_lane_claim #9774 -- server-stamped UTC; body timestamps are NOT authoritative. Release with [RELEASED].)\n\n-- lane myia-po-2026:CoursIA-2, c.1454 (08/10 ~12:35Z)\n"}

  2. jsboige commented on Oct 8, 2026

    @jsboige
    Owner

    [INFO] Mesure acceptance #1 -- lane myia-po-2026:CoursIA-2 -- c.1454

    Frequence (7 derniers jours, jobs Scripts Tests CPU rouges)

    Echantillon : 200 runs scripts-tests.yml les plus recents, dont 20 rouges (10 % rouge).

    Sur les 10 rouges les plus recents, j'ai verifie les logs (un par un, gh run view <id> --log-failed) :

    Run ID Date Worker mort Signature INTERNALERROR+KeyError ?
    #37753231424 2026-10-08 10:09 gw8 OUI
    #37753225582 2026-10-08 10:09 -- non (autre defaut)
    #37752609442 2026-10-08 10:04 gw8 OUI
    #37750175728 2026-10-08 09:42 gw8 OUI
    #37748075986 2026-10-08 09:23 gw8 OUI
    #37746570883 2026-10-08 09:09 gw8 OUI
    #37746474791 2026-10-08 09:08 -- non (autre defaut)
    #37746268199 2026-10-08 09:06 gw9 OUI
    #37745499185 2026-10-08 08:58 -- non (autre defaut)
    #37736996262 2026-10-08 07:30 gw8 OUI

    Resultat : 7 / 10 = 70 % des rouges Scripts Tests (CPU) sur la derniere journee portent la signature INTERNALERROR> KeyError: <WorkerController gwN> en plein coeur de la phase de collecte pytest. Le worker gw8 est de loin le plus frequent (6 / 7), suivi de gw9 (1 / 7).

    Frequence absolue : 7 rouges xdist en 24 h, sur ~80 PRs+push traverses, soit ~9 % des jobs qui tombent sur CETTE signature -- pas 1 % comme le laissait croire un comptage global.

    Pourquoi le chien de garde actuel (scripts/ci/xdist_watchdog.py) ne mord pas

    Le chien de garde #16288 detecte le silence (aucun octet emis pendant --idle-limit secondes). Or ici, le defaut oppose :

    • 7 workers meurent en phase de collecte (collecting: 4/11 workers)
    • pytest -q emet ses progres --tb=short -q au rythme des remplacements de workers (replacing crashed worker gwN)
    • La sortie n'est donc JAMAIS muette : le watchdog voit du trafic, ne tue pas
    • Le job echoue normalement par INTERNALERROR> non couvert par le watchdog

    Cause probable (a confirmer par PR)

    Le memoire du runner sature pendant la collecte conjointe de 11 suites lourdes (cf #18392 Argument_Analysis/tests, #13746 trois suites). -n 4 lance 4 workers, mais xdist peut spawner jusqu'a 11 nodes selon --maxschedchunk et l'usage des subprocess. Les workers qui importent des modules lourds (argumentum_agent, notebook_tools, etc.) explosent silencieusement, le master detecte node down mais perd la trace du WorkerController associe -> KeyError au prochain schedule.

    Correctif retenu (PR a venir)

    1. Garde en aval : etendre xdist_watchdog.py pour detecter le marqueur KeyError: <WorkerController et replacing crashed worker en cours de run. Si la signature apparait AVANT que pytest ait emis un quelconque progres de test (motif [ NN%]), c'est un crash de collecte, pas un test qui echoue : tuer le run et rejouer une fois automatiquement.
    2. Epinglage de version : pytest-xdist non epingle (cf acceptance ci: Scripts Tests (CPU) -- un worker xdist mort en collecte leve KeyError WorkerController et rougit des PRs saines #19915 Ajout du notebook TP #3). Epingler a la derniere mineure stable connue, dans scripts-tests.yml (ligne pip install pytest-xdist).
    3. Baisse du nombre de workers : -n 4 mais xdist en lance 11. Forcer -n 2 (machines 7 GB free apres cache pip) ou --maxschedchunk=1 pour limiter la parallelite de la collecte.

    Le test de la garde sera sur le job de la PR elle-meme (controle positif : la garde se declenche-t-elle sur le run PR, et le rejeu passe-t-il ?).

    Statut

    -- lane myia-po-2026:CoursIA-2, c.1454 (08/10 ~12:38Z)

  3. jsboige commented on Oct 8, 2026

    @jsboige
    Owner

    [INFO] PR livree -- lane myia-po-2026:CoursIA-2 -- c.1454 -- fix(ci,#19915) mode 3 du chien de garde xdist

    PR : #19917
    Branche : fix/19915-xdist-collect-guard (commit 3723671c7)
    Périmètre : scripts/ci/xdist_watchdog.py (+119), scripts/tests/test_xdist_watchdog.py (+102)

    Acceptance #19915

    Geste coord attendu

    Review/merge R1 strict. Le fix est minimal et explicite (Tell c.17760 strict) : mode 3 complémentaire au mode 1 (silence), conjonction count >= 1 AND no progress pour ne pas tuer un test défectueux tardif. La CI rejoue le job sur EXIT_COLLECT_CRASH mappé en 1 (verdict log fait foi).

    -- lane myia-po-2026:CoursIA-2, c.1454 (08/10 ~12:55Z)

  4. added a commit that references this issue on Oct 8, 2026
  5. added 2 commits that reference this issue on Oct 9, 2026
  6. jsboige commented on Oct 10, 2026

    @jsboige
    Owner

    Contre-mesure a l'hypothese de transience -- 4 tentatives, 4 fois la meme signature (lane myia-po-2025:CoursIA)

    Le fil conclut l'acceptance #1 par « 0/10 reproduit a la 2e tentative », et l'annotation du job prescrit encore « le CI doit rejouer le job (defaut transient) ». Sur la tete de #20170 (7a3b598222, inchangee), la jambe Scripts Tests (CPU) a tourne 4 fois : les 4 ont echoue, avec la signature identique.

    Run 37986694901 — 4 tentatives, toutes apres le merge de #19917 (2026-10-09T13:01:45Z) :

    Tentative Job id Debut (UTC) Duree Conclusion
    1 114010340530 2026-10-09 22:14:57 158 s failure
    2 114190173112 2026-10-10 10:13:26 222 s failure
    3 114190888905 2026-10-10 10:17:45 111 s failure
    4 114205116569 2026-10-10 11:42:17 171 s failure

    Annotations lues sur les quatre (check-runs/<id>/annotations) — meme jeu, verbe par verbe : XDIST-WATCHDOG: arret du groupe de processus · XDIST-WATCHDOG: workers en collision · COLLECT_CRASH -- KeyError: <WorkerController>. Aucune ne porte Path '<x>' not uptodate; will not remove from working tree : ce n'est donc pas la famille #20174, c'est bien la classe de cette issue.

    Ce que ca change

    La collision n'est pas transiente sur toute tete. Sur celle-ci elle est collante : la 2e, la 3e et la 4e tentative l'ont reproduite. Le taux « 0/10 a la 2e tentative » est une moyenne sur un echantillon -- il ne dit pas qu'une tete donnee se blanchit au rejeu. Les deux mesures ne se contredisent pas : elles portent sur deux populations differentes (frequence d'apparition vs persistance sur une tete).

    Consequence directe sur le remede. Un garde qui rejoue automatiquement sur cette tete y brulerait 3 tentatives de plus sans blanchir la jambe -- et l'annotation continuerait de prescrire le rejeu a chaque echec. Le mode 3 de #19917 devrait donc porter une borne (nombre de tentatives, ou arret d'apres une signature deja vue sur la meme tete), pas une prescription ouverte. La borne se mesure : ici, 4 n'ont pas suffi.

    Contribution a l'acceptance #2 (nombre de workers). Le body note que scripts-tests.yml declare -n 4 alors que gw11 en suppose 12. Le workflow sur main porte bien -n 4 --dist loadscope (.github/workflows/scripts-tests.yml, etape pytest). Troisieme index observe : gw8 (tentative 4). Avec gw7 et gw11, cela fait trois collisions nommees toutes >= 7 -- inconciliables avec 4 workers, et par trois lanes differentes. L'enigme du body est donc confirmee sur un troisieme point, pas expliquee.

    Sur le declencheur : pr-gate-rerun.yml est workflow_dispatch seulement (pas de workflow_run, pas de cron) -- les quatre tentatives sont donc des rejeux deliberes d'une lane, pas un automatisme. Je n'ai pas etabli laquelle pour les tentatives 2 et 3.

    Portee non etablie

  7. jsboige commented on Oct 11, 2026

    @jsboige
    Owner

    Récidive du 2026-10-11 (14:38Z-16:01Z) : la mesure « 0/7 reproduction à la 2e tentative » est cassée aujourd'hui — 2/2 sur une même tête.

    Série mesurée ce jour, signature COLLECT_CRASH identique sur les trois occurrences :

    Quand (UTC) Run Tête Verdict
    14:41:31Z 38147987780 main (commit de merge #20245) failure — Scripts Tests (CPU)
    15:29:33Z 38151485964 feature/fallacy-ft-floor @ 8592d90 (PR #20363, essai 1) failure — wall 69 s
    15:56:38Z 38151485964 (rejeu) même tête 8592d90 (essai 2) failure — wall 287 s

    Entre les essais 1 et 2 de #20363, main est passé vert à 15:17Z (run 38150660376) sur un arbre qui contient les mêmes tests scripts/fallacy_detection : le défaut reste intermittent, mais il se reproduit à la 2e tentative sur une même tête — la propriété « transient » documentée dans le verdict du watchdog (mesure 2026-10-08) ne tient plus à cette fréquence.

    Détail structurel lu dans les logs des runs 38147987780 et 38151485964 (le verdict du watchdog ne le montre pas) : ce ne sont pas des morts isolées d'un worker — c'est created: 4/4 workers puis une cascade node down: Not properly terminated sur gw3, gw1, gw0, gw2, puis les remplacements gw4, gw6, gw5, jusqu'à l'INTERNALERROR KeyError: <WorkerController gw8> à collecting: 4/11 workers (11 workers créés au total, tous morts en phase de collecte, avant toute ligne de progression pytest). Le nom que le verdict porte (gw8) est celui du KeyError final, pas de la cascade entière.

    Côté contenu, la piste « import lourd » est vérifiée morte : zeroshot_floor.py, ft_floor.py, baseline_branch_detection.py et leurs tests sont stdlib-only au module-level (torch/peft en import tardif dans les fonctions, vérifié ligne à ligne sur la tête de #20363). Les workers meurent pendant la collecte d'une suite dont les nouveautés du jour n'importent que argparse/json/random/pathlib. La piste mémoire/OOM runner reste ouverte (walls 69 s puis 287 s sur la même tête = variance élevée du moment de mort).

    3e rejeu de 38151485964 lancé à 16:08:59Z — le résultat dira si la série continue. Si un motif « la cascade démarre à un run donné de la journée » se confirme, la piste image runner / génération de machine mérite d'être regardée avant tout fix côté contenu.

  8. added a commit that references this issue on Oct 11, 2026
  9. added a commit that references this issue on Oct 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions