Purge du contenu des classes virtuelles
Version métier : voir Purge du contenu des classes virtuelles.
Description
Section intitulée « Description »Une classe virtuelle accumule du contenu avant même d’avoir commencé : messages postés dans le chat de la session, fichiers partagés par le formateur, blobs du test de bande passante, enregistrements d’une éventuelle répétition en salle de test. Rien ne nettoyait cela jusqu’ici — le serveur WebSocket du LMS ne vide que son cache mémoire, les objets MinIO et les RoomMessage restaient.
La commande balaie chaque nuit les classes virtuelles du jour et publie, pour chacune, un message Messenger différé qui se déclenche exactement 2 h avant lmsStartDate. À la consommation, le message supprime les messages du chat et tous les fichiers de la classe.
Le repère des 2 h n’est pas arbitraire : c’est l’instant précis où la vraie salle LiveKit s’ouvre et où la salle de répétition se ferme (VirtualClassRoomResolver::resolve(), realOpensAt = lmsStartDate -2h). La classe démarre donc toujours sur une salle propre.
Déclenchement
Section intitulée « Déclenchement »Planification GitLab :
- Description :
Purge virtual class content - ID du schedule : à renseigner après création
- Cron :
0 1 * * *(fuseau :Europe/Paris) — 01:00 Paris chaque jour. - Branche :
refs/heads/master - État : à créer
- Variable injectée :
SCHEDULE_TYPE=purge_virtual_class_content
TODO(review) : créer le schedule dans GitLab > Build > Pipeline schedules, puis reporter son ID ici et dans le tableau de la page pipeline.
Job CI : purge_virtual_class_content dans ci-jobs/.scheduled-ci.yml (stage purge_virtual_class_content). Le job se connecte en SSH à $DATABASE_SERVER_URL et exécute la commande dans le conteneur applicatif $APP_NAME.
Commande exécutée :
docker exec $APP_NAME php bin/console scheduled:purge-virtual-class-contentOption --date=Y-m-d disponible pour rejouer manuellement un jour donné (défaut : aujourd’hui).
Fonctionnement
Section intitulée « Fonctionnement »VirtualClassRepository::findHostingByDay()récupère les classes virtuelles dontsessionDate.lmsStartDatetombe dans la journée demandée, fenêtre calculée enEurope/Parispuis convertie en UTC viagetDateRange().- Filtre
vc.hosting = true: les sous-CVS partagent la salle et les préfixes de fichiers de leur CVS principale, les purger reviendrait à supprimer deux fois le même contenu. - Filtre
sd.internalId != 100: les sessions d’administration sont exclues, comme partout ailleurs dans ce repository.
- Filtre
- Aucune classe ce jour-là →
warninget sortie enSUCCESS. - Pour chaque classe :
purgeAt = lmsStartDate - 2 heures, puisdelay = max(0, (purgeAt - maintenant) * 1000)millisecondes. Lemax(0, …)couvre les classes démarrant avant 03:00, dont le-2 hest déjà passé quand le cron tourne à 01:00 : le message est alors consommé immédiatement. - Publication d’un
PurgeVirtualClassContentMessage(virtualClassId)surMessageBusInterfaceavec unDelayStamp, comme le faitReminderService::executeVirtualClassReminder(). Le délai reste borné à moins de 24 h, aucun message ne campe des jours dans RabbitMQ. PurgeVirtualClassContentMessageHandlerconsomme le message :- classe virtuelle introuvable (supprimée entre la planification et la consommation) → sortie silencieuse, sans erreur ;
- suppression des objets MinIO d’abord —
virtual-class/{id}/ettest/uploads/{id}/surerevo-cdn,virtual-class/{id}/surerevo-lms(qui contient les enregistrements). Un échec en cours de route ne doit pas laisser des fichiers orphelins dont les messages porteurs d’URL auraient déjà disparu ; - suppression des
ReportMessagede la salle avant lesRoomMessage:ReportMessage.messageReportedest une clé étrangère non nullable sansonDelete, une suppression naïve part en violation de contrainte. LereplyToauto-référent deRoomMessageest enonDelete: SET NULLcôté base, il ne demande aucun passage supplémentaire ; - la salle (
Room) elle-même est conservée, seul son contenu est vidé.
Les deux DELETE DQL lient l’identifiant de salle avec le type UuidType explicite. Sans ce type, Doctrine envoie l’UUID en chaîne, la colonne binaire ne correspond à rien et la suppression ne fait rien — silencieusement.
Entités impactées
Section intitulée « Entités impactées »- VirtualClass — lecture (classes du jour, statut
hosting). - SessionDate — lecture (
lmsStartDate,internalId), chemin vers la salle. - Room — lecture, conservée.
- RoomMessage — suppression de tous les messages de la salle.
- ReportMessage — suppression des signalements portant sur ces messages.
Gestion des erreurs
Section intitulée « Gestion des erreurs »- Le handler capture toute
Throwable, la remonte dans Sentry viaAppHelper::sentryCatchExceptionWithContext()avec le contextepurgeVirtualClassContent(virtualClassId), puis la relance. Le transport retente 3 fois (retry_strategy.max_retries: 3) et gare le message dans le transportfailed(doctrine://default?queue_name=failed) en cas d’échec persistant. Une classe non purgée reste donc visible, elle n’échoue pas en silence. - Côté commande, aucune exception dédiée : une erreur de dispatch remonte telle quelle, le job GitLab échoue et la journée sera rattrapée manuellement avec
--date.
Dépendances externes
Section intitulée « Dépendances externes »- RabbitMQ — transport
purge-virtual-class-content(exchange fanoutpurge_virtual_class_content), consommé par le worker supervisorpurge-virtual-class-content. - MinIO — bucket
erevo-cdn(fichiers partagés et blobs de test) et bucketerevo-lms(enregistrements LiveKit), exposés via les storages Flysystemcdn.storageetlms.storage. - MariaDB — suppression des
RoomMessageetReportMessage.
Limite connue
Section intitulée « Limite connue »Les collections MongoDB du LMS ne sont pas nettoyées : virtual-class-state.sharedFiles et virtual-class-recordings conservent leurs entrées alors que les objets MinIO correspondants ont disparu. Le LMS peut donc encore afficher des fichiers ou des enregistrements dont le lien est mort. internal-api n’a pas d’accès Mongo et le périmètre retenu exclut toute modification du dépôt LMS.
TODO(review) : prévoir côté LMS le nettoyage de
virtual-class-state.sharedFilesetvirtual-class-recordings, ou exposer un endpoint que ce handler appellerait.
Observabilité
Section intitulée « Observabilité »- Logs :
GitLab > Build > Pipelines > purge_virtual_class_content(sortie SSH : nombre de classes, heure de purge et délai par classe). - Logs du worker :
docker logs $APP_NAME→ programme supervisorpurge-virtual-class-content. - Profondeur de file surveillée par liip_monitor (
messenger.transport.purge-virtual-class-content, seuils 10 / 20) et visible sur/healthcheck. - Alerting : Sentry capture les exceptions du handler, contexte
purgeVirtualClassContent.
Références code
Section intitulée « Références code »- Commande :
src/Command/Scheduled/PurgeVirtualClassContentCommand.php - Job CI :
ci-jobs/.scheduled-ci.yml(jobpurge_virtual_class_content), stage déclaré dans.gitlab-ci.yml - Message / handler :
App\Message\PurgeVirtualClassContentMessage,App\MessageHandler\PurgeVirtualClassContentMessageHandler - Repository :
App\Repository\VirtualClassRepository::findHostingByDay() - Configuration :
config/packages/messenger.yaml,config/packages/flysystem.yaml,config/packages/liip_monitor.yaml,docker/supervisor/messenger.conf,docker/supervisor/messenger-local.conf - Tests :
tests/Command/CommandsTests.php