Repository navigation
feat(ci,#15574): instrument de caractérisation de la variance runners (items 1-2) - #18673
Conversation
… (items 1-2) characterize_runner_variance.py collecte les jobs complets d'un workflow (runner, horodatages, conclusion), attribue chaque job a son hote physique par prefixe de runner, et mesure la duree selon trois axes : par runner, par heure locale, et par co-residence (nombre d'autres jobs lourds chevauches sur le meme hote -- la grandeur que l'item 3 borne). Sortie JSON + tableau humain ; instrument de mesure, pas un gate (rc 0 = mesure, rc 1 = [PANNE] API). Trois pieges d'API mesures et documentes dans le code : percent-encodage du >= du filtre created (un > brut rend 0 run en silence), completed_at non fiable sur le endpoint LIST (prefiltre sur created_at), pagination &page=N jusqu'a page courte (gh api n'expose pas next_page_url dans le corps). 15 tests sans reseau (collecte injectable, chevauchements frontieres, buckets de co-residence, [PANNE]). See #15574 Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
|
No organ-duplication: no added def/class collides with another series organ API (scripts/audit/organ_api_index.yaml). Detector: |
|
Cette PR depasse le seuil de couverture review (par defaut 300 additions) et n'a recu aucune review -- ni bot, ni humaine. Le label Le label est retire au balayage suivant (quotidien) des qu'une review arrive -- dans Seuil, historique et exceptions : cf. |
|
[ADJOINT PREFLIGHT] |
Grain: DEEP/research-code — lane myia-po-2024:CoursIA — prev: LIGHT/tooling #18668
Summary
Items 1-2 de #15574 : la variance 3-4x du pool self-hosted est caractérisée par mesure et la co-résidence par hôte est écrite. Deux livrables : un instrument (
scripts/ci/characterize_runner_variance.py, 15 tests, sans réseau) et les nombres qu'il produit, croisés avec une mesure directe de la flotte de slots.Mesure directe de la co-résidence (item 2) — po-2024, 2026-10-01 ~11:20Z
La flotte CI de po-2024 vit dans une VM WSL unique (16 vCPU, 24 Go) derrière le daemon docker-ce dédié (socket
/var/run/docker-ce.sock, cf piège « docker ps nu rend 0 »). État mesuré :linux-docker(poolcoursia-linux)lean-docker(poolcoursia-lean)linux-waiter(poolcoursia-waiter)docker ps -a | grep -c waiter) : le déploiement historique de 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 en portait 24 ; la flotte a été redimensionnée depuis.--cpusdocker est par conteneur ; il ne borne pas une flotte. Le harnais le sait déjà (assert_cpu_budget, supervise.sh, CI: pools de runners specialises par labels (cache Mathlib chaud, quarto, navigateurs) plutot qu'une image unique qui grossit #14337) mais le garde est désactivé par défaut (COURSIA_RUNNER_CPU_BUDGET=0→ retour immédiat). L'état live est exactement celui que le garde refuse de lancer.Instrument (item 1) —
characterize_runner_variance.pyCollecte les jobs complétés d'un workflow via l'API GitHub (runner, horodatages, conclusion), attribue chaque job à son hôte physique par préfixe de nom de runner (#15152 : le préfixe dérive de la machine), puis mesure la durée selon trois axes :
Sortie JSON (machine) + tableau (humain). Instrument de mesure, pas un gate : rc 0 = mesure produite, 1 =
[PANNE](API injoignable). Trois pièges d'API corrigés en cours de route, documentés dans le code : le>=du filtrecreateddoit être percent-encodé (un>brut rend zéro run en silence), le endpoint LIST ne porte pascompleted_atde façon fiable (préfiltre surcreated_at), et gh api n'expose pasnext_page_urldans le corps (pagination&page=Njusqu'à page courte — sans elle, seule la centaine de runs la plus récente est vue).Limite d'attribution assumée : les slots
ai-01-wsl-Nsont traités chacun comme son propre hôte (le préfixe ne distingue pas leurs familles de slots) — leur co-résidence interne est donc sous-comptée. Ils servent ici de groupe de contraste externe, pas d'objet de mesure ; leurs hosts relèvent d'ai-01.Complémentaire de #18626 (item 4, mergé) :
check_runner_variance.pysignale la variance à postériori (garde advisory), l'instrument présent explique la variance (par runner / par heure / par co-résidence) — la décision item 3 consomme les deux.Résultats sur 7 jours (scripts-tests.yml)
Fenêtre 2026-09-24 → 2026-10-01, 1749 jobs complétés dont 637 lourds (docker/lean). Médiane globale 133 s.
Durée par co-résidence — le chiffre-titre (jobs lourds, hôte po-2024) :
Proposition pour l'item 3 (décision — pas appliquée ici)
La décision de plafond est un arbitrage débit/variance qui dépasse cette lane ; proposition soumise sur l'issue :
COURSIA_RUNNER_CPU_BUDGET=16dans l'environnement de déploiement des superviseurs (variable déjà lue parassert_cpu_budget, refus au démarrage, jamais avertissement).ai-01-wsl-*) relèvent d'ai-01 : recommandation uniquement, pas de geste.Test plan
python -m pytest scripts/tests/test_characterize_runner_variance.py -q→ 15 passed (collecte injectable, aucun réseau)scripts-tests.yml: rc 0, 1749 jobs / 637 lourds collectés (sortie ci-dessus)host_of/is_heavycouverts par tests unitaires (suffixes de famille, machines étrangères)See #15574 (items 1-2 ; item 3 proposé, item 4 déjà livré par #18626)
🤖 Generated with Claude Code