33800 Docs

← Retour

Audit PASS 3 - AI Orchestrator - Integration & Contrats Inter-Services

Date : 15/02/2026 04:00 Status : IMPLEMENTEE Auditeur : Claude Opus 4.6 (Pass 3 independant) Fichier principal : /stock_8to/33800-stack/projects/ai-orchestrator/app/main.py (4675 lignes) Fichiers secondaires : Dockerfile, requirements.txt, docker-compose.yml, conf.prod.gouroubleu.yml, .env.deploy, .gitlab-ci.yml


Resume executif

Severite Nombre
BUG CERTAIN 6
BUG PROBABLE 11
RISQUE THEORIQUE 12
TOTAL 29

Les problemes les plus graves concernent :


Findings

F-01 : Race condition job_id entre Redis pop et DB update (queue/cancel)

MAIS: si le timing est inverse (worker pop + read = pending, PUIS cancel):

cancel fait queue_remove_job() -> job deja poppe, pas trouve -> retourne False

cancel fait db_cancel_job() -> UPDATE WHERE status='pending' -> 0 rows (worker a deja mis "loading_model")

-> cancel retourne {"status": "failed"} alors que le job tourne

- **Impact** : Un job "cancel" peut continuer a s'executer. L'API retourne "failed" pour le cancel alors que le job a deja commence, sans indication a l'utilisateur.
- **Fix suggere** : Dans `process_single_job`, re-verifier le status DB juste avant de passer a "loading_model/generating". Ajouter un mecanisme de cancellation token (Redis key `ai:cancel:{job_id}`) que le worker verifie periodiquement.

---

### F-02 : Jobs requeue cree des doublons en Redis si le worker crash entre pop et requeue
- **Severite** : BUG CERTAIN
- **Fichier** : `app/main.py:3952-3954` et `app/main.py:3978-3980` et `app/main.py:3991-3994`
- **Description** : Le pattern requeue fait `db_update_job_status(job_id, "pending")` puis `queue_push_job(job_id, ...)`. Si le worker crash (OOM, container restart) apres le db_update mais avant le queue_push, le job reste "pending" en DB mais absent de Redis. Inversement, si le crash arrive apres le push, le job est "pending" en DB et present en Redis. L'endpoint `/api/queue/sync` resout le cas "absent de Redis" mais ne deduplique pas les entrees Redis existantes avant de pousser les jobs.
- **Code concerne** :
```python
# Requeue pattern (3 occurrences):
await db_update_job_status(job_id, "pending")     # Etape 1: DB
await queue_push_job(job_id, job_data["priority"]) # Etape 2: Redis
# Si crash entre 1 et 2 -> job perdu dans les limbes

F-03 : Credentials en dur dans le code source


F-04 : Schema SQL absent du repo - colonnes supposees sans verification


F-05 : Absence de transaction entre creation job DB et push Redis queue


F-06 : health endpoint crash si Redis est down


F-07 : Cloud jobs (Anthropic) dispatches sans aucun tracking de slots - parallelisme illimite


F-08 : Budget Anthropic verifie apres dequeue mais sans lock atomique


F-09 : run_ssh_command ne kill pas le process en cas de timeout


F-10 : check_tool_health bare except avale toutes les exceptions


F-11 : start_tool escape des backslashes casse les chemins Windows


F-12 : stop_tool ne gere pas les outils cloud (gpu_id=-1)


F-13 : refresh_tools_status inclut anthropic dans les health checks SSH


F-14 : Watchdog GPU parse des strings avec "N/A" -> ValueError


F-15 : Anthropic Batch poll utilise un httpx.AsyncClient qui expire apres 30s


F-16 : CORS "allow_origins=*" avec "allow_credentials=True" est invalide


F-17 : Variable is_cloud re-declaree dans le meme scope, masque la valeur initiale


F-18 : active_tools est un dict mutable partage entre coroutines sans lock


F-19 : _gpu_cache n'est pas thread-safe et pas async-safe entre worker et API


F-20 : queue_sync efface les queues Redis SANS verifier si le worker est en train de traiter


F-21 : docker-compose.yml ne declare pas les connexions Redis et PostgreSQL


F-22 : conf.prod.gouroubleu.yml definit ANTHROPIC_API_KEY vide


F-23 : Pas de limite de taille pour le file upload


F-24 : db_update_job_status construit du SQL dynamique sans parameterisation des noms de colonnes


F-25 : execute_anthropic_batch_job ne verifie pas le type de reponse "succeeded" vs "errored"


F-26 : sadtalker cleanup async fire-and-forget sans await


F-27 : Le Dockerfile utilise python:3.12-slim mais requirements.txt n'a pas de version pinned pour redis et asyncpg


F-28 : set_parallel_jobs clamp "between 1 and 5" mais le code clamp a 10


F-29 : L'endpoint costs_summary a des parametres par defaut hardcodes (year=2026, month=2)


Synthese des chemins d'erreur

Que se passe-t-il si Ollama crash ?

Que se passe-t-il si la DB PostgreSQL est down ?

Que se passe-t-il si Redis est plein/down ?

Que se passe-t-il si le reseau SSH vers win11 tombe ?


Recommandations prioritaires

  1. Ajouter un fichier schema.sql avec la definition de ai_jobs et une verification au demarrage
  2. Retirer les credentials hardcodes et les deplacer dans des env vars obligatoires
  3. Ajouter queue_sync au demarrage dans la fonction lifespan
  4. Limiter les jobs cloud concurrents avec un asyncio.Semaphore
  5. Proteger le healthcheck contre les pannes Redis
  6. Tuer les process SSH en timeout dans run_ssh_command
  7. Filtrer les CLOUD_TOOLS dans stop_tool, refresh_tools_status, et stop-all