• Aucun résultat trouvé

Chapitre V Une architecture flexible

V.1.2 Métaobjets de tolérance aux fautes

La partie centrale de cette architecture est, bien évidemment, la couche de tolérance aux fautes. Nous avons choisi d’illustrer le protocole à métaobjets et l’architecture décrite ci-dessus avec un mécanisme de réplication passive simplifié. Nous décri- vons ici sa conception et son implémentation.

La description de haut-niveau d’un algorithme de réplication passive peut sembler, au premier abord, simple. Pourtant cet algorithme est, dans la pratique, relativement complexe, notamment en ce qui concerne le traitement de la reconfiguration suite à une défaillance d’une des répliques. Pour la maîtrise de la conception de cet algo- rithme, nous avons choisi de le modéliser avec des Statecharts1, ce qui permet éga- lement de simuler son fonctionnement. Cette simulation s’est révélée très fructueuse puisque de nombreuses fautes de conception ont été découvertes et corri- gées lors de cette phase de la conception.

A un haut-niveau d’abstraction, quatre types d’évènements principaux sont à traiter par un tel algorithme :

• L’invocation d’une méthode du serveur,

• Le recouvrement suite à une défaillance d’une des répliques, • La réception d’une copie de l’état du primaire,

• Le clonage d’une nouvelle réplique.

Le traitement à effectuer suite à la réception d’un tel évènement varie en fonction de l’état actuel de la réplique considérée, si la réplique est primaire ou secondaire, si un recouvrement est en cours, etc.

Le but de cette section n’étant pas de détailler le mécanisme lui-même mais plutôt d’illustrer l’utilisation du protocole à métaobjet, nous ne donnerons qu’une vue relativement générale du mécanisme.

La figure 34 représente la modélisation abstraite de ce protocole en utilisant les Sta- techarts. Deux états représentent les fonctionnements en mode primaire ou secon- daire. Le fonctionnement mode primaire est relativement plus simple : lors du fonctionnement normal (en l’absence de faute), le métaobjet reçoit et répond à des invocations (invoke) ; lorsqu’une défaillance survient (recover), il clone une nou- 1. Les Statecharts [Harel 1996] sont des automates à état finis, hiérarchisés, concurrents

V.1.2- Métaobjets de tolérance aux fautes 121

velle réplique. En revanche, le mode de fonctionnement secondaire implique de stocker les invocations, de traiter les copies d’état reçues du primaire (checkpoint), de mettre à jour son état lors de la phase de clonage (cloning) et éventuellement d’activer une phase de recouvrement (recover) lors d’une défaillance du primaire.

Figure 34 - Modélisation Statecharts du métaobjet de

réplication passive

V.1.2.1 Traitement des invocations

Le traitement des invocations représente la majeure partie du traitement demandé à un serveur puisqu’il correspond au fonctionnement en l’absence de fautes. Dans le cadre de réplication passive, seul le primaire réalise effectivement les invocations. Les secondaires se contentent de mettre à jour leur état en fonction des informations reçues du primaire, ainsi que de stocker les invocations dans le cas d’une défaillance du primaire.

A chacune des invocations reçues, un identificateur unique est associé ; on consi- dère que pour chacune des répliques, l’ordre de réception est le même, propriété ici fournie par le service de groupe. Chacune des répliques associe donc le même iden- tificateur à chacune des invocations qu’elle reçoit. Côté secondaire, l’identificateur est stocké avec l’invocation et coté primaire, l’identificateur est envoyé avec l’état qui fait suite à l’invocation. Autrement dit, après avoir réalisé une invocation, le pri- maire obtient l’état du serveur et l’envoie aux répliques accompagné de la réponse faite au client et de l’identificateur de l’invocation. Ainsi, les secondaires peuvent purger l’invocation de leur file d’attente puisqu’elle a été traitée par le primaire. Ils conservent néanmoins la réponse faite par ce dernier au client, car, en cas de

PRIMARY IDLE @INVOKE @RECOVER INIT BACKUP ELECTION IDLE FILTERING @CHECKPOINTING @CLONING @RECOVER TO PRIMARY état de départ @ état hierarchique

défaillance du primaire, elle sera nécessaire1 afin de ne pas répéter une exécution de méthode déjà effectuée. Ils appliquent l’état reçu du primaire à leur objet. Lorsqu’il répond au client, le primaire renvoie également cet identificateur, ainsi le client pourra filtrer les doublons éventuels.

V.1.2.2 Défaillance d’un secondaire

Lorsqu’une réplique défaille, les autres répliques en sont averties par le biais d’un changement de vue du groupe auquel elles appartiennent car, dans ce cas, le service de groupe averti chacun des membres qu’un membre a quitté le groupe. Lorsque c’est une réplique secondaire qui défaille, le primaire doit reconfigurer le système pour qu’il retrouve un nombre de répliques correct. Pour ce faire, il demande à la fabrique d’objets de créer une nouvelle réplique sur le site sélectionné. Il envoie à cette nouvelle réplique une copie de son état accompagnée de l’identificateur de la prochaine invocation.

Lorsque sa création est terminée, la nouvelle réplique peut recevoir des invocations issues des clients avant de recevoir le message contenant l’état du primaire. Elle ne peut par contre assigner d’identificateur correct à ces invocations puisqu’elle ne connaît pas le passé du groupe de répliques, elle se contente donc de stocker ces invocations avec un identificateur temporaire. Lorsqu’elle reçoit l’état complet du primaire, elle peut re-calculer les identificateurs corrects des requêtes mises en attente à partir de l’information fournie par le primaire. Elle retrouve alors le statut de secondaire correctement initialisé.

V.1.2.3 Défaillance du primaire

Une défaillance du primaire est bien plus complexe à gérer que celle d’un simple secondaire. En effet, le processus qui mène un secondaire à se reconfigurer en pri- maire implique de nombreuses activités de reconfiguration.

Suite à la défaillance du primaire, les secondaires doivent tout d’abord élire un nou- veau primaire. Ce dernier doit créer un nouveau secondaire qui le remplacera et lui envoyer son état pour qu’il se reconfigure correctement. Afin de prévenir toute famine d’un client, le nouveau primaire ré-émet la réponse à la dernière invocation traitée ; il avait stocké cette réponse lorsqu’il a reçu le dernier état issu du précédent primaire. Cette ré-émission est nécessaire pour traiter le cas où la réponse n’aurait pas été correctement envoyée au client avant la défaillance du primaire. Ensuite, il traite toutes les invocations de sa file d’attente, celles-ci n’ont en effet pas été trai- tées par le primaire ayant défailli. En effet, dans la pratique, les requêtes reçues en tant que secondaire sont associées à des informations complémentaires ; le nouveau primaire doit donc traiter tout d’abord ces requêtes avant de ré-initialiser ses files d’attente et de jouer pleinement le rôle de primaire.

1. Comme nous nous le présentons en V.2.1.3, en cas de défaillance du primaire, la pré- cédente réponse à une invocation est systématiquement ré-émise.

V.1.2- Métaobjets de tolérance aux fautes 123

La figure 35 illustre ce processus. Tout d’abord le nouveau primaire obtient l’état de son objet de base (état Local_SaveState), puis il crée une nouvelle réplique secon- daire (via la fabrique d’objets) à laquelle il envoie son état (état Remote_Clone). La boucle qui commence à partir de l’état Handle_Pending_Queue correspond à la gestion des invocations en attente. Pour chacune des requêtes de sa file d’attente, le nouveau primaire effectue l’invocation sur son objet de base (Local_Invoke), copie son état (Local_SaveState_2) et envoie cet état à ses répliques secondaires (Remote_Checkpoint). Une seconde boucle commence à partir de l’état

Flush_Queue, son but est de vider la queue des messages qui ne seront plus néces-

saires. En effet, les secondaires utilisent certains messages, tels que les copies d’état du primaire, qui sont inutiles au primaire. Une fois la file d’attente vidée, la procé- dure de recouvrement est terminée et le primaire retrouve un fonctionnement nor- mal.

Figure 35 - Statechart-Recouvrement par un secondaire

V.1.2.4 Adéquation du protocole à métaobjets

On se rend bien compte sur cet exemple de réplication passive, que la mise en œuvre de mécanismes de réplication est une activité indépendante de l’application. A aucun moment de son cycle le mécanisme n’a besoin de savoir ce que réalise l’application, il suffit de pouvoir activer ses méthodes, obtenir ou restaurer son état, savoir la cloner, etc. Même si l’algorithme de ce mécanisme peut paraître complexe, il n’en est rien si on le compare à un même mécanisme intégré à l’application. La séparation des concepts permise par le protocole à métaobjets entre l’objet qui représente la fonction de l’application et le métaobjet, qui représente les propriétés non-fonctionnelles, est grandement simplificatrice. Toutes les informations néces-

RECOVER TO PRIMARY BEGIN HANDLE PENDING QUEUE REMOTE CHECKPOINT LOCAL SAVESTATE REMOTE CLONE LOCAL INVOKE LOCAL SAVESTATE_2 DONE FLUSH QUEUE REPEAT

saires à la réplication d’objets CORBA (qui sont disponibles sur une plate-forme COTS) sont fournies au métaobjet. Ce dernier dispose également des moyens d’acti-

vation nécessaires.

Dans l’exemple de mécanisme de tolérance aux fautes que nous avons présenté, nous n’avons pas traité le problème des invocations imbriquées qui se présente lorsqu’un serveur répliqué S1 est client d’un autre serveur répliqué S2. Dans ce cas, si la réplique primaire de S1 défaille juste après avoir invoqué une méthode de S2, le primaire S1 qui la remplace ne doit pas invoquer une seconde fois la méthode de

S2 sous peine de modifier son état de façon incohérente. Ce problème est difficile à

résoudre car l’invocation de S2 par S1 se fait en plein milieu d’une méthode de ce dernier, et qu’il est difficile de contrôler les actions internes à une méthode sans uti- liser un support d’exécution réflexif. Dans notre cas, on pourra utiliser un protocole spécifique entre métastubs et métaobjets pour éviter d’effectuer une invocation identique plusieurs fois tout en permettant d’obtenir à chaque fois la réponse cor- recte.

Finalement, et malgré ses limites dues à l’opacité de la plate-forme sous-jacente, ce protocole à métaobjets convient parfaitement à l’implémentation de mécanismes de tolérance aux fautes mais également, comme développé en V.1.3, permet bien d’autres utilisations liées à la sûreté de fonctionnement de manière plus générale.

V.1.2.5 Simulation et autres avantages de la modélisation

L’outil que nous avons utilisé pour la modélisation Statechart de ce mécanisme, Statemate®, permet la simulation des automates décrits. On peut grâce à cet outil, faire avancer la simulation pas-à-pas, transition par transition, observer les structu- res de données, files d’attente de messages entre autres. Ce processus de simulation nous a permis de valider, dans une certaine mesure, l’algorithme du mécanisme à travers son modèle. Même si cette simulation n’aide pas au test de l’implémentation correspondante, elle nous a permis d’augmenter la confiance que nous portons dans sa conception.

La simulation a, par exemple, mis en lumière certains dysfonctionnements de la gestion de la file des invocations lors d’un recouvrement de défaillance du primaire. Identifier cette faute de conception lors de l’implémentation aurait été bien plus dif- ficile du fait de la complexité des structures de données et du parallélisme du sys- tème.

La modélisation par Statecharts ne prend pas en compte la notion d’objets ou de métaobjets, elle s’attache au caractère fonctionnel du système représenté. Néan- moins, la communication par messages et par files d’attente facilite la modélisation des inter-actions entre objets et métaobjets. Cette description fonctionnelle peut, par la suite, être utilisée pour la conception UML de l’application. Ceci est d’autant plus vrai que les états de haut niveau de la figure 34 représentent les fonctions géné- rales du mécanisme et correspondent dans l’implémentation aux méthodes du métaobjet.

V.1.2- Métaobjets de tolérance aux fautes 125

V.1.2.6 Intégration des métaobjets dans l’architecture

Comme nous l’avons vu précédemment, le métaobjet de tolérance aux fautes prend place au dessus du serveur dans le premier métaniveau. Au-dessus de celui-ci se trouve un métaobjet de communication de groupe qui reçoit les messages du service de groupe et les transforme en invocation pour le métaobjet de tolérance aux fautes. Certaines de ces invocations n’engendrent pas de réponse, c’est le cas par exemple du métaobjet de réplication passive en mode secondaire qui ne traite pas les requê- tes mais se contente de les stocker. Le métaobjet de groupe ne sait a fortiori pas dans quel mode, primaire ou secondaire, son objet de base (le métaobjet de réplica- tion passive) se trouve puisqu’il est indépendant de son caractère fonctionnel. Le mode de fonctionnement, le fait de répondre ou de ne pas répondre, sont des déci- sions du ressort du métaobjet de tolérance aux fautes et dans un souci de séparation des responsabilités, il faut préserver cette indépendance. C’est pourquoi, pour répondre aux invocations du client, le métaobjet de tolérance aux fautes utilise un stub (qui sera réifié pour utiliser le service de groupe) et communique directement avec le client. Le métaobjet de tolérance aux fautes prend lui-même l’initiative de répondre ou de ne pas répondre et cette décision n’influe pas sur le métaobjet de communication de groupe.

L’architecture de métaniveau correspondant à ce choix de conception est illustrée figure 36. Dans cette architecture, le chemin pris par les invocations et leurs répon- ses peut paraître surprenant. En effet, l’invocation arrive au métaobjet de tolérance aux fautes par son propre métaobjet mais la réponse ne passe pas par celui-ci. La justification de ce choix de communication dissymétrique (la réponse ne suit pas le chemin de la requête) réside dans la logique de séparation des responsabilités des métaobjets. Une approche symétrique aurait conduit les deux métaobjets, de tolé- rance aux fautes et de groupe, à communiquer ou à partager de l’information afin de déterminer s’il fallait envoyer une réponse au client ou pas, réduisant à néant leur indépendance mutuelle. On notera également que le métaobjet de tolérance aux fau- tes accède au groupe de répliques à travers un stub réifié. En effet, on peut dire que le primaire est le client des secondaires : il invoque certaines de leurs méthodes, pour leur fournir son état notamment. Le stub S_MFT permet donc d’accéder à ceux-ci ; il est contrôlé par un métastub de gestion du protocole de groupe qui per- met, encore une fois, de rendre plus indépendants les différents métaniveaux.

Figure 36 - Architecture complète du métaniveau