AlgoDist
Master 2 · Polytech UCAChapitre 3F. Baude / L. Henrio

Les fautes,
et comment
s'en remettre.

Une machine tombe en panne au milieu d'un calcul distribué. Comment reprendre sans tout recommencer, et sans reconstruire un état impossible ? On sauvegarde, on rejoue, on évite l'effet domino.

Le chapitre où « coupure cohérente » devient concret. Toute la reprise repose sur une idée du chapitre 2 : ne jamais restaurer un message reçu mais jamais envoyé. Chaque mécanisme ci-dessous s'anime pas à pas.
checkpoint message
En 30 secondes

On veut qu'un système survive à des pannes. La brique de base : sauvegarder de temps en temps l'état sur un stockage stable (checkpoint), pour pouvoir reculer après une panne. Piège : reculer chacun à son dernier checkpoint peut donner une photo incohérente (un message reçu mais jamais envoyé), et le rattrapage peut cascader jusqu'au début (effet domino). Deux grandes familles de solutions : le checkpointing (coordonné, ou induit par les communications CIC) qui garantit une coupe cohérente, et le message logging qui journalise les événements non déterministes pour rejouer seulement les processus fautés. Chandy-Lamport calcule une photo cohérente sans figer personne.

01 · Généralités

Quelles fautes, quelle redondance ?

Avant de se protéger, il faut nommer ce contre quoi. Les fautes vont de la plus simple (une machine s'arrête) à la plus vicieuse (une machine ment).

Le plus simple

Crash (fail-stop)

Le serveur fonctionne correctement, puis s'arrête net et ne fait plus rien. C'est l'hypothèse de travail de tout le chapitre. Détectable par temporisation si le canal est fiable et borné.

Intermédiaire

Omission, timing, valeur

Omission : le serveur oublie de répondre, d'émettre ou de recevoir. Timing : il répond hors du délai spécifié. Response : la valeur ou la transition d'état est fausse.

Le plus dur

Arbitraire (byzantine)

Le composant peut produire n'importe quelle réponse à n'importe quel instant, y compris mentir de façon cohérente ou dire des choses différentes à deux pairs. Le pire cas.

k-fault tolerant

Un système est k-fault tolerant s'il continue de respecter sa spécification malgré la panne de k composants. L'ordre de grandeur de la redondance dépend du modèle : pour des crashs, souvent k+1 répliques suffisent ; pour des fautes byzantines, il en faut 2k+1 (et jusqu'à 3k+1 pour se mettre d'accord). Plus la faute est méchante, plus la redondance coûte.

Prérequis : le stockage stable

La reprise relit un état sauvegardé après la panne. Ce support doit donc survivre à la panne visée. Pour tolérer une seule panne, la mémoire volatile d'un autre processus suffit. Pour des pannes transitoires arbitraires, un disque local par hôte. Pour des pannes non transitoires (l'hôte ne revient pas), il faut un médium persistant hors de l'hôte : un système de fichiers répliqué, par exemple.

02 · Le décor

Exécutions, causalité, coupures cohérentes

Rappel express du chapitre 2, car toute la reprise en dépend : ce qui compte n'est pas l'heure, mais l'ordre causal des événements.

Happened-before (→)

  • Ordre local sur un même processus.
  • L'envoi d'un message précède sa réception.
  • Transitivité. Deux événements non reliés sont concurrents (||).

Deux exécutions sont identiques si l'ordre de réception des messages est le même, quels que soient les temps physiques.

Coupe cohérente

Une coupe découpe, pour chaque processus, un passé (à gauche) et un futur. Elle est cohérente si son passé est clos par causalité : jamais de réception dans le passé dont l'envoi serait dans le futur. C'est exactement un état qui aurait pu exister sans panne.

0 / 0
Prêt
Une coupe cohérente respecte la causalité. Avance pour voir un orphelin (interdit) et un message en transit (à sauvegarder).
cohérente orphelin / incohérente en transit
Les deux ennemis

Orphelin = message reçu (passé) dont l'envoi est dans le futur : rend la coupe incohérente, à bannir. En transit = message envoyé (passé) mais pas encore reçu (futur) : la coupe reste cohérente, mais si le canal est fiable il faut le sauvegarder pour le rejouer. Toute la suite du chapitre consiste à produire des coupes sans orphelin, et à gérer les en-transit.

03 · Principe de la reprise

Deux façons de revenir en arrière

Un état recouvrable contient assez d'information pour rejouer l'exécution jusqu'à une coupe cohérente de l'exécution originale. Il en existe deux grandes constructions.

Famille 1

Checkpointing

Chaque processus stocke son état local (checkpoint) de temps en temps. Après une panne, on redémarre assez de machines et on repart d'une coupe cohérente : presque tout le monde rejoue une nouvelle exécution.

Famille 2

Message logging

On journalise en plus les événements non déterministes (surtout les réceptions). Après une panne, seul le processus fauté (ou quelques-uns) redémarre et rejoue les messages journalisés jusqu'au point de panne.

Messages en transit et orphelins, côté reprise

Si la livraison n'est pas garantie, un message en transit peut être oublié sans rien violer. Si le protocole est fiable, il faut le stocker (avec le checkpoint de l'émetteur ou du récepteur) et le rejouer. Pour les orphelins, deux écoles : le checkpointing évite les coupes incohérentes (donc pas d'orphelin par construction) ; le message logging évite de ré-émettre le message et rejoue la même exécution.

04 · Non coordonné

Chacun sauvegarde quand il veut, et c'est le drame

Le plus simple : chaque processus prend ses checkpoints sans se concerter. Hypothèse fail-stop. Après une panne, on recalcule la ligne de recovery. Problème : elle peut être très loin en arrière.

0 / 0
Prêt
Une seule panne, deux processus. Regarde le rollback se propager de proche en proche.
checkpoint ligne de recovery orphelin
L'effet domino

Chaque rollback annule des réceptions, qui deviennent orphelines, ce qui force d'autres processus à reculer, et ainsi de suite. Avec un mauvais entrelacement, la cascade ramène jusqu'au tout premier checkpoint : tout le travail est perdu. C'est le défaut rédhibitoire du checkpointing non coordonné, et la raison pour laquelle on va vouloir synchroniser les checkpoints.

05 · Ligne de recovery

Jusqu'où reculer ? Le graphe de dépendances

Pour trouver la coupe cohérente la plus récente après une panne, on raisonne sur le graphe de dépendances de rollback : un nœud par intervalle entre deux checkpoints, une arête par message.

0 / 0
Prêt
Après une panne, on marche à reculons sur les dépendances des messages pour trouver la coupe cohérente la plus récente.
ligne de recovery dépendance suivie
Recette

1) Marquer l'état au point de panne. 2) Marquer par atteignabilité tout nœud atteignable depuis un nœud marqué (les messages orphelins propagent le marquage). 3) La ligne de recovery est l'union, par processus, du dernier checkpoint non marqué. C'est la coupe cohérente la plus récente.

06 · Coordonné

Un chef orchestre la sauvegarde

Pour tuer le domino : garantir que les checkpoints forment toujours une coupe cohérente. Un initiateur déclenche le snapshot global, tout le monde suit.

Le protocole (bloquant)

// initiateur
Init : checkpoint provisoire ;
       broadcast <prends un checkpoint>
Sur reception de la requete (Pi) :
       stop execution ;
       flush tous les canaux ;
       checkpoint provisoire ;
       send <ack> a l'initiateur
Init recoit tous les acks :
       broadcast <commit>
Sur reception de commit :
       jeter l'ancien checkpoint // le nouveau devient permanent
Prix à payer

Les communications sont bloquées pendant le protocole : latence, tout le monde attend. En échange : coupe toujours cohérente, un seul (ou deux) checkpoint par processus, pas d'effet domino.

Version non bloquante

Si les canaux sont FIFO : propager la requête de checkpoint avant tout autre message (ou la piggybacker sur le premier message, et checkpointer avant de le traiter). Aucun message ne franchit la coupe à l'envers, sans jamais bloquer.

0 / 0
Prêt
P0 initie. Requête, puis flush + checkpoint + accusé, puis commit. Résultat toujours cohérent.
checkpoint message de contrôle coupe cohérente

07 · CIC

Sans coordination : les messages induisent les checkpoints

Communication Induced Checkpointing : pas un seul message de contrôle. On accroche une information (un index croissant) aux messages applicatifs, et la réception peut forcer un checkpoint.

Deux sortes de checkpoints : locaux (chacun en prend spontanément) et forcés (imposés par un message). Objectif : garantir qu'une coupe cohérente existe toujours, sans jamais se parler explicitement, et sans créer de checkpoint inutile.

Briatico (index)

Chaque processus a un index courant croissant, piggybacké sur ses messages. À réception d'un message d'index supérieur au sien, on prend un checkpoint forcé avant de traiter le message, puis on rattrape l'index. Une ligne de recovery = tous les checkpoints de même index.

Pourquoi ça marche

Le checkpoint forcé empêche qu'un message franchisse « à l'envers » la coupe des checkpoints de même index. Donc pas d'orphelin, donc pas d'effet domino, et zéro message de contrôle. C'est le compromis élégant entre le non coordonné (léger mais dangereux) et le coordonné (sûr mais bloquant).

0 / 0
Prêt
Un index accroché aux messages. Un index trop grand force un checkpoint. Ligne de recovery = même index.
checkpoint local checkpoint forcé ligne de recovery

08 · Snapshot cohérent

Chandy-Lamport, un CIC avant l'heure

L'algorithme du chapitre 2 est le cousin de la CIC : canaux FIFO, messages jamais perdus, un marqueur se propage et déclenche l'enregistrement, en parallèle de l'application. Ici on l'applique à un anneau qui fait tourner un jeton.

0 / 0
Prêt
Un unique jeton tourne. P vaut 101 et lance le snapshot juste après avoir envoyé. Quels états globaux possibles ?
jeton m marqueur
Règles de Chandy-Lamport

Premier marqueur reçu sur un canal : enregistrer son état, marquer ce canal vide, rediffuser le marqueur. Messages arrivant ensuite sur les autres canaux entrants (avant leur marqueur) : journalisés comme état de canal. Fin : chaque processus a reçu le marqueur sur tous ses canaux entrants. FIFO est nécessaire pour que le marqueur sépare proprement l'avant et l'après sur chaque canal.

09 · Message logging

Rejouer plutôt que reculer tout le monde

L'autre famille. On journalise les événements non déterministes pour rejouer à l'identique. Seuls les processus fautés redémarrent : bien plus scalable, mais il faut une hypothèse forte.

Déterminisme par morceaux (PWD)

Hypothèse : tous les événements non déterministes peuvent être identifiés et leur déterminant stocké sur stockage stable. Entre deux tels événements, l'exécution est déterministe (donc rejouable). Une exécution = une suite d'événements déterministes (rejoués) et non déterministes (logués puis simulés depuis le log). Plus des checkpoints pour ne pas repartir du début.

La règle « no-orphan »

Un processus est orphelin s'il dépend d'un événement non déterministe dont le déterminant n'est pas logué (irrécupérable). On note Log(e) les processus stockant e, Stable(e) si son déterminant est sur stockage stable, Depend(e) les processus affectés par e. La condition « toujours sans orphelin » impose que tout déterminant dont dépend un autre processus soit stable.

Sûr

Pessimiste

Loguer le déterminant avant d'exécuter. Jamais d'orphelin, reprise simple et rapide (seul le fauté redémarre), garbage collection simple, monde extérieur facile à gérer. Coût : la pénalité de synchronisation à chaque événement.

Rapide

Optimiste

Déterminant gardé localement, flushé parfois. Suivre les dépendances causales ; à la reprise, calculer l'état maximum recouvrable (synchrone) ou propager la reprise aux processus liés (asynchrone). Risque de rollbacks exponentiels.

Alléger

Réduire le coût

Matériel : NVRAM plutôt que disque. Relâcher l'atomicité : si la perte de messages est tolérée, il suffit de loguer une réception avant d'émettre le suivant, plus besoin d'un log strictement synchrone.

Checkpointing vs message logging (simplifié)

Checkpointing : cible les systèmes petits à moyens, surcoût plutôt faible, mais tout le monde recule ensemble. Message logging : cible la grande échelle (on ne relance que les fautés), surcoût moyen à élevé (journaliser coûte). Comme souvent en tolérance aux fautes, les résultats forts exigent des hypothèses fortes ou beaucoup de redondance ; heureusement, en pratique, la plupart des systèmes sont assez fiables.

10 · Exercices corrigés

Le homework et les exercices du cours

Les exercices du homework (rendu du 29/09) et ceux vus en cours, corrigés en détail. Les diagrammes sont redessinés inline : pas besoin de la feuille du prof.

Corrigé

Homework, exercice 1 (Q1) : faire tourner la CIC

Exécuter le protocole CIC indexé (Briatico) sur un exemple, puis expliquer ce qui se passe à la reprise quand P2 tombe en panne.

›

Le sujet donne un exemple à 6 processus et 17 messages. On le redessine fidèlement (mêmes checkpoints, mêmes messages, dans le même ordre sur chaque ligne), puis on applique le protocole : l'index est écrit à côté de chaque checkpoint, l'index piggybacké sur chaque message, et les checkpoints forcés ajoutés par le protocole sont en or. Règle : recevoir un index plus grand que le sien force un checkpoint avant de traiter le message.

P0 P1 P2 P3 P4 P5 0 0 0 0 0 0 1 1 2 1 1 2 2 2 3 1 2 2 3 3 3 3 m1[0] m2[1] m3[0] m4[0] m5[0] m6[2] m7[2] m8[0] m9[2] m10[2] m11[2] m12[1] m13[1] m14[2] m15[2] m16[2] m17[2]
HW1 après exécution de la CIC. Carrés gris = checkpoints du sujet (le premier de chaque ligne est l'état initial, index 0), carrés or = checkpoints forcés. Chaque flèche porte son nom et, entre crochets, l'index piggybacké. P2 tombe en panne à droite.

Q1Dérouler le protocole

Tout le monde démarre à l'index 0. Chaque checkpoint local incrémente l'index. On lit chaque ligne de gauche à droite :

  • m2 (P1→P0) porte 1 : P1 a pris un checkpoint local juste avant. P0 est encore à 0 → checkpoint forcé d'index 1 sur P0, avant de délivrer m2.
  • P1 prend ensuite un checkpoint local et passe à 2. Ses messages suivants portent tous 2. m6 arrive sur P5 (à 1) → forcé d'index 2. m9 arrive sur P3 (à 1) → forcé d'index 2.
  • m7 arrive sur P2, encore à 0 : P2 saute directement à 2. Écart de plus de 1 : ce seul checkpoint forcé tient lieu de checkpoint d'index 1 et d'index 2 pour P2 (c'est la question « et si l'écart dépasse 1 ? » des slides).
  • Tous les autres messages portent un index inférieur ou égal à celui du récepteur : rien à forcer (m3 porte 0 vers P1 à 1, m11 porte 2 vers P0 à 3, m16 porte 2 vers P1 à 3...). Un index plus petit signale juste un message qui franchit une ligne vers l'avant : il sera en transit pour cette ligne.
  • Bilan : 4 checkpoints forcés (P0, P2, P3, P5). À la fin, P0, P1, P2, P3 et P5 sont à l'index 3, P4 à l'index 2. Pour chaque k, les checkpoints d'index k forment une coupe cohérente : un message d'index ≥ k ne peut jamais arriver chez un processus encore sous k sans le forcer.

Q2Ce qui se passe à la reprise (P2 en panne)

P2 tombe en panne. Son dernier checkpoint est d'index 3. La reprise prend la ligne d'index 3 : le checkpoint d'index 3 de chaque processus. P4 n'a jamais atteint l'index 3, il garde son état courant (tout ce qu'il a envoyé porte au plus 2, donc aucun orphelin possible). La ligne est cohérente par construction, sans graphe de dépendances, sans effet domino.

P0 P1 P2 P3 P4 P5 0 0 0 0 0 0 1 1 2 1 1 2 2 2 3 1 2 2 3 3 3 3 m1[0] m2[1] m3[0] m4[0] m5[0] m6[2] m7[2] m8[0] m9[2] m10[2] m11[2] m12[1] m13[1] m14[2] m15[2] m16[2] m17[2] ligne d'index 3 P4 n'a pas de checkpoint d'index 3 : la ligne passe par son état courant
Ligne de recovery d'index 3 (pointillés verts). En or : les messages envoyés avant la ligne et reçus après, donc en transit, à rejouer.

Six messages traversent la ligne vers l'avant : m10 et m14 (vers P2), m11 (vers P0), m15 (vers P3), m16 (vers P1), m17 (vers P5). Ils sont en transit : il faut les rejouer, par exemple depuis un log côté émetteur.

Pour aller plus loin

P2 n'a rien envoyé après son dernier checkpoint. Strictement, seul P2 avait besoin de reculer, et seuls m10 et m14 seraient à rejouer. La CIC ne le sait pas : elle garantit une ligne cohérente sans calcul, au prix de faire reculer tout le monde. Trouver la ligne minimale demande l'analyse de dépendances de la section 05.

Corrigé

Homework, exercice 1 (Q2) : pseudo-code de la CIC

Écrire les fonctions ReceiveMsg / sendMsg avec index piggybacké, appelant ProcessMessage à la fin.

›

Chaque processus Pi tient un index local idx, incrémenté à chaque checkpoint (local ou forcé). Tout message emporte l'index courant de son émetteur. À la réception, si l'index reçu est plus grand, on force un checkpoint avant de délivrer le message.

// etat local de Pi
idx := 0                 // index courant

checkpointLocal() :
     idx := idx + 1
     store(etat local, idx)   // checkpoint sur stockage stable

sendMsg(m, Pj) :
     send (m, idx) to Pj   // on piggyback l'index courant

ReceiveMsg(m, i) // i = index piggybacke, recu de Pj :
     if (i > idx) :
          idx := i
          store(etat local, idx)  // CHECKPOINT FORCE, avant de traiter m
     else if (i == idx) :
          // meme index : rien de force
     // (i < idx impossible d'apporter une contrainte : cf remarque)
     ProcessMessage(m)      // traitement applicatif normal

Messages en transit (bonus). Pour aussi restaurer les en-transit côté récepteur, on les ajoute au checkpoint forcé au moment où on le prend (receiver-based), et on les traite à la reprise. On le mentionne car le sujet le rend optionnel : ici on choisit de ne pas les gérer pour garder le pseudo-code lisible, l'ajout est direct.

Que faire si l'index reçu dépasse de plus de 1 ? On saute directement à cet index (idx := i) : on peut avoir « raté » des index intermédiaires, ce n'est pas un problème, un seul checkpoint forcé suffit à rejoindre la bonne coupe. Et si l'index reçu est plus petit que le sien ? Le message vient d'un checkpoint plus ancien : on n'a rien à forcer, mais cette information dit que l'émetteur est « en retard », on peut l'utiliser pour la garbage collection (les vieux checkpoints d'index inférieur sont encore utiles à quelqu'un).

Corrigé

Homework, exercice 2 : garder un nombre linéaire de checkpoints

Si chaque processus prend aussi des checkpoints locaux réguliers (toutes les 10 min), la CIC multiplie les checkpoints. Comment rester linéaire tout en gardant toujours un checkpoint ?

›

Le risque : additionner naïvement checkpoints locaux (périodiques) et checkpoints forcés (induits) fait exploser le total. L'idée est de fusionner quand c'est possible, en gardant un invariant simple.

  • Invariant à maintenir : il existe toujours, pour chaque processus, un checkpoint (forcé ou local) portant l'index courant. C'est cet invariant, et lui seul, qui garantit la cohérence de la ligne de recovery.
  • Réutiliser plutôt que dupliquer : quand la règle réclamerait un checkpoint forcé alors qu'un checkpoint local récent existe déjà pour le bon index, on l'utilise au lieu d'en créer un nouveau. Symétriquement, un checkpoint local qui tombe juste peut jouer le rôle du forcé attendu.
  • Résultat : le nombre de checkpoints forcés réellement ajoutés reste borné par le nombre de messages « incrémentant l'index » qui ne coïncident pas avec un checkpoint local. Le total (forcés + locaux) reste linéaire en le nombre d'événements, sans jamais violer « toujours un checkpoint disponible pour l'index courant ».

Intuition : on ne veut pas deux checkpoints à quelques instants d'intervalle pour le même index. Un seul suffit à la cohérence, donc on garde le premier venu (local ou forcé) et on ignore la demande redondante.

Corrigé

Homework, exercice 3 : la PWD est-elle nécessaire ?

Le déterminisme par morceaux est-il indispensable aux protocoles CIC ? Et aux autres checkpointing coordonné / non coordonné ?

›

CIC et checkpointing en général

Non, la PWD n'est pas nécessaire aux protocoles de checkpointing, qu'ils soient non coordonnés, coordonnés ou CIC. Raison : le checkpointing ne rejoue pas l'exécution passée. Il restaure un état sauvegardé (une coupe cohérente) et relance une nouvelle exécution à partir de là. Cette nouvelle exécution a parfaitement le droit de diverger de l'ancienne (autres choix non déterministes, autre ordre de réception) : tant qu'on repart d'un état qui aurait pu exister sans panne, la spécification est respectée. On n'a donc jamais besoin de reproduire à l'identique les événements non déterministes.

Message logging

À l'inverse, le message logging en a absolument besoin. Son principe est de rejouer l'exécution depuis le dernier checkpoint jusqu'au point de panne, à l'identique. Sans PWD, on ne saurait pas quels événements sont non déterministes ni comment les reproduire : le rejeu produirait une exécution différente, incohérente avec l'état des processus survivants (qui, eux, ont vu l'exécution d'origine). D'où des orphelins. La PWD est la pierre angulaire qui rend le rejeu possible.

En une phrase

Checkpointing = « je restaure et je repars, tant pis si ça diverge » → PWD inutile. Message logging = « je dois rejouer exactement pareil » → PWD indispensable.

Corrigé A++

Les quatre stratégies de checkpoint forcé

On veut un protocole simple sans effet domino : un checkpoint forcé avant/après l'envoi, ou avant/après la réception. Laquelle est utile ?

›

Exécution du sujet redessinée à l'identique : 4 processus, 3 ou 4 checkpoints locaux chacun, 8 messages (nommés m0 à m7 comme dans le corrigé), P1 et P3 tombent en panne à droite.

P0 P1 P2 P3 m0 m1 m2 m3 m4 m5 m6 m7
Exécution de base. Carrés gris = checkpoints locaux, X rouges = pannes de P1 et P3.

MéthodePlacer les forcés, puis trouver la ligne

Pour chaque stratégie, on ajoute un checkpoint forcé (en or) à côté de chaque envoi ou de chaque réception. Si un checkpoint local est déjà collé à l'événement (aucun autre événement entre les deux), il tient lieu de forcé. Ensuite la ligne de recovery (pointillés verts) : P1 et P3 repartent de leur dernier checkpoint, P0 et P2 de leur état courant, puis on recule tant qu'un message est reçu avant la ligne mais envoyé après (orphelin). C'est exactement le marquage par atteignabilité du graphe de dépendances (section 05).

1Checkpoint forcé AVANT l'envoi : la pire

P0 P1 P2 P3 m0 m1 m2 m3 m4 m5 m6 m7
Avant l'envoi. Ligne de recovery : P2 revient à son état initial.

L'émission reste hors du checkpoint. P1 repart de son forcé d'avant m3, mais m3 est déjà reçu par P0 : orphelin, P0 recule. Ce recul rend m2 orphelin, P1 recule avant m2, puis m4 fait reculer P3 avant m4, et m5 fait reculer P2 jusqu'au début. Cascade m3 → m2 → m4 → m5 : 7 checkpoints perdus, c'est l'effet domino qu'on voulait éviter. Stratégie la moins utile.

2Checkpoint forcé APRÈS l'envoi : la meilleure

P0 P1 P2 P3 m0 m1 m2 m3 m4 m5 m6 m7
Après l'envoi. P1 et P3 repartent de leur dernier forcé, P0 et P2 gardent leur état courant (point vert).

L'émission est toujours dans le checkpoint. Un processus qui recule ne peut donc jamais « désenvoyer » un message déjà reçu : aucun orphelin. P1 et P3 repartent de leur dernier checkpoint, personne d'autre ne recule, 0 checkpoint perdu. La ligne est établie immédiatement après la panne. Stratégie la plus utile.

Écart avec le corrigé manuscrit

Le corrigé fait aussi repartir P0 et P2 de leur dernier checkpoint. Ce n'est pas nécessaire : aucun message ne relie la ligne de P1/P3 à un événement de P0 ou P2 qui la précède. Ils peuvent garder leur état courant, ce qui renforce la conclusion.

3Checkpoint forcé AVANT la réception : plutôt utile

P0 P1 P2 P3 m0 m1 m2 m3 m4 m5 m6 m7
Avant la réception. P0 et P2 reculent d'un cran, jusqu'au forcé posé juste avant m3 et m7.

La réception reste hors du checkpoint. P1 et P3 repartent de leur dernier checkpoint local, mais m3 et m7, envoyés après, sont déjà reçus : P0 et P2 reculent, et tombent tout de suite sur le forcé posé juste avant ces réceptions. Recul borné à un cran, 2 checkpoints perdus, pas de cascade. Utile, juste un peu moins efficace que « après l'envoi ».

4Checkpoint forcé APRÈS la réception : peu utile

P0 P1 P2 P3 m0 m1 m2 m3 m4 m5 m6 m7
Après la réception. Tous les processus reculent de plusieurs checkpoints.

La réception est toujours capturée : chaque forcé contient une réception dont l'émission peut être annulée par la reprise. P0 recule à cause de m3, P1 à cause de m2, P3 à cause de m4, P2 à cause de m7 : 6 checkpoints perdus, tous les processus reculent. Mieux que « avant l'envoi » (la cascade s'arrête avant l'état initial), mais peu utile.

StratégieCe que le forcé captureCheckpoints perdusVerdict
après l'envoil'émission0la meilleure
avant la réceptionni l'un ni l'autre2utile
après la réceptionla réception6peu utile
avant l'envoirien, émission dehors7 (P2 au début)la pire
Classement

Après l'envoi (meilleure) > avant la réception > après la réception > avant l'envoi (pire). La règle générale : capturer l'émission dans le checkpoint tue les orphelins et rapproche la ligne de recovery du point de panne.

Corrigé

Chandy-Lamport sur l'anneau P/Q (état 101)

Un unique jeton m tourne entre P et Q, l'état = nombre de réceptions. P a m, état 101, initie juste après avoir envoyé. Quels états globaux possibles ?

›

L'animation de la section Snapshot déroule ce scénario. Le raisonnement :

  • Comme P envoie en premier et qu'il n'y a qu'un jeton, quand P a reçu 101 fois, Q a lui aussi reçu 101 fois juste avant. P envoie m vers Q puis initie : il enregistre Sp = 101 et place un marqueur sur le canal P→Q, derrière m.
  • Le canal est FIFO : Q reçoit d'abord m (son compteur passe à 102), puis le marqueur. Au premier marqueur, Q enregistre Sq = 102 et marque le canal P→Q comme vide (m a été délivré avant l'enregistrement), puis renvoie un marqueur à P.
  • Il reste le canal Q→P, dont l'état dépend de ce que Q a fait de m avant de traiter le marqueur.

États globaux possibles

  • (a) Q n'a pas encore renvoyé m quand il enregistre : snapshot = {P = 101, Q = 102, canaux vides}. Le jeton est « chez Q ».
  • (b) Q a déjà renvoyé m vers P avant de traiter le marqueur : ce m est parti avant le marqueur de Q sur le canal Q→P, donc P l'enregistre comme état de ce canal. Snapshot = {P = 101, Q = 102, canal Q→P = {m}}. Le jeton est « en transit vers P ».
P Q Sp = 101 Sq = 102 canaux vides, jeton chez Q m marqueur marqueur
Cas (a) : Q n'a pas renvoyé m avant son marqueur. Carrés verts = instants où P et Q enregistrent leur état.
P Q Sp = 101 Sq = 102 canal Q→P = {m} m marqueur m marqueur
Cas (b) : Q a renvoyé m avant d'enregistrer Sq. FIFO sur Q→P : m arrive chez P avant le marqueur de Q, P l'enregistre comme état du canal.

Les deux sont des coupes cohérentes : dans les deux cas il y a exactement un exemplaire du jeton (chez Q, ou dans le canal), et la somme des états est cohérente. L'algorithme peut rapporter l'un ou l'autre selon l'entrelacement, jamais un état incohérent.

Corrigé

Exercice 1 (slides 21 et 23) : graphe de dépendances, ligne de recovery, panne de P3, messages en transit

Construire le graphe de dépendances de rollback, trouver la ligne de recovery, refaire si P3 tombe seul, puis étendre les règles aux messages en transit.

›

Exécution du cours (figure du survey d'Elnozahy et al.), redessinée : 4 processus, checkpoints ci,x, P0 et P1 tombent en panne. Les messages sont nommés a à k, sauf m1 et m2 que le prof a annotés.

P0 P1 P2 P3 c0,0 c0,1 c0,2 c0,3 c1,0 c1,1 c1,2 c1,3 c2,0 c2,1 c2,2 c2,3 c3,0 c3,1 c3,2 c3,3 a b c d m2 m1 e f g h k
Exécution de l'exercice 1. Pannes de P0 et P1 à droite.

Q1Le graphe de dépendances de rollback

Un nœud par checkpoint, plus un nœud par état volatil (ce qui a été fait depuis le dernier checkpoint). Pour chaque message envoyé dans l'intervalle qui se termine par ci,x et reçu dans l'intervalle qui se termine par cj,y, on trace l'arête ci,x → cj,y. Sens : si on annule l'intervalle qui contient l'envoi, il faut annuler celui qui contient la réception. Par exemple m1, envoyé par P1 après c1,3 et reçu par P0 avant c0,3, donne volatil(P1) → c0,3.

P0 P1 P2 P3 c0,0 c0,1 c0,2 c0,3 volatil c1,0 c1,1 c1,2 c1,3 volatil c2,0 c2,1 c2,2 c2,3 volatil c3,0 c3,1 c3,2 c3,3 volatil vert : ligne de recovery (dernier nœud non marqué) orange : marqué · orange cerclé : état perdu, marqué au départ
Graphe de dépendances de rollback. Les flèches grises relient les nœuds d'un même processus, les orange sont les messages.

Q2La ligne de recovery (P0 et P1 en panne)

On marque les états volatils de P0 et P1 (perdus), puis tout ce qui est atteignable. m1 marque c0,3, puis m2 (envoyé par P0 dans l'intervalle de c0,3) marque c1,3. Rien d'autre n'est atteint. La ligne = le dernier nœud non marqué de chaque processus : {c0,2, c1,2, état courant de P2, état courant de P3}. P2 et P3 n'ont pas besoin de reculer.

P0 P1 P2 P3 c0,0 c0,1 c0,2 c0,3 c1,0 c1,1 c1,2 c1,3 c2,0 c2,1 c2,2 c2,3 c3,0 c3,1 c3,2 c3,3 a b c d m2 m1 e f g h k
Ligne de recovery après la panne de P0 et P1.

Q3Et si P3 tombe seul ?

P3 repart de c3,3. Mais g (P3→P2) est parti après, donc P2 recule avant sa réception : c2,2. Alors d (P2→P1) devient orphelin : P1 recule à c1,1. Puis c (P1→P0) fait reculer P0 à c0,1, b (P0→P2) fait reculer P2 à c2,1, et f (P2→P3) ramène P3 à c3,1. Ligne : {c0,1, c1,1, c2,1, c3,1}, presque au début. Une seule panne, pourtant plus de dégâts : P3 est au cœur de l'enchevêtrement de messages. C'est la loterie du non coordonné.

P0 P1 P2 P3 c0,0 c0,1 c0,2 c0,3 c1,0 c1,1 c1,2 c1,3 c2,0 c2,1 c2,2 c2,3 c3,0 c3,1 c3,2 c3,3 a b c d m2 m1 e f g h k
Si P3 tombe seul (X pointillé), la ligne recule presque au début.

SuiteÉviter aussi les messages en transit (slide 23)

Autre exécution, P1 tombe. Avec la règle habituelle (pas d'orphelin), P1 recule à son 2e checkpoint à cause de h puis e, P0 à son 2e, P2 et P3 gardent leur état : c'est la ligne verte. Mais le message d (P2→P1, en or) est alors en transit : envoyé avant la ligne, reçu après.

P0 P1 P2 P3 a b c d e f g h i
Ligne sans contrainte sur les en-transit : d la traverse vers l'avant.

Extension des règles : on ajoute l'arête inverse. Si un message est reçu dans un intervalle annulé, on annule aussi l'intervalle qui contient son envoi. Ici d oblige P2 à reculer avant de l'envoyer, donc à son checkpoint initial. Du coup c, f et g (P2→P3) deviennent orphelins et P3 recule aussi à son checkpoint initial. Nouvelle ligne : {2e checkpoint de P0, 2e de P1, initial de P2, initial de P3}. Plus aucun message ne la traverse, mais on perd beaucoup plus. D'où l'autre option : garder la première ligne et journaliser les messages en transit (c'est ce que fait Chandy-Lamport).

P0 P1 P2 P3 a b c d e f g h i
Nouvelle ligne quand les en-transit sont interdits : P2 et P3 reviennent au début.
Corrigé

Z-paths et Z-cycles

Lien entre Z-chemins et dépendances de checkpoints : pourquoi un checkpoint dans un Z-cycle est inutile ?

›

Un Z-path (zigzag path) enchaîne des intervalles de checkpoints reliés par des messages, en autorisant, sur un même processus, un message reçu avant qu'un autre soit envoyé dans le même intervalle. Un Z-cycle est un Z-path qui revient sur le même checkpoint (par exemple la chaîne m5 m3 m4 du cours).

P0 P1 P2 c0,0 c0,1 c0,2 c1,0 c1,1 c1,2 c1,3 c2,0 c2,1 c2,2 c2,3 m0 m1 m2 m3 m4 m5
Figure du cours. En or, le Z-cycle m5, m3, m4 : il part de c2,2 (m5 envoyé après) et y revient (m4 reçu avant). c2,2 est encadré en rouge.

Lecture du Z-cycle : m5 est envoyé par P2 après c2,2 et reçu par P0 avant c0,2. Dans le même intervalle de P0, m3 part vers P1 (avant la réception de m5 : c'est le « zig-zag », pas une chaîne causale). m3 arrive chez P1, qui envoie ensuite m4, reçu par P2 avant c2,2. On est revenu sur c2,2.

Pourquoi le checkpoint est inutile

Preuve sur l'exemple : supposons une coupe cohérente qui contient c2,2. m5 est envoyé après c2,2, donc sa réception doit être hors de la coupe : P0 doit y figurer avant m5, donc au plus c0,1. m3 est alors envoyé après la coupe de P0 : P1 doit y figurer avant de recevoir m3, donc au plus c1,1. m4 est envoyé après : P2 doit y figurer avant de recevoir m4, donc avant c2,2. Contradiction. Aucune coupe cohérente ne contient c2,2.

Un checkpoint pris dans un Z-cycle ne sert jamais à la reprise. Intuition : appartenir à un Z-cycle signifie qu'il existe une chaîne causale qui, en partant de ce checkpoint, y revient. Aucune coupe cohérente ne peut donc « passer par » ce checkpoint sans se contredire (il faudrait qu'il soit à la fois dans le passé et le futur d'une même chaîne). Dans le graphe de dépendances de rollback, il est toujours marqué dès qu'un de ses voisins l'est, il n'est donc jamais le « dernier non marqué » d'un processus. Conséquence pratique : les protocoles CIC sont conçus pour ne pas créer de checkpoint dans un Z-cycle (checkpoints forcés bien placés), ce qui garantit qu'aucun checkpoint n'est gaspillé. Ce n'est pas une preuve formelle d'équivalence, mais l'argument informel qui la sous-tend.

11 · Révision active

QCM et cartes mémo

Le QCM tire des questions au hasard et mélange les options à chaque partie. Les cartes mémo, pour l'auto-test rapide avant l'examen.

Prêt

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

Modèles de faute
Cite les grands types de fautes
clique pour la réponse ↻
Modèles de faute
Crash (fail-stop), omission (send/receive), timing (trop tard), response (valeur ou transition d'état fausse), arbitraire (byzantine). Du plus simple au plus dur à tolérer.
k-fault tolerant
Que veut dire k-fault tolerant ?
clique pour la réponse ↻
k-fault tolerant
Le système respecte sa spécification malgré la panne de k composants. Crash : souvent k+1 répliques. Byzantin : 2k+1 (voire 3k+1 avec accord).
Stockage stable
Pourquoi faut-il un stockage stable ?
clique pour la réponse ↻
Stockage stable
Checkpoints et logs doivent survivre à la panne visée. 1 panne : mémoire volatile d'un autre processus. Pannes non transitoires : médium persistant hors hôte (disque, FS répliqué).
Coupe cohérente
Quand une coupe est-elle cohérente ?
clique pour la réponse ↻
Coupe cohérente
Passé causalement clos : tout message reçu dans le passé a son envoi dans le passé. Jamais d'effet sans sa cause. Elle peut traverser des messages (en transit).
Orphelin
Message orphelin ?
clique pour la réponse ↻
Orphelin
Réception dans le passé, envoi dans le futur : reçu sans avoir été envoyé. Rend la coupe incohérente. À la reprise il serait reçu deux fois.
En transit
Message en transit ?
clique pour la réponse ↻
En transit
Envoi dans le passé, réception dans le futur. Coupe cohérente, mais si le canal est fiable il faut le stocker et le rejouer.
État recouvrable
Que doit permettre un état recouvrable ?
clique pour la réponse ↻
État recouvrable
Rejouer jusqu'à une coupe cohérente de l'exécution originale. Il suffit de reconstruire un état qui aurait pu exister sans panne.
Deux familles
Checkpointing vs message logging ?
clique pour la réponse ↻
Deux familles
Checkpointing : repartir d'une coupe cohérente, presque tout le monde rejoue. Logging : seuls les fautés rejouent depuis le log jusqu'au point de panne.
Effet domino
Effet domino ?
clique pour la réponse ↻
Effet domino
Non coordonné : un rollback crée un orphelin qui force un autre rollback, en cascade, parfois jusqu'au début. Défaut majeur du checkpointing non coordonné.
Ligne de recovery
Comment la calculer ?
clique pour la réponse ↻
Ligne de recovery
Graphe de dépendances de rollback : marquer l'état fautif, marquer par atteignabilité tout ce qui en dépend. Ligne = derniers nœuds non marqués de chaque processus.
Coordonné
Étapes du checkpointing coordonné bloquant ?
clique pour la réponse ↻
Coordonné
Initiateur checkpointe + diffuse la requête. Chacun : bloque, flush les canaux, checkpoint provisoire, ack. Après tous les acks : commit → permanent.
Coordonné
Avantage / inconvénient du coordonné ?
clique pour la réponse ↻
Coordonné
Toujours cohérent (pas de domino, 1 checkpoint/processus). Mais latence : tout le monde attend, communications bloquées.
Non bloquant
Éviter le blocage avec des canaux FIFO ?
clique pour la réponse ↻
Non bloquant
Propager la requête avant tout autre message, ou la piggybacker sur le premier message et checkpointer avant de le traiter. Aucun message ne franchit la coupe à l'envers.
CIC
Principe du CIC ?
clique pour la réponse ↻
CIC
Communication Induced Checkpointing : pas de message de contrôle, on piggyback de l'info (index) sur les messages. La réception peut forcer un checkpoint. Checkpoints locaux + forcés.
Briatico
Règle de Briatico (CIC indexé) ?
clique pour la réponse ↻
Briatico
Recevoir un index plus grand que le sien → checkpoint forcé avant de traiter le message. Ligne de recovery = tous les checkpoints de même index.
Chandy-Lamport
Réaction au 1er marqueur sur un canal ?
clique pour la réponse ↻
Chandy-Lamport
Enregistrer son état, marquer ce canal vide, rediffuser le marqueur. Les messages arrivant ensuite sur les autres canaux (avant leur marqueur) sont journalisés = état de canal.
Chandy-Lamport
Pourquoi FIFO est nécessaire ?
clique pour la réponse ↻
Chandy-Lamport
Le marqueur sépare proprement messages « d'avant » (inclure) et « d'après » (exclure). Sans FIFO, un message d'après pourrait doubler le marqueur → orphelin, snapshot incohérent.
PWD
Piecewise determinism ?
clique pour la réponse ↻
PWD
Entre deux événements non déterministes, l'exécution est déterministe. Il suffit de journaliser les déterminants des événements non déterministes pour rejouer à l'identique. Base du message logging.
Orphelin (logging)
Processus orphelin en message logging ?
clique pour la réponse ↻
Orphelin (logging)
Son état dépend d'un événement non déterministe dont le déterminant n'est pas journalisé : irrécupérable. Les protocoles « no-orphan » rendent stables ces déterminants.
Pessimiste
Logging pessimiste ?
clique pour la réponse ↻
Pessimiste
Loguer le déterminant avant d'exécuter. Jamais d'orphelin, reprise simple/rapide (seul le fauté redémarre), GC simple. Coût : synchronisation à chaque événement.
Optimiste
Logging optimiste ?
clique pour la réponse ↻
Optimiste
Déterminants gardés localement, flushés parfois. Meilleures perfs sans panne, mais suivre les dépendances causales à la reprise → risque de rollbacks exponentiels.
Réduire la pénalité
Alléger le logging synchrone ?
clique pour la réponse ↻
Réduire la pénalité
Matériel NVRAM, ou relâcher l'atomicité si la perte de messages est tolérée : loguer une réception avant d'émettre le suivant, plus besoin de log strictement synchrone.
Comparaison
Quelle famille à quelle échelle ?
clique pour la réponse ↻
Comparaison
Checkpointing : petit/moyen, surcoût faible. Message logging : grande échelle, surcoût moyen/élevé mais on ne relance que les fautés.
Z-cycle
Z-path et Z-cycle ?
clique pour la réponse ↻
Z-cycle
Z-path : zigzag intervalles + messages. Z-cycle : boucle sur le même checkpoint. Un checkpoint dans un Z-cycle est inutile à la reprise ; le CIC vise à ne pas en créer.
Quatre stratégies
Classe les 4 stratégies de checkpoint forcé
clique pour la réponse ↻
Quatre stratégies
Après envoi = meilleure (émission capturée, pas d'orphelin). Avant réception = utile. Après réception = peu utile. Avant envoi = pire (laisse l'émission dehors → orphelins).
PWD & familles
La PWD est-elle utile au checkpointing ?
clique pour la réponse ↻
PWD & familles
Non : le checkpointing restaure un état, il ne rejoue pas. La PWD est indispensable au message logging (qui reproduit l'exécution à l'identique).
Reprise ciblée
Éviter de renvoyer les en-transit aux non-fautés ?
clique pour la réponse ↻
Reprise ciblée
Approche receiver-based : le message est déjà dans le checkpoint du récepteur, traité à la reprise, pas de ré-émission. Sender-based renverrait tout.