ci(test): sérialise vraiment le job test entre runs concurrents #96

Merged
kyuno merged 2 commits from fix/ci-test-job-serialization into main 2026-09-07 14:30:45 +02:00
Owner

Contexte

En diagnostiquant une contention sévère aujourd'hui sur 3 PR en cours
(#93/#94/#95 — Postgres coupé en cours de run, collisions de cache
d'actions, timeouts Cypress, jusqu'à une violation de clé étrangère au
seed des tour scenarios), j'ai retrouvé une session précédente qui avait
déjà entièrement diagnostiqué et corrigé exactement ce problème —
branche claude/magical-stonebraker-5dc636, mémoire projet
ci-forgejo-runner-constraints — mais jamais ouvert de PR ni mergé.
Ce PR reprend ce travail tel quel (diff identique, appliqué proprement sur
le main actuel, aucun conflit).

Le problème

Deux jobs test qui se chevauchent partagent un seul conteneur --name postgres sur le réseau docker du runner (pas de bloc services:) : leurs
prisma migrate deploy + seedReferenceData (beforeAll racine Mocha)
rentrent en collision — Unique constraint failed on (key), "No pending
migrations" alors qu'il en reste, décalages d'id tech_step/diet cassant
des assertions sans rapport ; et même en dehors de ça ils s'affament
mutuellement en RAM/CPU (chacun entraîne aussi un modèle spaCy) jusqu'à ce
que resetDatabase() dépasse le timeout de hook Mocha (10s) et que ça
cascade. Touché à répétition (PR #47, #53, #54, et de nouveau aujourd'hui),
à chaque fois résolu par une relance à vide une fois le runner plus calme —
jamais un vrai bug de la PR concernée.

concurrency: (le vrai correctif GitHub) est silencieusement ignoré par ce
Forgejo (9.0.3, arrivé en Forgejo v14/Gitea 1.26).

Le fix

Un objet de verrou (réseau, conteneur, fichier) fuirait : ce runner
(forgejo-runner v3.5.0) n'exécute pas les steps if: always() suivant un
step en échec, donc pas de libération fiable en fin de job. À la place :
s'appuyer sur ce que le runner nettoie toujours lui-même — le conteneur du
job (supprimé en succès, échec et annulation) — et attendre tant qu'un job
test de task id inférieur tourne encore. Le plus petit id part en
premier, donc deux jobs démarrés ensemble ne peuvent pas se bloquer
mutuellement ; rien à libérer explicitement.

Trois ajouts, purement additifs (aucune ligne existante modifiée) :

  • nouveau step "Wait for any earlier test job" (poll docker ps, plafond
    ~25min) juste avant le démarrage de Postgres ;
  • timeout-minutes: 45 sur le job (couvre le temps d'attente éventuel +
    garde-fou si le job reste bloqué, vu l'absence de --exit sur Mocha) ;
  • le nettoyage docker rm -f postgres existant balaie aussi les conteneurs
    postgres-<suffixe> orphelins.

Vérification

Déjà vérifié fonctionnel en conditions réelles par la session précédente
(run 130 : "12x waiting for earlier test job(s)... puis proceeding").
Impossible de re-simuler la course en local (pas de second runner ici),
mais le diff s'applique sans le moindre conflit sur l'état actuel de
main — les trois commits de ci.yml survenus depuis (upload-artifact
v3, bit gradlew, build-android) touchent un job entièrement différent du
fichier.

🤖 Generated with Claude Code

## Contexte En diagnostiquant une contention sévère aujourd'hui sur 3 PR en cours (#93/#94/#95 — Postgres coupé en cours de run, collisions de cache d'actions, timeouts Cypress, jusqu'à une violation de clé étrangère au seed des tour scenarios), j'ai retrouvé une session précédente qui avait déjà **entièrement diagnostiqué et corrigé exactement ce problème** — branche `claude/magical-stonebraker-5dc636`, mémoire projet `ci-forgejo-runner-constraints` — mais **jamais ouvert de PR ni mergé**. Ce PR reprend ce travail tel quel (diff identique, appliqué proprement sur le `main` actuel, aucun conflit). ## Le problème Deux jobs `test` qui se chevauchent partagent un seul conteneur `--name postgres` sur le réseau docker du runner (pas de bloc `services:`) : leurs `prisma migrate deploy` + `seedReferenceData` (beforeAll racine Mocha) rentrent en collision — `Unique constraint failed on (key)`, "No pending migrations" alors qu'il en reste, décalages d'id `tech_step`/`diet` cassant des assertions sans rapport ; et même en dehors de ça ils s'affament mutuellement en RAM/CPU (chacun entraîne aussi un modèle spaCy) jusqu'à ce que `resetDatabase()` dépasse le timeout de hook Mocha (10s) et que ça cascade. Touché à répétition (PR #47, #53, #54, et de nouveau aujourd'hui), à chaque fois résolu par une relance à vide une fois le runner plus calme — jamais un vrai bug de la PR concernée. `concurrency:` (le vrai correctif GitHub) est silencieusement ignoré par ce Forgejo (9.0.3, arrivé en Forgejo v14/Gitea 1.26). ## Le fix Un objet de verrou (réseau, conteneur, fichier) fuirait : ce runner (forgejo-runner v3.5.0) n'exécute pas les steps `if: always()` suivant un step en échec, donc pas de libération fiable en fin de job. À la place : s'appuyer sur ce que le runner nettoie toujours lui-même — le conteneur du job (supprimé en succès, échec et annulation) — et attendre tant qu'un job `test` de task id inférieur tourne encore. Le plus petit id part en premier, donc deux jobs démarrés ensemble ne peuvent pas se bloquer mutuellement ; rien à libérer explicitement. Trois ajouts, purement additifs (**aucune ligne existante modifiée**) : - nouveau step "Wait for any earlier test job" (poll `docker ps`, plafond ~25min) juste avant le démarrage de Postgres ; - `timeout-minutes: 45` sur le job (couvre le temps d'attente éventuel + garde-fou si le job reste bloqué, vu l'absence de `--exit` sur Mocha) ; - le nettoyage `docker rm -f postgres` existant balaie aussi les conteneurs `postgres-<suffixe>` orphelins. ## Vérification Déjà **vérifié fonctionnel en conditions réelles par la session précédente** (run 130 : "12x waiting for earlier test job(s)... puis proceeding"). Impossible de re-simuler la course en local (pas de second runner ici), mais le diff s'applique sans le moindre conflit sur l'état actuel de `main` — les trois commits de `ci.yml` survenus depuis (upload-artifact v3, bit gradlew, build-android) touchent un job entièrement différent du fichier. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
kyuno added 1 commit 2026-09-07 13:42:55 +02:00
ci(test): serialise vraiment le job test entre runs concurrents
Some checks failed
CI / lint (push) Successful in 2m47s
CI / build (push) Successful in 4m42s
CI / intent-service-test (push) Successful in 4m58s
CI / test (push) Failing after 14m15s
CI / e2e (push) Successful in 13m33s
CI / build-android (push) Has been skipped
8f75a47b23
Deux jobs `test` qui se chevauchent partagent un seul conteneur
`--name postgres` sur le reseau docker du runner (pas de bloc
`services:`) : leurs `prisma migrate deploy` + `seedReferenceData`
(beforeAll racine Mocha) rentrent en collision - "Unique constraint
failed on (key)", "No pending migrations" alors qu'il en reste,
decalages d'id tech_step/diet cassant des assertions sans rapport ;
et meme en dehors de ca ils s'affament mutuellement en RAM/CPU
(chacun entraine aussi un modele spaCy) jusqu'a ce que
resetDatabase() depasse le timeout de hook Mocha (10s) et que ca
cascade. Touche a repetition ce mois-ci (PR #47, #53, #54, et de
nouveau aujourd'hui sur #93/#94/#95), a chaque fois resolu par une
relance a vide une fois le runner plus calme - jamais un vrai bug de
la PR concernee.

`concurrency:` (le vrai correctif GitHub) est silencieusement ignore
par ce Forgejo (9.0.3, arrive en v14/Gitea 1.26). Un objet de verrou
(reseau, conteneur, fichier) fuirait : ce runner (forgejo-runner
v3.5.0) n'execute pas les steps `if: always()` suivant un step en
echec, donc pas de liberation fiable en fin de job. A la place :
s'appuyer sur ce que le runner nettoie toujours lui-meme - le
conteneur du job (supprime en succes, echec et annulation) - et
attendre tant qu'un job `test` de task id inferieur tourne encore.
Le plus petit id part en premier, donc deux jobs demarres ensemble ne
peuvent pas se bloquer mutuellement ; rien a liberer explicitement.

Trois ajouts, purement additifs (aucune ligne existante modifiee) :
- nouveau step "Wait for any earlier test job" (poll docker ps,
  plafond ~25min) juste avant le demarrage de Postgres ;
- `timeout-minutes: 45` sur le job (couvre le temps d'attente
  eventuel + garde-fou si le job reste bloque, vu l'absence de
  --exit sur Mocha) ;
- le nettoyage `docker rm -f postgres` existant balaie aussi les
  conteneurs `postgres-<suffixe>` orphelins (residus d'un essai
  anterieur de nommage par run, deja abandonne sur cette meme
  branche, jamais nettoyes faute de teardown fiable).

Travail deja fait et documente par une session precedente (branche
claude/magical-stonebraker-5dc636, verifie fonctionnel en run 130 :
"12x waiting for earlier test job(s)... puis proceeding") mais jamais
fusionne - retrouve et applique aujourd'hui en diagnostiquant la
meme contention sur trois PR en cours (#93/#94/#95).

Verification : impossible de re-simuler la course en local (pas de
second runner ici), mais le diff est purement additif et applique
sans le moindre conflit sur l'etat actuel de main - les trois commits
de ci.yml survenus depuis (upload-artifact v3, bit gradlew,
build-android) touchent un job entierement different du fichier.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
kyuno added 1 commit 2026-09-07 14:02:21 +02:00
chore: relance la CI (Postgres tue par un job non protege pendant l'attente)
All checks were successful
CI / lint (push) Successful in 2m23s
CI / build (push) Successful in 2m24s
CI / intent-service-test (push) Successful in 4m31s
CI / e2e (push) Successful in 7m39s
CI / test (push) Successful in 25m37s
CI / build-android (push) Has been skipped
f99bf46407
Le nouveau step "Wait for any earlier test job" a fonctionne comme
prevu (13 tentatives, ~4m30s a attendre le job 1217, puis
"proceeding") - mais Postgres a ete coupe quasi immediatement apres
(0 passing, Can't reach database server). Cause : d'autres jobs test
en cours sur d'autres PR (#93/#94/#95, sans le correctif) executent
encore `docker rm -f postgres` sans condition au demarrage de LEUR
propre run, y compris pendant que ce job-ci utilise deja le
conteneur. Le correctif protege un job qui demarre poliment, pas
contre un job non protege qui demarre en parallele. Je laisse #93/#94/
#95 tranquilles maintenant et je ne relance que celle-ci jusqu'a ce
qu'elle passe.
kyuno merged commit f484465bee into main 2026-09-07 14:30:45 +02:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: kyuno/batchCooking#96
No description provided.