• Aucun résultat trouvé

Description de la structure des métadonnées

Dans cette section nous présentons la structure de métadonnées. Nous décrivons d’abord le contenu des métadonnées avant de détailler leur implémentation.

4.3.1 Description et structure des métadonnées

Cette partie décrit les informations qui sont stockées dans le catalogue réparti et comment elles sont structurées.

Besoin de garder des informations multiples

Les métadonnées sont les informations relatives à la distribution des données et des transac-tions exécutées, autrement dit, les métadonnées contiennent les informatransac-tions sur les ND. Elles sont stockées dans le catalogue réparti à travers des nœuds appelés nœuds catalogue (NC). L’objectif de garder des métadonnées est de faciliter la recherche des ressources du système et particulièrement l’état des bases de données afin de router efficacement une transaction. Plus les informations utili-sées pour décrire les ressources du systèmes sont nombreuses plus la recherche d’une ressource sa-tisfaisant un certain nombre de critère est rapide, précise et fructueuse. Ainsi, pour chaque relation Ride la base de données, nous gardons plusieurs informations : (1) les identifiants de l’ensemble des ND qui stockent une copie de Ri; (2) pour chaque NDj stockant Ri, nous gardons l’état cou-rant de la base i.e. la liste des transactions coucou-rantes et/ou déjà exécutées sur NDj; (3) pour chaque ND, nous gardons son statut i.e. s’il est connecté ou déconnecté ; etc. Nous mentionnons que nous gardons dans le catalogue toutes les transactions en cours et celles qui sont déjà validées mais non encore propagées sur toutes les répliques. Le choix de garder qu’une partie des transactions déjà

exécutée a pour objectif de minimiser le volume des métadonnées. En effet un volume de méta-données très important peut entrainer des latences lors des accès . En outre, les transactions déjà exécutées sur un quelconque ND sont propagées périodiquement sur l’ensemble des ND distants et sont immédiatement éffacées du catalogue. Le catalogue réparti stocke aussi, pour chaque tran-saction T , le temps estimé pour exécuter T , qui est une moyenne variable obtenue par l’exécution des précédentes exécutions de T . Il est initialisé par une valeur par défaut en exécutant T sur un nœud non chargé. Cette mesure est indispensable pour effectuer le routage (voir chapitre 5). Besoin de fragmenter le catalogue

Comme nous l’avions mentionné ci-avant, les GT ont besoin des métadonnées pour assurer le traitement des transactions envoyées par les noeuds NA et contrôler la cohérence globale. De ce fait, le catalogue devient indispensable et doit être disponible d’autant plus que les GT peuvent simultanément solliciter l’accès aux métadonnées.

Pour garantir la disponibilité des métadonnées et leur utilisation simultanée par plusieurs GT, les métadonnées sont fragmentées et répliquées sur plusieurs sites. La fragmentation augmente les accès disjoints et favorise ainsi les accès parallèles, ce qui améliorer les performances du système, particulièrement le débit du routage. La fragmentation est faite de telle sorte que chaque fragment ne contient que les métadonnées relatives à une relation Ri (M eta(Ri). Ceci facilite la recherche par nom de relation et fournit des accès indépendants de chaque GSG(Ri). Pour trouver le graphe global (GSG(T )) relatif à la transaction T , il faut récupérer les GSG(Ri) tel que Ri ∈ Rel(T ) et donc GSG(T ) =S GSG(Ri)|Ri ∈ Rel(T ).

Cependant, la réplication peut entraîner quelques problèmes dans la gestion des métadonnées, notamment leur cohérence. Ainsi, il devient important de gérer les accès concurrents au catalogue de manière efficace afin de garantir le cohérence et de réduire la latence (cf. section 5.2).

4.3.2 Implémentation du catalogue

Pour implémenter les nœuds NC, nous avons utilisé des systèmes tiers qui fournissent des services de gestion et de stockage de données dans un environnement à grande échelle. En premier lieu, nous utilisons JuxMem [ABJ05] qui fournit un service transparent et cohérent de partage de données sur une grille informatique. En second lieu, nous utilisons une DHT pour un meilleur passage à l’échelle et une disponibilité plus importante du catalogue.

Architecture du catalogue avec un système à mémoire partagée : JuxMem

JuxMem fournit un service transparent et cohérent de partage de données sur une grille in-formatique. Il permet le partage d’un ensemble de blocs de données dans un système à mémoire partagé. Ainsi, il offre des primitives d’écriture et de lecture de données stockées sur ces blocs tout en garantissant leur cohérence et leur disponibilité. Par conséquent, pour stocker les informations du catalogue, le problème consiste à travers JuxMem de demander des blocs de mémoire de blocs nécessaire sans pour autant se soucier des endroits où le stockage physique se fera proprement fait. Cependant, comme les blocs de JuxMem sont de taille limitée, il faut éviter que les informations

d’un fragment de métadonnées se trouvent dans deux blocs, qui peuvent être éloignés et donc ra-lentir l’accès. C’est une motivation de plus à notre choix de ne pas garder dans le catalogue les transactions déjà propagées afin de réduire la taille des métadonnées.

Interfaces. JuxMem offre à travers une interface plusieurs primitives pour manipuler les données parmi lesquelles nous pouvons citer :

– alloc (size, attributes), une primitive qui permet d’obtenir un nouveau bloc d’une taille don-née (size) avec un degré de redondance et un protocole de contrôle de cohérence mutuelle spécifiés par le paramètre attributes.

– put (id, value) permet de modifier la valeur d’une donnée d’un bloc identifié par id. – get(id) permet de récupérer la valeur du bloc identifié par id.

– lock(id) et unlock(id) respectivement pour verrouiller et déverrouiller le bloc d’identifiant id.

FIGURE4.8 – Méthodes d’accès au catalogue avec JuxMem

Pour manipuler les métadonnées, les nœuds GT se comportent comme des clients dans l’ar-chitecture de JuxMem (voir figure 4.8). Par conséquent, un GT demande à JuxMem un nouveau bloc lors de la création d’un fragment de métadonnées, puis utilise la primitive put (id, value) pour insérer la structure Meta(R). Pour lire le contenu du Meta(R), il utilise la primitive get(id).

Accès concurrents avec JuxMem. La lecture et la modification des blocs avec Juxmem se fait via l’utilisation de verrous. Pour assurer la cohérence lors des routages, nous avons utilisé les pri-mitives de verrous de JuxMem pour reproduire le schéma classique de verrouillage à deux phases (2PL). En d’autres termes, un GT garde un verrou sur le bloc de données requis durant tout le processus de routage. Ceci ne dégrade pas les performances du système, puisque : (1) le processus de routage est très rapide comparé à l’exécution de la transaction et des opérations de rafraîchis-sement, et (2) le catalogue est découpé de tel sorte qu’un seul bloc est souvent sollicité par une transaction.

JuxMem permet d’obtenir de bonnes performances si le nombre de GT concurrents est faible. L’utilisation des verrous entraîne la dégradation des performances si le nombre de GT concur-rents est important puisqu’il faut attendre toujours le relâchement d’un verrou pour pouvoir traiter (router) une transaction. Dans un environnement à volatilité très importante la panne d’un noeud cause de sérieux problème à l’utilisation des verrous puisqu’un nœud détenant un verrou peut tom-ber à tout moment en panne. En outre, JuxMem cache les détails de stockage et de réplication des données. Ainsi, il devient impossible de modifier le protocole de réplication pour des besoins spécifiques de gestion du catalogue réparti.

Architecture du catalogue avec une DHT

Pour garantir que le catalogue réparti soit plus disponible et que son utilisation simultanée par plusieurs GT ne constitue pas une source de congestion, nous avons utilisé une DHT qui permet d’avoir des services d’indexation passant à l’échelle. La DHT distribue et réplique les métadonnées sur plusieurs noeuds NC pour assurer la disponibilité. Dans la suite de cette section, nous décrivons comment nos métadonnées sont intégrés dans une DHT.

Interfaces. Pour manipuler les métadonnées dans la DHT, nous avons besoin d’opérations de base pour les insérer, les retrouver et les modifier. Pour ce faire et compte tenu de notre contexte, nous avons d’une part les primitives natives offertes par la plupart des implémentations des DHT et d’autre part des primitives additionnelles développées pour nos propres besoins (cf. figure 4.9 ).

FIGURE4.9 – Méthodes d’accès au catalogue avec DHT

Méthodes d’accès avec les primitives natives des DHT. En général, une DHT offre deux pri-mitives d’opérations très usuelles : put(k, v) pour insérer une valeur v associée à une clé k, et get(k)

pour récupérer la valeur v associée à k. Dans notre cas, k est nom d’une relation R et v est la structure Meta(R). En outre, pour tolérer la panne ou la déconnexion des noeuds, la DHT réplique chaque couple (k, v) sur plusieurs nœuds. Une opération put(k, v) crée n répliques (k, vi)0 < i < n. La valeur de n est initialisée au moment de l’insertion de la valeur et en fonction du niveau de dis-ponibilité sollicité. En plus toute réplique peut être utilisée par la DHT sans distinction. La seule chose à assurer est qu’une opération get retourne toujours une des copies de la valeur associée à la clé utilisée. Ainsi, deux opérations get concurrentes peuvent trouver des répliques différentes gérées par deux nœuds distincts. La DHT ne prend pas en compte la détection d’opérations get concurrentes puisque les DHT sont conçues de tel sorte que les données qui y sont stockées ne soient accessibles qu’en lecture seule. C’est la raison pour laquelle il n’y a que deux situations dans lesquelles, les primitives natives des DHT peuvent être utilisées. Premièrement, quand une GT route une transaction de lecture seule, il utilise la primitive get(R) pour obtenir la structure Meta(R). Le GT ne modifie pas Meta(R), il le traverse uniquement dans l’objectif de calculer la séquence de rafraîchissement. Deuxièmement, quand une structure Meta(R) vient d’être créée, on utilise la primitive put pour l’insérer une première fois dans le catalogue réparti. Tout autre accès nécessite les primitives personnalisées ci dessous.

Personnalisation des méthodes d’accès d’une DHT Il existe deux cas dans lesquels nous avons besoin de primitives autre que celles proposées par une DHT et correspondent à des modifications du contenu du catalogue. En effet, quand un GT route une transaction, il lit le graphe de précédence avec l’intention de le modifier ( get_for_update), ainsi il a besoin d’être tenu informé des autres accès concurrents pour ne pas faire diverger les copies des métadonnées. A la fin de l’exécution d’une nouvelle transaction, le GT a besoin de marquer sur le catalogue que la transaction a été bien exécutée et pour ce faire il modifie le graphe de précédence et l’état du nœud sur la quelle la tran-saction est validée. Cette dernière opération est appelée metadata_update. L’implémentation des opérations metadata_update et get_for_update est basée simplement sur les primitives d’accès des DHT. De manière plus précis, nous modifions légèrement le protocole de réplication d’une DHT de tel sorte que tous les noeuds stockant une copie d’une même portion des métadonnées ne jouent pas le même rôle. Les noeuds qui stockent une copie du couple (k, v) sont appelés successeurs de la clé k. Ainsi, le premier successeur (nœud dont l’identifiant est le plus proche de la clé k) est appelé nœud NC maître et est utilisé pour le routage des transactions de mises à jour. Les autres successeurs sont appelés secondaires et sont utilisés pour le routage des transactions de lecture et donc sont accessibles avec les primitives d’opérations natives de la DHT. Le NC maître est utilisé pour mettre à jour les métadonnées et est donc responsable de la synchronisation avec les autres successeurs.

Accès concurrent avec une DHT. Pour gérer l’accès concurrent au catalogue lors du routage, nous ajoutons une entrée dans le catalogue appelée Last(R) . Last(R) est un pointeur sur le dernier GT qui a accédé à la structure Meta(R). Ceci permet d’ordonner les écritures des GT accédant simultanément la structure et donc de préserver la cohérence. Le choix de garder le dernier GT à accéder à la structure Meta(R) est guidé par notre principe de conception qui consiste à ne jamais utiliser de verrous durant l’accès aux métadonnées. Nous argumentons cela par le fait que les

mécanismes de verrous ne passent jamais à l’échelle notamment parce qu’un noeud détenant un verrou peut quitter le système et donc bloquer tous les autres nœuds qui souhaitent accéder aux même métadonnées. Cependant, nous avons besoin de contrôler l’accès simultané de plusieurs GT afin d’éviter des incohérences au niveau du graphe de précédence. En bref, Last(R) permet aux GT de reconstruire le graphe de précédence complet de manière cohérent. Nous donnerons plus de détails sur la gestion de la cohérence des métadonnées dans la section 5.2