AlgoDist
Master 2 · Polytech UCAChapitre 2F. Baude

Le temps,
les coupures,
les snapshots.

Sans horloge commune, comment savoir quel événement s'est passé avant quel autre ? Et comment prendre une photo cohérente d'un système où personne ne voit tout ? On construit le temps logique, pas à pas.

Perdu avec les horloges de Lamport et les vecteurs ? C'est le chapitre le plus abstrait. Ici chaque diagramme espace-temps s'anime : tu vois l'horloge se mettre à jour à chaque message. Objectif : réussir le QCM et le TD.
événement message date logique
En 30 secondes

Dans un système asynchrone, aucune horloge commune. On ne peut pas dater les événements à l'heure réelle, mais on peut capturer leur ordre causal : la relation happened-before (e1 → e2). Les horloges de Lamport (un entier) respectent cet ordre mais ne savent pas repérer les événements concurrents ; les horloges vectorielles (un entier par processus) le font exactement, au prix de la taille. Pour photographier l'état global sans figer tout le monde, une coupe cohérente respecte la causalité, et l'algorithme de Chandy-Lamport (canaux FIFO) la calcule avec un marqueur qui se propage.

01 · Le problème

Pourquoi le temps est un piège ?

On veut dater les événements d'un système distribué : pour tracer, déboguer, rejouer une exécution, ou horodater une transaction bancaire qui touche plusieurs sites.

L'idée clé

Rejouer une exécution, ce n'est pas reproduire les mêmes instants réels : c'est reproduire le même ordre d'envoi, de réception et de traitement des messages. On veut le même graphe temporel, pas la même montre.

Le vrai obstacle : il n'existe aucun observateur global, aucun « dieu » qui verrait tous les événements à la fois. Chaque processus ne voit qu'une partie de l'histoire et son ordre local. On ne peut pas en déduire un ordre global fiable. Il va falloir fabriquer une notion de temps qui marche quand même.

02 · Horloges physiques

Synchroniser des montres qui dérivent

Une horloge physique unique serait parfaite. Mais elle n'existe pas : chaque ordinateur a la sienne, elles dérivent, et les lire à distance ajoute une incertitude (délai de propagation non nul).

Sources

D'où vient l'heure ?

Temps atomique international (1s = 9 192 631 770 transitions du Césium 133), diffusé en UTC par radio, et embarqué dans les satellites GPS. Les ordinateurs se recalent dessus avec NTP (serveurs hiérarchisés, niveau 1 au plus près des sources).

Externe

Cristian

Synchro sur une source externe, en asynchrone (délai fini mais inconnu). P demande l'heure à S, mesure l'aller-retour, pose son horloge à t + round-trip/2. Précision ±(round-trip/2 − min).

Interne

Berkeley

Pas de référence absolue : on veut juste que les horloges soient d'accord entre elles. Un maître les collecte, écarte les valeurs trop divergentes (skew δ), fait une moyenne tolérante aux fautes, et renvoie à chacun l'ajustement + ou −.

Le vrai enjeu

Même bien synchronisées, les horloges physiques restent chères et imparfaites. Or, souvent, on n'a pas besoin de l'heure réelle : il suffit de connaître l'ordre des événements. C'est là qu'interviennent les horloges logiques, bien moins coûteuses.

03 · Causalité

La relation « happened-before » (→)

C'est le cœur du chapitre. On définit un ordre partiel sur les événements, sans aucune horloge, uniquement à partir de la structure des messages.

Les trois règles

  • Ordre local : si e1 et e2 sont sur le même processus et e1 avant e2, alors e1 → e2.
  • Message : si e1 est l'envoi d'un message et e2 sa réception, alors e1 → e2.
  • Transitivité : si e1 → e2 et e2 → e3, alors e1 → e3.

Concurrence (||)

Deux événements que rien ne relie (ni e1 → e2 ni e2 → e1) sont concurrents, notés e1 || e2. Attention : « concurrent » ne veut pas dire « en même temps », mais « causalement incomparable ». Et || n'est pas transitive.

À retenir

La causalité est un ordre partiel : certains couples d'événements ne sont tout simplement pas comparables. Tout l'enjeu des horloges logiques est de coder cet ordre partiel avec des nombres.

04 · Horloge de Lamport

Un seul entier par événement

L'idée la moins chère : chaque processus tient un compteur L. On veut que si e1 → e2, alors L(e1) < L(e2).

Les règles de « tic »

// L initialisé à 0 sur chaque Pi
Événement local : L := L + 1
Envoi de m      : L := L + 1 ; estampille m avec L
Réception de m (ts = l) :
     L := max(L, l) ;   // on se recale sur le plus avancé
     L := L + 1        // puis on date la réception
Le piège à comprendre

Lamport est correct dans un seul sens : e1 → e2 implique L(e1) < L(e2). Mais la réciproque est fausse : L(a) < L(b) ne dit rien sur la causalité. Deux événements concurrents peuvent recevoir n'importe quel ordre d'entiers.

Deux événements sur des sites différents peuvent avoir la même valeur L. Pour un ordre total (parfois nécessaire, ex. départager deux requêtes), on casse l'égalité avec l'identifiant du site : L=8 sur A < L=8 sur B si A « plus petit » que B.

0 / 0
Prêt
Trois processus P1, P2, P3. Avance pas à pas : chaque événement fixe sa date entière, chaque message porte son estampille.
événement message estampillé valeur de L

05 · Horloge vectorielle

Un entier par processus [Fidge / Mattern]

Pour capturer exactement la causalité (concurrence comprise), on remplace l'entier par un vecteur de taille N : une case par processus.

Les règles

// V[j] = 0 pour tout j, sur chaque Pi
Avant chaque événement : V[i] := V[i] + 1
Envoi de m            : estampille m avec V
Réception (vecteur t) :
     pour tout j : V[j] := max(V[j], t[j])   // merge

V[i] = nombre d'événements que Pi a estampillés. V[j] = nombre d'événements de Pj dont Pi a pu subir l'influence.

Comparer deux vecteurs

  • V1 ≤ V2 ssi V1[j] ≤ V2[j] pour tout j.
  • V1 < V2 ssi V1 ≤ V2 et V1 ≠ V2.
Le sur-pouvoir

V(e1) < V(e2) équivaut à e1 → e2. Et si les vecteurs sont incomparables (aucune inégalité dans les deux sens), alors e1 || e2 : concurrents. Les vecteurs détectent la concurrence, ce que Lamport ne sait pas faire.

Le prix

Une estampille pèse N entiers. Sur un grand système, ce n'est pas scalable.

0 / 0
Prêt
Même diagramme, mais avec des vecteurs [P1,P2,P3]. Regarde la fusion (max) à chaque réception.
événement message (porte le vecteur) [P1,P2,P3]

06 · Coupures et cohérence

Une photo qui a du sens

Une coupe (cut) sépare l'histoire en un passé (à gauche) et un futur (à droite). Sa frontière = le dernier événement retenu sur chaque processus. Mais toutes les coupes ne se valent pas.

Définition

Cohérence = respect de la causalité. Une coupe est cohérente si, pour tout événement du passé, tous ses ancêtres causaux sont aussi dans le passé. Interdiction formelle : inclure la réception d'un message sans inclure son émission. Ce serait un message reçu mais jamais envoyé : impossible.

0 / 0
Prêt
Un message part de s (P1) et arrive en r (P3). Compare une coupe cohérente et une coupe incohérente.
coupe cohérente coupe incohérente
Astuce d'étiquetage

Une coupe cohérente « c » peut être étiquetée par un vecteur Vc = max (composante par composante) des vecteurs des événements de sa frontière. Ça relie directement ce chapitre aux horloges vectorielles.

07 · Snapshot distribué

Chandy-Lamport : photographier sans tout figer

On veut un état global cohérent : l'état de chaque processus plus l'état de chaque canal (les messages en transit). Impossible de visiter tous les sites au même instant. L'idée : un marqueur qui se propage.

Pourquoi la solution naïve échoue

Si chacun enregistre son état puis prévient ses voisins, les délais différents font qu'un site peut enregistrer la réception d'un message dont l'émission n'a été capturée nulle part. L'union des états n'est alors pas cohérente.

L'algorithme (canaux FIFO)

// hypothèses : canaux FIFO, pas de perte, pas de panne
Réception du marqueur (1re fois sur un canal) :
     enregistrer mon état ;
     état du canal d'arrivée := vide ;
     rediffuser le marqueur à tous mes voisins sortants ;
     journaliser les messages des autres canaux entrants

Réception du marqueur (fois suivantes sur un canal) :
     état du canal := messages journalisés reçus sur ce canal
                      depuis mon enregistrement

Terminé : chaque Pi a reçu le marqueur sur tous
          ses canaux entrants

Un processus quelconque peut initier à tout moment (il joue son propre marqueur). L'état d'un canal P→Q, ce sont les messages que Q reçoit sur ce canal après son enregistrement et avant le marqueur venu de P : exactement ce qui était en transit.

Résultat

Dans l'exemple ci-dessous, le snapshot est {Sp, Sq, Sr, m3} : trois états de processus plus un message en transit. Cohérent, sans jamais figer tout le monde au même instant.

0 / 0
Prêt
R initie le snapshot. Le marqueur se propage, chaque processus enregistre son état, et m3 en transit est capturé.
enregistrement d'état marqueur / message

08 · Exercices corrigés

Le TD2, pas à pas

Les six exercices du TD2, corrigés en détail : d'abord ce que la consigne demande vraiment, puis la réponse justifiée. Pour l'exercice 2, les valeurs d'horloges sont exactement celles que produit l'animation ci-dessus, tu peux vérifier. L'exercice 1 porte sur un second diagramme (P, Q, R), décrit dans le corrigé.

Corrigé

Exercice 1 : horloges entières sur le diagramme P, Q, R

Un second diagramme, trois processus P, Q, R. Nature des événements, canaux FIFO ou non, horloges de Lamport, profondeur, concurrence.

›

Diagramme du sujet (exo2.pdf). P : p1, p2, p2', p3, p3', p4, p5, p6. Q : q1, q2, q3, q3', q4, q5, q6, q7, q8, q9, q10, q11. R : r1..r6. Messages lus sur les flèches : p1→q3, p2→q6, q2→p5, q4→p3, q5→r2, q2→r1, q7→r5, r3→q9, q10→r6. Les événements primés (p2', p3', q3') servent à la question 3, ils ne portent aucun message.

PQRp1p2p2'p3p3'p4p5p6q1q2q3q3'q4q5q6q7q8q9q10q11r1r2r3r4r5r6
Diagramme P, Q, R. Points = evenements, points oranges = receptions, fleches = messages. Le temps va vers la droite ; Q envoie vers P5 puis Q4 vers P3, d'ou le canal Q vers P non FIFO.

Q1Que représentent p5, q1, p2, r4 ?

Trois natures d'événement existent : émission, réception, interne (local, sans message).

  • p5 : une réception (P reçoit le message parti de q2).
  • p2 : une émission (P envoie le message reçu en q6).
  • q1 : un événement interne de Q (aucune flèche ne le touche).
  • r4 : un événement interne de R (entre l'émission r3 et la réception r5, sans message).

Q2Les canaux sont-ils FIFO ?

Non, pas tous. Regarde le canal Q→P : q2 envoie un message (reçu en p5), puis plus tard q4 en envoie un autre (reçu en p3). Or p3 est avant p5 sur P : le message parti le plus tard (q4→p3) arrive le premier. Il y a dépassement, donc Q→P n'est pas FIFO. Les autres canaux utilisés respectent l'ordre d'émission : P→Q (p1→q3 puis p2→q6) et Q→R (q2→r1, q5→r2, q7→r5) sont FIFO.

QPq2 vers p5q4 vers p3q2q4p3p5recu en 1errecu en 2e
Canal Q vers P : q2 envoie avant q4, mais son message arrive en p5, apres p3. Le second parti double le premier : non FIFO.

Q3Peut-on avoir ces échanges ?

Règle : un message e→f est possible si et seulement s'il ne crée pas de cycle causal, c'est-à-dire s'il n'existe pas déjà un chemin causal de f vers e (sinon f serait à la fois avant et après e). On ne compare jamais les hauteurs entre deux processus différents : il n'y a pas d'horloge globale, seule la relation « arrivé avant » compte.

  • Émission en q5, réception en p2' : existe-t-il un chemin de p2' vers q5 ? P n'envoie qu'en p1 et p2, tous deux avant p2' : rien ne sort de P après p2', donc aucun chemin de p2' vers q5. Pas de cycle. Oui, possible. C'est exactement le message ajouté au test 2 du cours ; avec q4→p3, il rend le canal Q→P non FIFO (q4 émet avant q5, mais p2' est reçu avant p3).
  • Émission en p3', réception en q3' : il existe déjà le chemin q3' → q4 → p3 → p3' (q4 envoie à p3). Le nouveau message p3'→q3' fermerait la boucle q3' → … → p3' → q3' : cycle causal. Non, impossible.

Q4Horloges de Lamport

Même règle qu'à l'exercice 2 : +1 avant chaque événement local et chaque envoi ; à la réception, max puis +1. On obtient :

Pp1=1p2=2p2'=3p3=6p3'=7p4=8p5=9p6=10
Qq1=1q2=2q3=3q3'=4q4=5q5=6q6=7q7=8
Q (suite)q8=9q9=10q10=11q11=12    
Rr1=3r2=7r3=8r4=9r5=10r6=12  

Valeurs demandées : sur P après p6 = 10, sur Q après q11 = 12, sur R après r6 = 12.

Q5Profondeur et durée

La profondeur du diagramme (durée minimum) est la plus longue chaîne causale, soit la plus grande valeur de Lamport atteinte : 12. Un chemin critique menant à r6 : p1→q3, puis q3'→q4→q5, puis q5→r2, r3→q9, q10→r6. Il compte 4 messages. Si chaque transmission dure au plus τmax et qu'on néglige les événements locaux, la durée maximum le long de ce chemin est de l'ordre de 4·τmax.

Q6Événements concurrents à q6

q6 est la réception du message de p2. Son passé causal est {p1, p2, q1, q2, q3, q3', q4, q5} ; son futur est {q7, q8, q9, q10, q11, r5, r6}. Tout le reste lui est concurrent :

p2', p3, p3', p4, p5, p6, r1, r2, r3, r4 (10 événements). Par exemple r1 vient de q2 comme q6, mais sur une autre branche : aucun ne cause l'autre.

PQRp1p2p2'p3p3'p4p5p6q1q2q3q3'q4q5q6q7q8q9q10q11r1r2r3r4r5r6passeq6futurconcurrent
Cone causal de q6. Vert = passe (ce qui cause q6), violet = futur (cause par q6), gris = concurrent (10 evenements).

Q7heure(a) < heure(b) n'implique pas a → b

Contre-exemple : r1 (Lamport 3) et q6 (Lamport 7). On a heure(r1) < heure(q6), pourtant r1 et q6 sont concurrents (tous deux issus de q2, sur des branches différentes, sans chaîne causale entre eux). Un entier de Lamport plus petit ne prouve donc pas la précédence causale. C'est exactement la limite que les horloges vectorielles corrigent.

QRaucun lienq2 vers r1q2q6r1L=2L=7L=3chaine sur Q
r1 et q6 descendent de q2, sur deux branches. heure(r1)=3 < heure(q6)=7, pourtant aucun ne cause l'autre.
Corrigé

Exercice 2 : Lamport, vecteurs, coupures

Trois processus, onze événements, huit messages. Calculer les deux types d'horloges et classer les coupures.

›

Diagramme du sujet (exo2.pdf, figure 1), le même que celui des animations « Lamport » et « Vecteurs » ci-dessus. P1 : a1, b1, c1, d1 ; P2 : a2, b2, c2, d2 ; P3 : a3, b3, c3. Huit messages : a1→a2, a2→b1, a3→b2, b1→b3, c1→c2, c2→c3, c2→d1, c3→d2. Frontières des coupes : C = {a1, b2, b3}, C' = {c1, c2, c3}, C'' = {d1, d2, c3}.

P3P2P1CC'C''a1b1c1d1a2b2c2d2a3b3c3
Diagramme de l'exercice 2 (figure 1 du sujet). C (or) est incoherente : le message b1 vers b3 la traverse a l'envers. C' et C'' (vertes) sont coherentes.

Q1Modèle atomique ?

Oui. Chaque événement est ponctuel (instantané) et les événements d'un même processus sont totalement ordonnés ; un message relie une émission à une réception, sans durée modélisée à l'intérieur d'un événement. D'où des événements qui reçoivent et émettent en même temps : a2 (reçoit de a1, envoie à b1), b1 (reçoit de a2, envoie à b3), et c2 qui reçoit de c1 puis émet vers c3 et d1. C'est le modèle atomique de Mattern : on raisonne uniquement sur l'ordre des événements, jamais sur leur durée réelle.

Q2Estampilles de Lamport

Règle : +1 avant chaque événement local et chaque envoi ; à la réception, max puis +1 (un événement qui reçoit et émet compte une seule fois). On obtient :

P1a1=1b1=3c1=4d1=6
P2a2=2b2=3c2=5d2=7
P3a3=1b3=4c3=6 

Lance l'animation « Lamport » et vérifie chaque valeur : les entiers croissent le long des flèches.

Q3Horloges vectorielles [P1, P2, P3]

P1a1=[1,0,0]b1=[2,1,0]c1=[3,1,0]d1=[4,3,1]
P2a2=[1,1,0]b2=[1,2,1]c2=[3,3,1]d2=[3,4,3]
P3a3=[0,0,1]b3=[2,1,2]c3=[3,3,3] 

Q4Commenter c1 et c3

c1 = [3,1,0] et c3 = [3,3,3]. On a c1 ≤ c3 composante par composante (3≤3, 1≤3, 0≤3) et c1 ≠ c3, donc c1 < c3, c'est-à-dire c1 → c3 : ils sont comparables. C'est logique, il existe une chaîne causale c1 → c2 → c3.

P3P2P1c3c2c1[3,1,0][3,3,1][3,3,3]
c1 -> c2 -> c3 : chaine causale (fleches epaisses). Donc c1 < c3, comparables. Les vecteurs le confirment : [3,1,0] <= [3,3,3].

Q5b3 contre c2, puis b3 contre d2

Lamport : b3 = 4 et c2 = 5. On aurait envie de conclure b3 → c2 car 4 < 5. Faux : L(a) < L(b) ne prouve rien. Les vecteurs tranchent : b3 = [2,1,2], c2 = [3,3,1]. Sur P3, 2 > 1 ; sur P2, 1 < 3. Aucune inégalité ne tient dans un seul sens : b3 || c2, ils sont concurrents. Voilà pourquoi Lamport ne suffit pas.

Pour b3 et d2 : Lamport donne 4 < 7, ce qui ne suffit toujours pas. Vecteurs : b3 = [2,1,2], d2 = [3,4,3]. Cette fois 2≤3, 1≤4, 2≤3 : donc b3 < d2, c'est-à-dire b3 → d2. Comparables, via la chaîne b3 → c3 → d2.

P3P2b3 || c2b3c3c2d2[2,1,2][3,3,1][3,4,3]
b3 || c2 (incomparables : [2,1,2] contre [3,3,1]). Mais b3 -> c3 -> d2 (chemin epais), donc b3 < d2.

Q6Les coupes C, C', C'' sont-elles cohérentes ?

Une coupe est cohérente si sa frontière ne coupe aucun message « à l'envers » (réception dans le passé, émission dans le futur). Test avec les vecteurs : VCut = max des vecteurs de la frontière, cohérente si et seulement si VCut[k] = Vk[k] pour tout k (la diagonale).

  • C, frontière {a1, b2, b3} : VCut = max([1,0,0], [1,2,1], [2,1,2]) = [2,2,2], diagonale (a1[1], b2[2], b3[3]) = (1,2,2). VCut[1]=2 ≠ 1 : incohérente. Concrètement, b3 (passé) a reçu le message de b1, mais b1 est après a1 sur P1, donc dans le futur.
  • C', frontière {c1, c2, c3} : VCut = max([3,1,0], [3,3,1], [3,3,3]) = [3,3,3] = diagonale (3,3,3). Cohérente. Deux messages sont en transit : c2→d1 et c3→d2.
  • C'', frontière {d1, d2, c3} : VCut = max([4,3,1], [3,4,3], [3,3,3]) = [4,4,3] = diagonale (4,4,3). Cohérente.

Coupe D (pour s'entraîner), frontière {d1, b2, c3} : VD = max([4,3,1], [1,2,1], [3,3,3]) = [4,3,3]. Composantes 1 et 3 conformes (d1[1]=4, c3[3]=3), mais VD[2]=3 ≠ b2[2]=2 : incohérente. c3 et d1 dépendent tous deux de c2, troisième événement de P2, alors que la frontière arrête P2 à b2 : on garderait des réceptions sans leur cause.

Q7S = Σ si et R = Σ ri sur C, C', C''

On compte, sous chaque frontière, les messages émis (si) et reçus (ri) par chaque processus. c2 émet deux messages.

Coupes1+s2+s3 = Sr1+r2+r3 = R
C1+1+1 = 30+2+1 = 3
C'3+3+2 = 81+3+2 = 6
C''3+3+2 = 82+4+2 = 8

Piège : sur C on a S = R alors que la coupe est incohérente. Le message a1→a2 est compté émis et reçu, mais b1→b3 est compté reçu sans être émis, et a2→b1 émis sans être reçu : les deux erreurs se compensent.

Q8Peut-on conclure la terminaison ?

Sur C'', S = R = 8 : sur une coupe cohérente, cela veut dire qu'aucun message n'est en transit, les canaux sont vides. Mais S = R ne suffit pas seul : C montre S = R sur une coupe incohérente. Il faut une coupe cohérente et des processus passifs. C'' est cohérente, donc si les processus sont passifs à cet instant, l'algorithme est terminé. Sur C', S > R : deux messages en vol, on ne peut rien conclure.

Q9Événements concurrents et diagonale des vecteurs

Affirmation : si des événements de vecteurs Vt1, ..., Vtn sont deux à deux concurrents, alors le max composante par composante vaut la diagonale (Vt1[1], Vt2[2], ..., Vtn[n]).

Intuition : Vti[i] compte les événements que Pi a lui-même estampillés (sa propre « hauteur »). Dans la colonne j, Vti[j] dit combien d'événements de Pj ont influencé l'événement i. Si i et j sont concurrents, i ignore l'événement j, donc Vti[j] < Vtj[j] : personne ne connaît autant d'événements de Pj que Pj lui-même. Le max de la colonne j est donc atteint pour i = j, soit Vtj[j], la diagonale.

Exemple sur le diagramme, trio deux à deux concurrent {c1, b2, b3} : c1=[3,1,0], b2=[1,2,1], b3=[2,1,2] (aucun vecteur n'est ≤ un autre). max = [3,2,2] = (c1[1], b2[2], b3[3]) = (3, 2, 2). C'est justement le vecteur de la coupe cohérente de frontière {c1, b2, b3}. Attention, {d1, c2, b3} ne marche pas : c2 → d1 (message c2→d1). On retrouve la condition VCut[k]=Vk[k] : une coupe est cohérente si et seulement si sa frontière est faite d'événements deux à deux concurrents.

P3P2P1b3b2c1c1 = [3,1,0]b2 = [1,2,1]b3 = [2,1,2]max = [3,2,2]diagonale = max
Trio deux a deux concurrent {c1, b2, b3}. Le max colonne par colonne vaut la diagonale (3,2,2) : chaque Pi domine sa propre colonne.
Corrigé

Exercice 3 : estampilles bornées pour l'accès exclusif (2 processus)

Deux processus se disputent une ressource : requête estampillée, puis grant. Peut-on borner la taille des estampilles pour que le compteur ne parte pas à l'infini ?

›

1La version non bornée (le point de départ)

Chaque demande porte une estampille de Lamport. Un processus accorde (grant) la ressource à l'autre s'il ne la veut pas, ou si sa propre estampille de requête est plus grande que celle reçue (égalité départagée par l'identité 0/1). Autrement dit, la plus petite estampille gagne. C'est l'exclusion mutuelle de Lamport réduite à deux processus. Ennui : les estampilles croissent sans fin, le compteur n'est pas borné.

2L'indice : quel écart maximal entre les deux estampilles ?

C'est la clé. Avant de pouvoir redemander la ressource, un processus doit l'avoir acquise, utilisée, puis relâchée ; ce cycle échange des messages (request, grant, release) avec l'autre, ce qui recale l'horloge de l'autre au passage. Conséquence : à tout instant, les deux estampilles de requête en jeu ne diffèrent que de 1 au plus. Un processus ne peut jamais prendre plus d'une requête d'avance.

3Donc oui, des estampilles bornées suffisent

Puisqu'on n'a jamais besoin de distinguer un écart supérieur à 1, coder des valeurs arbitrairement grandes est inutile. On garde le compteur dans un petit ensemble (par exemple {0, 1, 2}) et on compare modulo : avec un écart borné par 1, « qui est le plus petit » reste sans ambiguïté malgré le repli à 0. Quand les deux processus sont d'accord et qu'aucune requête n'est en cours (système au repos), on remet les deux compteurs à 0.

012P0P1ecart des 2 horloges= 1 au plusdonc 3 valeurssuffisent (mod 3)
Horloge repliee sur {0,1,2}. Les deux processus ne sont jamais a plus de 1 d'ecart, donc 3 valeurs suffisent a trancher l'ordre (retour a 0).

4Le tic borné, pas à pas

// horloge L dans {0..K}, K petit (K=2 suffit pour 2 processus)
Événement local ou envoi :
     L := (L + 1) mod (K+1)

Réception d'une estampille l :
     L := (max_cyclique(L, l) + 1) mod (K+1)

Comparer deux requêtes (moi, autre) (écart borné par 1) :
     si moi = autre alors l'identité (0 < 1) départage
     sinon le plus petit au sens cyclique gagne

Retour au repos (aucune requête, horloges d'accord) :
     L := 0   // les deux processus se resynchronisent à 0

On obtient le même ordre d'accès que la version non bornée : la comparaison désigne toujours le même gagnant, car l'information utile (l'ordre de deux requêtes proches) est préservée. Seule la taille du compteur change.

Ce qu'il faut retenir

Borner une horloge logique est possible quand l'écart utile entre les valeurs comparées est lui-même borné. À deux processus, cet écart vaut au plus 1, donc quelques valeurs suffisent. Généraliser à N processus demande de borner l'écart pour chaque paire, ce qui est nettement plus délicat.

Corrigé

Exercice 4 : réception en ordre croissant d'estampilles

N processus, canaux FIFO. Chaque processus doit traiter les messages dans l'ordre croissant de leurs estampilles. Comment garantir ça ?

›
P1P2P3M1 ts=1M2 ts=2M3 ts=4
P1 envoie M1 a P2 et M2 a P3 ; a la reception P3 envoie M3 a P2. P2 peut recevoir M3 (ts=4) avant M1 (ts=1).

1Le scénario qui pose problème

P1 envoie M1 (ts=1) à P2 et M2 (ts=2) à P3 ; à la réception, P3 envoie M3 (ts=4) à P2. P2 reçoit donc M1 (ts=1) et M3 (ts=4). Parfois il les reçoit dans l'ordre (1 puis 4) : pas de souci. Mais rien ne l'empêche de recevoir M3 (ts=4) avant M1 (ts=1) : les canaux sont FIFO mais point-à-point, M1 et M3 voyagent sur des canaux différents, aucun ne « double » l'autre. P2 risque alors de traiter ts=4 avant ts=1.

2Le vrai problème à résoudre

Quand P2 reçoit un message d'un processus, comment être sûr qu'aucun autre processus ne lui enverra plus tard un message d'estampille plus petite ? Sans cette garantie, impossible de traiter en ordre croissant.

3L'idée : messages « null » et attente d'un message par source

Avant de traiter un message au niveau applicatif, P attend d'avoir reçu un message de chaque source possible. Pour que ça arrive toujours : chaque fois qu'un processus envoie un message applicatif à un destinataire, il diffuse aussi un message « null » estampillé à tous les autres. Comme les données sont émises infiniment souvent (hypothèse), les messages null aussi. Le récepteur finit donc toujours par avoir, de chaque source, un message (null ou non) : il peut comparer les estampilles et traiter le plus ancien en toute sécurité.

4L'algorithme sur Pi

// Log[k] = dernier message connu de Pk ; init : null, ts = -infini
Message M reçu de Pj (Log[j] vide ou déjà délivré) :
     synchroniser l'horloge de Lamport ;
     Log[j] := M ;
     plusVieux := M ;
     pour tout k ≠ i, k ≠ j :
         si Log[k].ts < plusVieux.ts alors plusVieux := Log[k] ;
     si plusVieux ≠ null et pas encore délivré :
         délivrer plusVieux à l'application ; marquer délivré

Envoi applicatif de M :
     L := L + 1 ;
     envoyer M estampillé L au destinataire ;
     envoyer "null" estampillé L à tous les autres

Deux subtilités : (1) si Pj renvoie un message alors que le précédent n'est pas encore délivré, il faut le bufferiser (les canaux de capacité infinie servent à ça, on ne le consomme pas tout de suite) ; (2) recevoir un message null peut débloquer la délivrance d'un message non-null devenu le plus ancien.

Le lien à retenir

Ce principe est exactement celui de l'exclusion mutuelle de Lamport (vu au dernier chapitre) : avant d'entrer en section critique, un processus doit être certain qu'aucune requête plus ancienne que la sienne ne circule encore. Même garantie, même mécanisme d'attente sur les estampilles.

Corrigé

Exercice 5 : Chandy-Lamport casse en non-FIFO

Construire un exemple où le snapshot devient incohérent si les canaux ne sont pas FIFO.

›
PQmmarqueursnap
Canal P vers Q non FIFO : le marqueur (envoye apres m, a l'evenement snapshot) depasse m et arrive avant. m tombe hors du snapshot.

IdéeLe marqueur double un message applicatif

Deux processus P et Q, canal P→Q non-FIFO. Déroulé :

  • P envoie un message applicatif m à Q.
  • P initie le snapshot : il enregistre son état, puis envoie le marqueur sur P→Q. Le marqueur part après m.
  • Canal non-FIFO : le marqueur dépasse m et arrive chez Q en premier.
  • Q reçoit le marqueur : il enregistre son état et note le canal P→Q comme vide (rien reçu avant).
  • Q reçoit ensuite m. Ce message a été envoyé avant le snapshot de P, il aurait dû être compté comme « en transit », mais Q le reçoit après son propre enregistrement et le traite comme un message post-snapshot.

Conséquence : m disparaît du snapshot (ni dans l'état d'un processus, ni dans l'état du canal). L'état global enregistré est incohérent : il « perd » un message. La propriété FIFO est donc nécessaire pour que le marqueur ne double jamais un message applicatif, garantissant que tout ce qui précède le marqueur sur un canal est bien capturé.

Corrigé

Exercice 6 : anneau P/Q, le jeton m qui tourne

P et Q font tourner un unique message m. État = nombre de réceptions de m. P a m, état 101, et lance le snapshot juste après avoir envoyé m.

›
PQmmarqueurmarqueurrm
Anneau P/Q, un seul jeton m. P envoie m puis lance le snapshot (Sp=101). Q recoit m (Sq=102) puis le marqueur. Etat rapporte : Sp=101, Sq=102, canaux vides.

SetupDeux canaux, un seul jeton

Anneau P ⇄ Q, deux canaux (P→Q et Q→P). Un seul exemplaire de m circule. P envoie m en premier. À l'instant considéré, P détient m, son état vaut 101. P envoie m à Q, puis immédiatement initie le snapshot.

DérouléApplication de Chandy-Lamport

  • P enregistre son état Sp = 101 et envoie le marqueur sur P→Q (derrière m, canal FIFO).
  • Q reçoit d'abord m : son état passe à 102. Puis Q reçoit le marqueur : il enregistre Sq = 102 et note le canal P→Q comme vide (m a été reçu avant le marqueur, il est donc dans l'état de Q, pas dans le canal).
  • Q rediffuse le marqueur sur Q→P. P le reçoit : ayant déjà enregistré son état, il note le canal Q→P comme vide (rien reçu dessus entre-temps, m circulait dans l'autre sens).
État global rapporté

Sp = 101, Sq = 102, les deux canaux vides. Total = 203 réceptions. C'est cohérent : le message m est comptabilisé une seule fois (dans l'état de Q), aucun message n'est perdu ni dupliqué, et personne n'a été figé au même instant réel.

09 · Entraînement

QCM d'entraînement

Teste-toi avant le QCM de la semaine. Pool de questions tirées au hasard, options mélangées, correction et explication immédiates. Ton meilleur score est gardé dans ton navigateur.

Prêt

Pool de 34 questions. Tire un échantillon au hasard, correction immédiate et explication à chaque réponse.

Cartes mémo / clique pour retourner

Lis la question, réponds dans ta tête, puis retourne la carte.

Motivation
Pourquoi pas d'observateur global pour dater ?
clique pour la réponse ↻
Motivation
Aucun « dieu » ne voit tout. Un observateur ne perçoit que certains événements et leur ordre relatif ; l'ordre global ne s'en déduit pas.
Horloges physiques
Méthode de Cristian, en une ligne ?
clique pour la réponse ↻
Horloges physiques
P demande l'heure t à S, mesure l'aller-retour, et pose son horloge à t + round-trip/2. Précision ±(round-trip/2 − min).
Horloges physiques
Berkeley : externe ou interne ?
clique pour la réponse ↻
Horloges physiques
Interne. Un maître collecte les horloges, fait une moyenne tolérante aux fautes (écarte les valeurs hors skew δ) et renvoie l'ajustement à chacun.
Horloges physiques
À quoi sert NTP ?
clique pour la réponse ↻
Horloges physiques
Resynchroniser une horloge locale sur l'UTC, via des serveurs hiérarchisés (niveau 1 = au plus près des sources atomiques).
Causalité
Définition de e1 → e2 ?
clique pour la réponse ↻
Causalité
Ordre local sur un même processus, OU envoi→réception d'un même message, OU par transitivité. C'est la relation happened-before.
Causalité
e1 || e2 veut dire quoi ?
clique pour la réponse ↻
Causalité
Concurrents : ni e1 → e2 ni e2 → e1. Incomparables selon la causalité, sans rapport avec la simultanéité réelle.
Lamport
Règle à la réception (ts = l) ?
clique pour la réponse ↻
Lamport
L = max(L, l) + 1. On recale sur le max puis on incrémente pour dater la réception après l'envoi.
Lamport
Ce que Lamport garantit (et pas) ?
clique pour la réponse ↻
Lamport
e1 → e2 implique L(e1) < L(e2). Mais L(e1) < L(e2) n'implique pas e1 → e2 (ils peuvent être concurrents).
Lamport
Comment forcer un ordre total ?
clique pour la réponse ↻
Lamport
Départager les ex æquo par l'identifiant du site (site A < site B). Utile quand deux requêtes de même date doivent être ordonnées pareil partout.
Vecteurs
Que représente V[i] ? V[j], j≠i ?
clique pour la réponse ↻
Vecteurs
V[i] = nombre d'événements estampillés par Pi. V[j] = nombre d'événements de Pj dont Pi a pu subir l'influence.
Vecteurs
Règle à la réception ?
clique pour la réponse ↻
Vecteurs
Incrémenter sa propre case, puis V[j] = max(V[j], t[j]) pour tout j (merge composante par composante).
Vecteurs
Comparer deux vecteurs ?
clique pour la réponse ↻
Vecteurs
V1 < V2 ssi V1[j] ≤ V2[j] pour tout j et V1 ≠ V2. Si ni ≤ ni ≥, alors concurrents.
Vecteurs
Le sur-pouvoir des vecteurs ?
clique pour la réponse ↻
Vecteurs
V(e1) < V(e2) équivaut à e1 → e2, et l'incomparabilité équivaut à e1 || e2. Ils caractérisent exactement la causalité.
Vecteurs
Le prix à payer ?
clique pour la réponse ↻
Vecteurs
Taille N par estampille (un entier par processus). Pas scalable sur de grands systèmes.
Snapshots
Un état global, c'est quoi ?
clique pour la réponse ↻
Snapshots
États de tous les processus + états de tous les canaux (messages en transit). Oublier les canaux = photo fausse.
Coupures
Coupe cohérente ?
clique pour la réponse ↻
Coupures
Elle respecte la causalité : si e2 est dans le passé et e1 → e2, alors e1 aussi. Interdit d'inclure une réception sans son émission.
Coupures
Étiqueter une coupe cohérente ?
clique pour la réponse ↻
Coupures
Par le vecteur Vc = max composante par composante des vecteurs des événements de la frontière.
Chandy-Lamport
Hypothèses de l'algo ?
clique pour la réponse ↻
Chandy-Lamport
Canaux FIFO, sans perte, sans panne. Il tourne en parallèle de l'application.
Chandy-Lamport
1er marqueur reçu : que fait Pi ?
clique pour la réponse ↻
Chandy-Lamport
Il enregistre son état, marque le canal d'arrivée comme vide, rediffuse le marqueur, puis journalise les messages des autres canaux.
Chandy-Lamport
État du canal P→Q ?
clique pour la réponse ↻
Chandy-Lamport
Les messages que Q reçoit sur ce canal après son propre enregistrement et avant le marqueur venu de P (les messages en transit).
Chandy-Lamport
Terminaison ?
clique pour la réponse ↻
Chandy-Lamport
Chaque processus a reçu le marqueur sur tous ses canaux entrants. L'union des états est alors un snapshot cohérent.
Coupures
Coupe cohérente et concurrence ?
clique pour la réponse ↻
Coupures
Une coupe est cohérente ssi sa frontière est faite d'événements deux à deux concurrents. Le vecteur de coupe vaut alors la diagonale : VCut[k] = Vk[k].
Vecteurs
Événements deux à deux concurrents : que vaut le max des vecteurs ?
clique pour la réponse ↻
Vecteurs
La diagonale (Vt1[1], Vt2[2], ..., Vtn[n]). Chaque Pi connaît ses propres événements mieux que les autres, donc le max de la colonne j est Vtj[j].
Lamport
Peut-on borner une horloge de Lamport ?
clique pour la réponse ↻
Lamport
Oui quand l'écart utile entre valeurs comparées est borné. À 2 processus en exclusion mutuelle, l'écart des requêtes vaut au plus 1, donc un petit compteur modulo suffit (retour à 0).
Coupures
Repérer une coupe incohérente avec les vecteurs ?
clique pour la réponse ↻
Coupures
Calculer VCut = max des vecteurs de la frontière. Si pour un k, VCut[k] ≠ Vk[k], la coupe inclut un effet sans sa cause : incohérente.