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