ADR-0004 : Orchestration des sagas avec Wolverine
| Statut | Accepté |
| Date | 2025-11 |
| Décideurs | Équipe de développement Edukit |
| Périmètre | Edukit-VM |
Contexte
Le provisionnement d’une VM Proxmox est une opération longue (environ 3 minutes) et composée d’étapes synchrones qui émettent chacune un état : Pending → Creating → ConfiguringCloudInit → Starting → Running. Chaque étape peut échouer (Proxmox indisponible, clonage refusé, configuration cloud-init en erreur) et certaines doivent être compensées. À l’échelle d’une séance, une classe de trente étudiants représente trente processus de ce type en parallèle, soit potentiellement plusieurs centaines de transitions à propager en quelques minutes.
Il faut donc un mécanisme qui :
- maintienne l’état d’avancement de chaque provisionnement de façon durable (survivant à un redémarrage du processus) ;
- gère les délais d’expiration (une saga ne doit pas rester bloquée indéfiniment) ;
- garantisse la livraison des messages sortants (par exemple
VmCredentialIssuedconsommé par OrgService pour envoyer le courriel à l’étudiant) ; - protège contre le traitement concurrent de la même saga quand plusieurs processus de travail seront actifs.
Décision
Orchestrer le cycle de vie des VM (provisionnement, démarrage, arrêt, destruction) avec des sagas Wolverine, persistées par Marten (voir ADR-0005).
- Chaque saga (
VmProvisioningSaga, et les sagas de cycle de vie start / stop / destroy) est un point d’entrée unique pour sa commande. Wolverine prend en charge la persistance de l’état, la transactionnalité boîte de réception / boîte d’envoi, la planification des délais d’expiration et la publication des messages sortants. - La saga porte son identité sur l’identifiant de la VM, et implémente
IRevisionedavec un champVersion: Marten applique alors une concurrence optimiste. Deux processus qui tenteraient de faire avancer la même saga lèventConcurrencyExceptionau lieu de s’écraser silencieusement. - Les exceptions de concurrence sont relancées avec des temporisations configurables (
Wolverine:ConcurrencyRetryCooldownsMs, par défaut 100 / 250 / 500 ms). - La saga compose avec le sémaphore indexé
ProxmoxThrottlerqui limite la concurrence des appels Proxmox, et avec une boucle de réconciliation périodique (VmStatusPollingService) qui rattrape les divergences survenues hors flux.
Conséquences
Bénéfices
- État de provisionnement durable et observable : un redémarrage de processus ne perd pas l’avancement.
- Livraison au moins une fois des messages sortants grâce à la boîte d’envoi transactionnelle (voir ADR-0005) ; en contrepartie, les gestionnaires doivent être idempotents.
- Intégration native .NET 9 : Wolverine est idiomatique, la découverte des gestionnaires est explicite (opt-in), et il s’intègre nativement avec Marten pour la saga et la boîte d’envoi (un seul produit pour les deux préoccupations).
- Prêt pour la mise à l’échelle : le passage de un à plusieurs processus de travail se fait par configuration (partitionnement RabbitMQ par empreinte d’identifiant de saga), sans réécriture, la concurrence optimiste servant de filet de sécurité.
Coûts et limites
- Les gestionnaires doivent être idempotents (conséquence de la livraison au moins une fois). C’est une contrainte de conception à respecter sur chaque consommateur.
- Wolverine est une dépendance structurante du service de provisionnement. Le risque est borné : le tableau d’argumentation des choix techniques conserve les alternatives, et le métier reste dans le domaine, indépendant du bus (voir ADR-0001).
Alternatives écartées
- Hangfire (planification de tâches en arrière-plan) : testé en prototype. Adapté à des travaux planifiés, mais n’offre pas le modèle de saga avec état, boîte d’envoi transactionnelle et concurrence optimiste recherché. Écarté.
- MassTransit : déjà connu de l’équipe et très complet, mais l’intégration native Marten (saga plus boîte d’envoi dans le même produit, sur la base PostgreSQL déjà présente) a fait préférer Wolverine, plus léger à câbler pour ce besoin.
- NServiceBus : mature, mais modèle de licence commerciale et intégration moins directe avec la pile retenue.
- Orchestration « maison » (service en arrière-plan plus table d’état) : faisable, mais réimplémenterait la durabilité, les délais d’expiration, la boîte d’envoi et la concurrence optimiste. Coût et risque de défaut supérieurs à l’adoption d’un produit éprouvé.