Le job integration-test échoue de façon intermittente (main et PR ouvertes) depuis le 2026-10-17 :
httpcore.ReadTimeout: timed out # sur PUT /collections/... ou PUT /collections/.../index?wait=true
httpx.ReadTimeout: timed out
qdrant_client.http.exceptions.ResponseHandlingException: timed out
Pendant ce temps GET /collections et /healthz répondent normalement, et tests/test_integration.py n'exerce jamais le transport HTTP/SSE du serveur MCP (il importe get_qdrant_client, search_emails, read_email) — ce n'est donc pas une régression d'une PR, c'est le job qui est fragile.
Preuve : au même moment, la branche main (e883a791, run 12253, job integration-test 62451) échoue à l'identique sur PUT /collections/test_mcp_maildir/index?wait=true, alors que le même job passait à 00:08 (run 12097) et à 21:05 (run 12251). Le probe docker du runner lui-même a même rendu context deadline exceeded dans ce job.
Cause
Le client Qdrant a un timeout par défaut de 5 s (QdrantClient(timeout=None) → 5 s REST/gRPC).
Le job n'attend que /healthz, qui est servi avant que le moteur de stockage accepte les écritures. Sur un runner qui fait plusieurs builds en parallèle, la première création de collection / indexation dépasse 5 s → la fixture module-scope part en ERROR et les 6 tests échouent.
Correctif
Pin de l'imageqdrant/qdrant:v1.19 (même digest que latest, identique à ce que tourne le cluster) dans docker-build.yaml et main.yaml, + docker-compose.yml ; renovate.json suit maintenant le pin dans les workflows aussi.
Attente d'une écriture réelle : la gate crée puis supprime une collection ci_readiness_probe?timeout=60 (60 tentatives), au lieu de se contenter de /healthz ; en cas d'échec les logs du conteneur Qdrant sont dumpés.
QDRANT_TIMEOUT (secondes) honoré par get_qdrant_client(), défaut inchangé (5 s) si non défini ; les jobs d'intégration le passent à 60, ils n'ont pas de SLA de latence.
Étape de diagnostic (docker ps -a + docker logs --tail 200) ajoutée avant le cleanup, en cas d'échec.
Vérifications
ruff check src/ tests/ --select F821 → All checks passed.
pytest tests/ -q → 45 passed, 6 deselected.
pytest tests/ -q -m integration (contre un vrai Qdrant) → 6 passed in 10.37s.
Séquence de readiness testée en réel : PUT /collections/ci_readiness_probe?timeout=60 → 200, DELETE → 200, collections inchangées.
Validation YAML de tous les workflows, JSON de renovate.json.
## Problème
Le job `integration-test` échoue de façon intermittente (main **et** PR ouvertes) depuis le 2026-10-17 :
```
httpcore.ReadTimeout: timed out # sur PUT /collections/... ou PUT /collections/.../index?wait=true
httpx.ReadTimeout: timed out
qdrant_client.http.exceptions.ResponseHandlingException: timed out
```
Pendant ce temps `GET /collections` et `/healthz` répondent normalement, et `tests/test_integration.py` n'exerce **jamais** le transport HTTP/SSE du serveur MCP (il importe `get_qdrant_client`, `search_emails`, `read_email`) — ce n'est donc pas une régression d'une PR, c'est le job qui est fragile.
Preuve : au même moment, la branche `main` (`e883a791`, run 12253, job integration-test 62451) échoue à l'identique sur `PUT /collections/test_mcp_maildir/index?wait=true`, alors que le même job passait à 00:08 (run 12097) et à 21:05 (run 12251). Le probe docker du runner lui-même a même rendu `context deadline exceeded` dans ce job.
## Cause
1. Le client Qdrant a un **timeout par défaut de 5 s** (`QdrantClient(timeout=None)` → 5 s REST/gRPC).
2. Le job n'attend que `/healthz`, qui est servi **avant** que le moteur de stockage accepte les écritures. Sur un runner qui fait plusieurs builds en parallèle, la première création de collection / indexation dépasse 5 s → la fixture module-scope part en ERROR et les 6 tests échouent.
## Correctif
- **Pin de l'image** `qdrant/qdrant:v1.19` (même digest que `latest`, identique à ce que tourne le cluster) dans `docker-build.yaml` et `main.yaml`, + `docker-compose.yml` ; `renovate.json` suit maintenant le pin dans les workflows aussi.
- **Attente d'une écriture réelle** : la gate crée puis supprime une collection `ci_readiness_probe?timeout=60` (60 tentatives), au lieu de se contenter de `/healthz` ; en cas d'échec les logs du conteneur Qdrant sont dumpés.
- **`QDRANT_TIMEOUT`** (secondes) honoré par `get_qdrant_client()`, défaut inchangé (5 s) si non défini ; les jobs d'intégration le passent à `60`, ils n'ont pas de SLA de latence.
- Étape de diagnostic (`docker ps -a` + `docker logs --tail 200`) ajoutée **avant** le cleanup, en cas d'échec.
## Vérifications
- `ruff check src/ tests/ --select F821` → All checks passed.
- `pytest tests/ -q` → 45 passed, 6 deselected.
- `pytest tests/ -q -m integration` (contre un vrai Qdrant) → **6 passed in 10.37s**.
- Séquence de readiness testée en réel : `PUT /collections/ci_readiness_probe?timeout=60` → 200, `DELETE` → 200, collections inchangées.
- Validation YAML de tous les workflows, JSON de `renovate.json`.
The integration test job has been failing intermittently on main and on
open pull requests with `httpx.ReadTimeout: timed out` on the first write
to Qdrant, while `GET /collections` and `/healthz` kept answering.
Cause: the qdrant-client default request timeout is 5 seconds, and the job
only waits for `/healthz`, which is served before the storage engine
accepts writes. On a shared runner doing several builds at once, the first
collection creation or indexing exceeds 5s and the whole fixture errors
out.
- pin qdrant/qdrant to v1.19 (the digest `latest` and the cluster already
run) and let Renovate track the pin in the workflows as well
- wait for a real write round-trip (create + delete a probe collection)
instead of `/healthz` alone, and dump the container logs if it never
becomes writable
- make the Qdrant client timeout configurable via QDRANT_TIMEOUT and set
it to 60s in CI, where there is no latency SLA
opencodecabilloteu
requested review from jcabillot 2026-10-17 17:24:39 -04:00
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Problème
Le job
integration-testéchoue de façon intermittente (main et PR ouvertes) depuis le 2026-10-17 :Pendant ce temps
GET /collectionset/healthzrépondent normalement, ettests/test_integration.pyn'exerce jamais le transport HTTP/SSE du serveur MCP (il importeget_qdrant_client,search_emails,read_email) — ce n'est donc pas une régression d'une PR, c'est le job qui est fragile.Preuve : au même moment, la branche
main(e883a791, run 12253, job integration-test 62451) échoue à l'identique surPUT /collections/test_mcp_maildir/index?wait=true, alors que le même job passait à 00:08 (run 12097) et à 21:05 (run 12251). Le probe docker du runner lui-même a même renducontext deadline exceededdans ce job.Cause
QdrantClient(timeout=None)→ 5 s REST/gRPC)./healthz, qui est servi avant que le moteur de stockage accepte les écritures. Sur un runner qui fait plusieurs builds en parallèle, la première création de collection / indexation dépasse 5 s → la fixture module-scope part en ERROR et les 6 tests échouent.Correctif
qdrant/qdrant:v1.19(même digest quelatest, identique à ce que tourne le cluster) dansdocker-build.yamletmain.yaml, +docker-compose.yml;renovate.jsonsuit maintenant le pin dans les workflows aussi.ci_readiness_probe?timeout=60(60 tentatives), au lieu de se contenter de/healthz; en cas d'échec les logs du conteneur Qdrant sont dumpés.QDRANT_TIMEOUT(secondes) honoré parget_qdrant_client(), défaut inchangé (5 s) si non défini ; les jobs d'intégration le passent à60, ils n'ont pas de SLA de latence.docker ps -a+docker logs --tail 200) ajoutée avant le cleanup, en cas d'échec.Vérifications
ruff check src/ tests/ --select F821→ All checks passed.pytest tests/ -q→ 45 passed, 6 deselected.pytest tests/ -q -m integration(contre un vrai Qdrant) → 6 passed in 10.37s.PUT /collections/ci_readiness_probe?timeout=60→ 200,DELETE→ 200, collections inchangées.renovate.json.