Labo 1 - Diffusion fiable et exclusion mutuelle

Informations Générales

Notez enfin que l’objectif étant pour vous d’apprendre, vous serez toujours légitimes et bienvenu.e.s à nous poser des questions : sur la théorie, sur vos idées, sur vos blocages. Si vous vous sentez perdu.e.s ou coincé.e.s, c’est qu’il faut nous demander.

Le fil rouge du semestre

D’ici la fin du semestre, vous aurez atteint (et vaincu) le boss final du monde distribué : le consensus. Une fois que le consensus est disponible, tout devient possible à faible cout algorithmique.

Nous y parviendrons en quatre étapes :

  1. Permettre une communication robuste entre processus, et l’exclusion mutuelle dans un monde sans panne. Le consensus est facile par dessus l’exclusion mutuelle, mais nous voulons tolérer les pannes. Ce n’est donc que le début.
  2. Permettre l’élection d’un leader dans un monde où les pannes sont possibles.
  3. Utiliser le leader pour synchroniser les processus.
  4. Tirer profit de la synchronisation pour atteindre le consensus.

Ce labo correspond à la première étape. Il se divise en deux parties : (A) Reliable Broadcast avec pannes, et (B) Exclusion Mutuelle sans pannes.


Les suppositions


Partie A — Diffusion fiable

Tout au long du semestre, il arrivera souvent de devoir diffuser (broadcast) un message, c’est à dire l’envoyer à tous les autres processus du système.

Or, comme nous l’avons vu en cours, envoyer simplement à tous ne suffit pas : si l’émetteur tombe en panne au milieu de ses envois, seule une partie des processus l’auront reçu. Difficile d’imaginer du consensus dans ce contexte.

Vous implémenterez donc une couche de diffusion fiable (reliable broadcast), qui devra garantir les propriétés suivantes :


Partie B — Exclusion mutuelle de Lamport

L’algorithme est celui du cours, et suppose qu’aucune panne n’arrivera jamais.

Votre implémentation doit donc garantir les propriétés suivantes :


L’interface avec l’outillage

Votre programme correspond au comportement du module d’un processus du système. Il lit sur stdin les événements qu’il doit traiter (e.g. réception d’un message, demande d’entrée en section critique), et écrit sur stdout les événements qu’il déclenche (e.g. envoyer un message, sortir de section critique).

Un outil, cuelight, est ensuite chargé de lancer votre programme pour chaque processus du système, et lire et écrire sur stdin et stdout pour simuler le réseau entre les processus, et déclencher les événements qu’ils ont à traiter.

Afin de vous permettre de vous concentrer sur l’algorithmique, nous vous fournissons des squelettes qui gèrent stdin et stdout pour vous, et cachent cette complexité derrière des interfaces clairement définies. Si vous choisissez de ne pas utiliser un de ces squelettes, le README de votre repo contient les informations pour créer le vôtre de zéro.

Si vous utilisez nos squelettes, vous n’aurez donc que les points suivants à implémenter dans ce labo.


Une contrainte technique

Ces labos sont conçus de manière à ce que chaque couche que vous implémentez soit utilisable dans le labo suivant.

Afin d’évaluer chaque labo, nous pourrons remplacer les couches implémentées aux labos précédents par nos propres implémentations instrumentées pour tester une suffisamment grande variété de scénarios. Deux conséquences en découlent :

  1. Votre nœud doit être une fonction pure des événements. Pas d’horloge système, pas de threads, pas d’aléatoire non initialisé. C’est ce qui rend une exécution rejouable, et c’est vérifié avant tout le reste : si votre code ne se rejoue pas à l’identique, rien d’autre n’est évalué.
  2. Aucune opération n’est autorisée à bloquer en attendant une réponse. Votre processus est un gestionnaire d’événements mono-thread, et le temps logique n’avance qu’une fois que l’événement est traité. Si une de vos fonctions bloque, alors le temps arrête d’avancer.

En particulier, cela implique que tout est unidirectionnel : un événement en entrée ne peut pas répondre, sauf en générant un événement en sortie. Concrètement : broadcast(payload) ne retourne rien, et la délivrance arrive par abonnement. Un autre exemple est request(on_enter) de l’exclusion mutuelle. request ne bloque pas en attendant la section critique : elle envoit ses messages et stocke on_enter pour pouvoir retourner sans attendre. Ce n’est que plus tard, lorsqu’un autre événement déclenchera son entrée en section critique, qu’elle appellera on_enter.

Évaluation

Nous vous fournissons le vérificateur lab1-check, que vous construisez vous-même et qui exécute tous les tests fournis avec le code. Ces tests sont des scénarios décrivant une séquence d’événements, de ralentissements, de pannes, etc. Ceux-ci sont de deux types :

Référez-vous au README de votre repo pour plus de détails sur la construction et l’exécution de lab1-check.

Votre rendu sera évalué sur ces tests, ainsi que sur un quiz individuel juste après le rendu. L’objectif sera de vérifier que vous possédez les choix architecturaux, logiques et algorithmiques de votre solution, et que vous avez compris leurs enjeux.