Paillasse/docs/adr/0003-stock-chaud-et-horizon.md
Adam Belkacemi 80b6c0722d ajoute le stock chaud, la correction automatique et les nouveaux essais de création de stack
Stock de stacks prêtes par lab dans Redis (prise atomique en Lua, ménage
par comparaison avec Heat, disjoncteur), files gérées par Horizon, séances
planifiables. Correcteur Go sur l'hyperviseur : socket Unix, commandes lues
dans le lab.yaml uniquement, exécution par l'agent invité QEMU. Une création
de stack ratée est supprimée et réessayée deux fois, puis la session passe
en échec, visible du prof.

Corrige aussi trois défauts des étapes précédentes : la console se coupait
à la première frappe d'un navigateur (trames texte), l'élève ne pouvait pas
rejoindre une séance, la déconnexion n'avait pas de jeton CSRF.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 21:52:16 +02:00

60 lines
3.3 KiB
Markdown

# ADR 0003 — Stock chaud dans Redis, Heat pour le ménage, files gérées par Horizon
- **Date** : 2026-10-08
- **Statut** : accepté
## Contexte
Étape 11 : un lab pris dans le stock doit s'ouvrir en moins de 5 s, alors
que Heat met 20 à 40 s à construire une stack. Il faut donc des stacks
construites d'avance, données à un élève au moment où le prof l'admet, sans
jamais donner la même stack à deux élèves, ni perdre une stack (une VM qui
tourne pour rien coûte de la RAM, rare sur ce serveur de 32 Go).
## Décisions
1. **Une liste Redis par lab** (`stock:pretes:{lab}`) contient les stacks
prêtes. La prise est un script Lua (`LPOP` + `ZADD stock:attribuees`)
exécuté d'un bloc par Redis : une stack est toujours soit dans la liste,
soit marquée « en cours d'attribution », jamais nulle part.
2. **Redis fait foi pour ce qui est prêt ; Heat sert au ménage.** Chaque
minute, `labs:remplir-stock` liste les stacks `stock-…` dans Heat et
supprime celles que plus rien ne référence (ni la liste, ni une
attribution, ni une session en base, ni une construction en cours), après
10 min de marge. Une stack orpheline n'est jamais « récupérée » dans le
stock : la supprimer est plus sûr que risquer de la donner deux fois.
3. **Cible du stock** = taille par défaut (1 stack par lab) + élèves des
séances qui commencent dans les 10 min ou déjà ouvertes et pas encore
servis, dans la limite de `capacite_vm` (15 VM, voir journal de l'étape 11).
4. **Fiabilité commune** (`FabriqueStack`) pour le stock et les sessions :
3 essais, chaque essai porte son nom (`-e1`, `-e2`, `-e3`) et supprime sa
stack ratée ; avant un essai, les restes des précédents sont supprimés.
Disjoncteur sur le stock : 3 échecs définitifs de suite sur un lab →
plus de commande pour lui pendant 30 min.
5. **Laravel Horizon** remplace le `queue:work` unique de l'étape 10. Deux
superviseurs à nombre fixe de processus : `sessions` (4) et `stock` (2).
Un élève n'attend jamais derrière le remplissage du stock, et plusieurs
stacks se construisent en parallèle (chaque job bloque un processus
pendant qu'il attend Heat). Horizon était déjà prévu dans l'architecture
du CLAUDE.md ; le besoin réel apparaît ici. Tableau de bord `/horizon`
fermé à tous en production.
## Alternatives écartées
- **Une table MariaDB pour le stock** : possible, mais la prise atomique
demande un verrou de ligne (`SELECT … FOR UPDATE SKIP LOCKED`) ; la liste
Redis + Lua est plus simple, et le prompt de déploiement la demande.
- **Récupérer les stacks orphelines au lieu de les supprimer** : économise
une construction, mais ouvre une fenêtre où la même stack pourrait être
dans la liste deux fois. Refusé.
- **Plusieurs unités systemd `queue:work`** : faisable, mais sans métriques
ni gestion propre de plusieurs files ; Horizon fait les deux.
## Conséquences
- Les VM du stock tournent en permanence (≈ 270 Mo réels chacune au repos).
- Si Redis est vidé, le stock est perdu mais pas les VM : le ménage les
supprime 10 min plus tard, et le stock se reconstruit.
- `retry_after` de la file Redis (330 s) doit rester supérieur au timeout
des superviseurs Horizon (300 s), lui-même supérieur au timeout des jobs
(280 s).