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>
60 lines
3.3 KiB
Markdown
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).
|