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

3.3 KiB

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).