Deux clients peuvent lire « A1 est libre » avant que l’un d’eux ait modifié l’état ; si la lecture et la modification sont deux opérations séparées, la place est vendue deux fois. Nous allons réunir la vérification et la réservation dans le même callback GenServer, c-à-d. dans la fonction qui traite un seul message, puis apprendre à reconnaître une expiration devenue périmée. Une réservation retient une place pour un temps limité ; si personne ne la confirme avant la fin du délai, elle expire et la place redevient libre. Le sujet prolonge directement les processus à état d’AP6–AP7.

Téléchargez le squelette reservations-concurrentes. Le README guide le travail complet. Le registre, le superviseur dynamique, l’API, la commande de démonstration et les tests sont fournis ; aucun frontend, aucun paiement et aucune base de données ne sont demandés. Le projet n’a aucune dépendance à télécharger.

Deux clients demandent la même place ; le serveur accepte un seul jeton et refuse l'autre demande.

Sur le schéma, les deux demandes arrivent dans la boîte aux lettres du serveur de séance, qui les traite l’une après l’autre : la première reçoit un jeton, la seconde un refus. Un jeton identifie une réservation particulière, pas une personne ; il servira à confirmer, puis à reconnaître une expiration.

Démarrer avec les places libres

mix compile
mix test --only provided
iex -S mix

Dans IEx, Reservations.open("cinema", ["A1", "A2"]) crée une séance et Reservations.snapshot("cinema") consulte son état : %{"A1" => :free, "A2" => :free}. L’infrastructure démarre donc avant que vous écriviez les réservations. Le test fourni passe ; les sept autres indiquent les étapes à compléter.

Faire évoluer une place sans processus

Écrivez d’abord trois transitions pures dans le module Seats : réserver, confirmer et expirer. Une place est libre, retenue par un jeton ou confirmée avec ce jeton. Une confirmation répétée avec le bon jeton conserve le même résultat : on dit qu’elle est idempotente.

mix test --only transitions

Après expiration du jeton T1, la place peut être retenue par T2. Que ferait une vieille expiration de T1 qui arrive ensuite ? Si l’expiration ne portait que le nom de la place, elle effacerait T2 ! Le jeton permet de reconnaître à quelle réservation le message appartient. Les tests font observer ce scénario.

Traiter une demande atomiquement

Complétez les trois callbacks du GenServer Session. Chacun décide à partir de l’état courant du serveur et renvoie le nouvel état dans le même traitement : pas de lecture client suivie d’une écriture séparée. C’est cela, une transition atomique : aucun autre message ne peut s’intercaler entre la décision et la mise à jour. Une réservation réussie programme un message {:expire, seat, token} avec Process.send_after/3 ; handle_info/2 en vérifie encore le jeton à l’arrivée.

mix test --only server
mix test
mix reservations.demo

La démonstration lance vingt demandes pour une place, puis confirme l’unique réservation acceptée. Les tests font partir cent clients derrière une barrière de messages. Le gagnant peut changer, l’exclusivité doit rester vraie.

Chaque séance possède son propre processus, retrouvé par Registry, l’annuaire qui associe un nom comme "cinema" à un PID, et démarré sous DynamicSupervisor, un superviseur auquel on ajoute des enfants après le lancement. Comparez deux séances, puis provoquez une panne comme indiqué dans le README : le processus redémarre, mais ses réservations volatiles sont perdues. Il ne s’agit donc pas d’une billetterie prête pour la production. Le délai du minuteur est un délai d’envoi ; une boîte aux lettres occupée peut retarder le traitement de l’expiration.

Réserver plusieurs places dans une seule séance, annuler, rendre les demandes idempotentes ou notifier des abonnés constituent des prolongements. Le README explique leurs contrats et les difficultés qui restent après un redémarrage.

Ce projet noté commence pendant les trois heures d’AP9 et se poursuit à la maison. Le premier scénario est construit ensemble ; périmètre, barème et échéance seront précisés avant janvier, et les étapes ne sont pas un barème. Conservez le code, vos tests et COMPTE-RENDU.md, avec trois traces : deux clients concurrents, une expiration périmée et une observation de redémarrage. Expliquez où se trouve la décision atomique, ce que garantit le jeton et ce qui reste perdu après une panne. Pour toute question, vous pouvez me contacter par email.

Pour les curieux : le fascicule

Un petit fascicule d’approfondissement accompagne ce sujet : les idées derrière le projet, leur histoire, ce qu’elles deviennent dans les systèmes réels et quoi lire ensuite. C’est un cadeau de fin de cours ; rien n’y est à rendre.

Ouvrir le fascicule (PDF)