• Aucun résultat trouvé

6.2 Groupes de recouvrement dans ProActive

6.2.2 Localisation inter-groupe

Le service de localisation au sein d’un groupe proposé dans la section 4.2.2.3 repose sur le fait que chaque activité peut accéder à la table de localisation, donc à la mémoire stable. Or dans le cadre de groupes de recouvrement, nous ne pouvons plus faire la supposition que les activités d’un groupe Grpi peuvent accéder à la mémoire stable d’un groupe Grpj. Le problème se pose donc lorsqu’une activité i du groupe Grpi tente de contacter une activité j du groupe Grpj qui a redémarré et a donc changé de localisation.

Nous proposons d’utiliser les activités du groupe Grpj lui-même comme inter-médiaires entre l’activité i qui recherche la nouvelle localisation et la table de localisation du groupe Grpj. Nous avons donc ajouté un mécanisme de messages non fonctionnels permettant à l’activité i de demander à une autre activité du groupe Grpj la dernière localisation connue de l’activité j. Ces messages non fonc-tionnels permettent aux méta objetsFTManagerde communiquer sans passer par le mécanisme de communication propre de l’activité, et ne perturbent donc pas

6.2 Groupes de recouvrement dans ProActive 151

l’activité.

Pour que ce système de délégation fonctionne, il faut que l’activité i du groupe Grpi connaisse d’autres activités du groupe Grpj que j. En effet, dans le cas in-verse, i ne peut plus contacter aucune activité du groupe Grpj, et ne peut donc plus retrouver la localisation de j.

Il faut donc un système pour gérer les références inter-groupes : si une acti-vité acquiert une référence vers une actiacti-vité d’un autre groupe, il faut obtenir et maintenir des points d’entrée vers ce groupe, c’est-à-dire des références vers des activités de ce groupe. Dans notre exemple, le fait que i ait une référence sur j implique que le groupe Grpipossède et maintient un ensemble de points d’entrée vers le groupe Grpj. En pratique, une table de localisation pour le groupe Grpj, ou table d’entrées vers Grpj notée TGrpi→Grpj, est créée sur la mémoire stable du groupe Grpi au moment où une activité de Grpi acquiert une référence vers une activité du groupe Grpj.

Grâce au mécanisme de communication non fonctionnelle entre les activités (plus précisément entre leurs méta-objetsFTManagerrespectifs), chaque activité peut :

− fournir une table d’entrées vers son groupe de recouvrement à une autre activité,

− fournir la dernière localisation connue d’une activité appartenant à son groupe de recouvrement.

6.2.2.1 Création d’une table d’entrées

Les références étant réifiées sous forme de couple stub-proxy dans ProActive, nous avons ajouté un mécanisme de détection des références entrantes dans un groupe, en interceptant la désérialisation des couples stub-proxy. Lorsqu’une ré-férence vers une activité j est reçue, et donc désérialisée par une activité i du groupe Grpi, le mécanisme de création de la table d’entrées est déclenché : si l’ac-tivité j n’est dans aucune table sur la mémoire stable, alors un appel est fait sur cette référence pour connaître le Grpj. Soit

− il existe une table d’entrées TGrpi→Grpj vers le groupe Grpj sur la mémoire stable du groupe Grpi, alors la référence vers l’activité j est ajoutée à cette table,

− il n’existe pas de table d’entrées TGrpi→Grpj vers le groupe Grpj, alors une demande de table d’entrées est faite sur cette référence. Le groupe Grpi

obtient une table d’entrées vers le groupe Grpj. Cette table est sauvegardée sur la mémoire stable du groupe Grpi.

6.2.2.2 Maintenance d’une table d’entrées

L’activité qui a reçu la première référence vers le groupe distant est chargée de maintenir la table d’entrées au moyen d’un système de messages de vie

(heart-beat messages). Encore une fois, nous avons ajouté un mécanisme non fonctionnel

qui ne perturbe pas l’activité concernée ; on utilise une thread dédiée qui est créée et associée à l’activité. En particulier, cette thread ne fait pas partie de l’état de

l’activité. Une variable du méta objet FTManagerindique si l’activité est chargée ou non de la maintenance d’une table de localisation de groupe distant. Ainsi, en cas de reprise, l’activité peut recréer cette thread dédiée.

L’activité (sa thread dédiée) doit régulièrement vérifier la validité des réfé-rences dans la table de localisation de groupe qui lui est affectée. Plutôt que de toutes les vérifier une par une, nous avons optimisé le système de message de vie de la façon suivante. Nous avons ajouté à la table de localisation d’un groupe un numéro de version qui est incrémenté à chaque fois qu’une localisation est modi-fiée dans la table1. Ainsi, lorsque l’activité envoie un message de vie pour tester une référence, elle ajoute à ce message le dernier numéro de version connu de la table d’entrée. Si ce numéro de version est identique, alors le résultat de cet appel est vide, et la table d’entrées est considérée comme à jour ; les autres références ne sont pas testées. Si le numéro de version est inférieur, alors le résultat de cet appel est une table qui contient les changements de localisation qui ont eu lieu entre le numéro de version inférieur et le numéro de version actuel.

Ce mécanisme permet de minimiser le nombre de communications inter-groupes pour maintenir les tables d’entrées vers les groupes distants à jour.

6.2.3 Validation d’état global

Le mécanisme de validation d’état global assure, lorsqu’un message inter-groupe est envoyé, qu’en cas de panne et de reprise du inter-groupe émetteur, ce mes-sage sera réémis avec le même contenu que dans l’exécution de référence. Ce pro-blème d’équivalence du contenu est similaire au propro-blème posé par les messages orphelins présenté dans la section 4.1.2 ; nous utilisons donc une solution simi-laire, l’historique de réception des requêtes. En assurant l’ordre de réception des requêtes intra-groupes, nous allons assurer l’équivalence du contenu des mes-sages inter-groupes dupliqués.

La solution que nous proposons est de stocker sur la mémoire stable l’ensemble des historiques de réception du groupe, ou historique global, à chaque fois qu’un message à destination d’un autre groupe de recouvrement est émis. Chaque histo-rique constituant l’histohisto-rique global est associé au point de reprise correspondant déjà stocké sur la mémoire stable. C’est ce qui est fait au moment de la clôture de l’historique dans le protocole interne. Ainsi, comme dans le cas des messages or-phelins précédemment, le passé de ces messages est rejoué en cas de réexécution et le duplicata du message est assuré d’être équivalent à l’original.

6.2.3.1 Capture de l’historique global

La technique utilisée dans la section 4.1.4 pour identifier et stocker l’histo-rique global est asynchrone ; des messages spécifiques sont envoyés à toutes les

1Ce numéro de version ne correspond pas forcément au numéro d’incarnation car une seule panne peut entraîner plusieurs changements de localisation si des activités étaient coallouées.

6.2 Groupes de recouvrement dans ProActive 153

activités qui déclenchent la clôture et le stockage de l’historique à la réception de ce message. Cette approche implique donc une très grande latence sur les envois des messages inter-groupes ; il faut que l’émetteur attende que toutes les activités aient stocké leur historique courant sur la mémoire stable. Le temps de latence sur l’envoi d’un message inter-groupe n’est donc pas borné et dépend fortement du nombre d’activités dans le groupe.

Or, dans le contexte d’une application communicante déployée sur une grille de calcul, nous pouvons supposer que le nombre de messages inter-groupes est minimisé par rapport au nombre total de messages, de par la structure de l’ap-plication. Pour autant, les messages inter-groupes ne peuvent pas être considérés comme rares durant l’exécution. Si on prend le cas par exemple d’applications SPMD comme le noyau CG ou Jacobi présentées dans la section 4.3, le nombre de communications entre groupes est au moins de l’ordre du nombre d’itérations de l’application.

Nous proposons donc pour minimiser le temps de latence sur l’envoi d’un mes-sage inter-groupe de maintenir sur chaque activité une estimation suffisante de l’historique global. De cette manière, la validation d’état global peut être faite de manière indépendante par une activité : elle doit simplement stocker sur la mémoire stable sa vue locale de l’historique global avant d’envoyer un message inter-groupe.

La difficulté dans ce cas est de maintenir sur chaque activité une vue locale suffisante de l’historique global courant. Nous allons pour cela utiliser une solu-tion inspirée de certains protocoles de tolérance aux pannes par journalisasolu-tion causale tels que Manetho [ELN 92b]. Dans Manetho, chaque message porte avec lui son graphe d’antécédence, c’est à dire le graphe de dépendance causale de tous les évènements non déterministes qui ont été exécutés avant l’envoi de ce message. Ce graphe d’antécédence permet à l’activité réceptrice de maintenir une vue du passé causal de l’application sous forme d’un graphe d’antécédence global. Ainsi, lorsqu’une validation d’état global doit être faite, l’activité doit simplement stocker ce graphe sur la mémoire stable.

Dans notre cas, l’historique de réception des messages est utilisé pour repré-senter le passé causal d’un message ; chaque message doit donc porter l’ensemble des historiques de réception qui précèdent causalement son envoi. Ainsi, lors-qu’un message est reçu par une activité, celle-ci peut mettre à jour sa vue locale de l’historique global.

6.2.3.2 Maintenance de la vue locale de l’historique global

Pour maintenir la vue locale de l’historique global, nous utilisons une tech-nique proche des vecteurs d’horloges (vector clock) [BAL 02]. Chaque activité i maintient une table d’historiques, noté Hi, qui représente la vue de i de l’histo-rique global : Hi[j] est l’historique de j supposé par i. En particulier, Hi[i] repré-sente l’historique de i et est donc toujours exact.

réception, chaque historique Hi[j] doit être indexé de façon absolue : quand un élé-ment est ajouté dans un historique, son index, c’est-à-dire son rang dans l’histo-rique, doit rester constant même si d’autres éléments de l’historique sont effacés. En effet, pour minimiser la taille des vues locales, lorsqu’une activité i stocke en mémoire stable Hi, elle efface le contenu de Hi (sauf le dernier élément de chaque historique, de manière à conserver l’index du dernier élément stocké en mémoire stable). Ainsi, lorsque l’activité i propage par la suite Hi sur les messages de l’ap-plication, elle indique aux récepteurs les éléments de l’historique global qui sont sauvegardés sur la mémoire stable, et qui n’ont donc plus besoin d’être mainte-nus : ces éléments peuvent être effacés des vues locales des récepteurs.

Finalement, lorsqu’un message Mi,j est envoyé par une activité i, le vecteur Hi est ajouté au message. Lorsque ce message est reçu, l’activité j met à jour sa vue locale de l’historique global Hj en fonction de la vue locale de i, à la manière des vecteurs d’horloges. Cette mise à jour a deux fonctions :

− elle ajoute les éléments manquants à la fin de chaque historique, de manière à propager la progression de l’historique global ;

− elle efface aussi les éléments au début de chaque historique devenus in-utiles, de manière à minimiser la taille des vues locales, et ainsi réduire la taille des informations à ajouter sur les messages.

Hj[k] Hi[k] Hi[k] n n + 3 n + 3 n + 3 n + 7 n + 7 n + 12 n + 12

FIG. 6.2 – Mise à jour des vues locales de i

La figure 6.2 résume le mécanisme de mise à jour : l’activité i met à jour sa vue locale de l’historique de réception de l’activité k en fonction de la vue locale d’une activité j. La partie antérieure à la partie commune en gris peut être effacée, et la partie postérieure à la partie commune doit être ajoutée. On note que l’index des éléments est absolu : le premier élément de Hi[k] après la mise à jour porte l’index n + 3.

6.2.3.3 Conservation de l’ordonnancement causal des réceptions entre les groupes

Nous montrons ici que la sauvegarde de l’historique global est aussi néces-saire sur réception d’un message inter-groupe. Prenons le cas de l’exemple de la figure 6.3 : les activités i et j,k forment deux groupes de recouvrement. Comme on peut le voir, les réceptions de Q0 et de Q2 sont causalement liées : Q0 doit être

6.2 Groupes de recouvrement dans ProActive 155

reçue avant Q2. Or, si le groupe formé de j et k doit recouvrir depuis l’état global n, le message Q2 est renvoyé artificiellement par le mécanisme de tolérance aux pannes, et n’est donc plus synchronisé avec la réception de Q0. Il faut donc que les réceptions de ces deux messages fassent partie de l’historique de réception utilisé pendant la réexécution. Par conséquent, l’activité j doit sauvegarder son histo-rique sur la mémoire stable au moment de la réception du message inter-groupe Q2. Enfin, comme l’historique global doit constituer une coupe cohérente de l’exé-cution de référence, j ne peut pas sauvegarder seulement son historique locale ; il doit sauvegarder sa vue locale Hj de l’historique global.

i Q0 j Cn k Q1 Q2 k Cn j X(Q1)

FIG. 6.3 – La réception de Q0doit précéder celle de Q2 en cas de reprise depuis n

6.2.3.4 Cohabitation des deux protocoles

Le protocole de maintenance de l’historique global ne rentre pas en conflit avec le protocole interne. Il suppose que les historiques de réception de requête sont maintenus par les activités tout le long de l’exécution. La clôture d’historique du protocole interne sur réception du message Mgs

n déclenche encore sur chaque activité une sauvegarde sur la mémoire stable de l’historique local avec le dernier point de reprise (section 4.1.4) ; la différence est que même après cette clôture, l’activité continue à maintenir son historique local. Ainsi, l’historique global peut être maintenu durant toute l’exécution.

Lors d’une validation d’état global, comme lors de la clôture du protocole in-terne, les historiques de réceptions constituant l’historique global doivent être associés aux points de reprises formant la dernière ligne de recouvrement ; ils doivent pouvoir être ajoutés à la fin de la queue des requêtes en attente en cas de reprise depuis cette ligne (section 4.1.5).

Dans le cas de la clôture du protocole interne, chaque historique local est sys-tématiquement associé au dernier point de reprise pris par l’activité. En effet, c’est cette clôture qui rend le dernier état global formé recouvrable, c’est-à-dire

qui transforme le dernier état global en ligne de recouvrement.

Dans le cas d’une validation d’état global, chaque historique local constituant l’historique global doit être associé à tous les derniers points de reprise pris par l’activité jusqu’à celui qui fait partie de la dernière ligne de recouvrement. En effet, le dernier point de reprise de chaque activité ne fait pas forcément partie de la dernière ligne de recouvrement. Notons Nrec l’index de la dernière ligne de recouvrement stockée en mémoire stable. Lorsqu’une activité a pris un point de reprise n mais que la dernière ligne de recouvrement est Nrec < n, le point de re-prise n ne peut pas encore être utilisé pour redémarrer l’activité en cas de panne. Or il faut que l’état global soit validé même en cas de reprise depuis l’état global Nrec < n. Par conséquent, lorsqu’une activité valide l’état global, il faut qu’elle stocke chaque historique local constituant l’historique global avec tous points de reprise n déjà stockés sur la mémoire stable tels que n > Nrec, ainsi qu’avec les points de reprise Nrec.