7.14.2.4. Storage Engine¶
7.14.2.4.1. Présentation¶
Module embarquant la partie core du storage (client et server).
7.14.2.4.1.1. Services¶
De manière générale, pour le Storage, les méthodes utilisées sont les suivantes :
- GET : pour l’équivalent du « Select ».
- POST : sans X-Http-Method-Override: GET dans le Header, pour faire un insert.
- POST : avec X-Http-Method-Override: GET dans le Header, pour faire un select (avec Body).
- PUT : pour les mises à jour de Units et ObjectGroups.
- DELETE : pour effacer des métadonnées, des objects, des units, des journaux ou bien des containers.
- HEAD : pour les tests d’existence.
7.14.2.4.1.1.1. Rest API¶
7.14.2.4.1.1.1.1. URI d’appel¶
7.14.2.4.1.1.1.2. Headers¶
Plusieurs informations sont nécessaires dans la partie header :
- X-Strategy-Id : Stratégie pour Offres de stockage et Copies (conservation).
- X-Tenant-Id (obligatoire pour toute requête) : id du tenant. Cette information sera utilisée dans toutes les requêtes pour déterminer sur quel tenant se baser.
- X-Request-Id : l’identifiant unique de la requête.
- Accept : Permet de spécifier si un résultat doit contenir uniquement des métadonnées (“application/json”), un DIP complet (un ZIP contenant les métadonnées et les objets) ou seulement des Objects avec un contenu binaire (“application/octet-stream”).
- X-ObjectGroup-Id : Id de l’ObjectGroup
- X-Units : Ids des Units parents
- X-Caller-Id : Id du service demandeur
7.14.2.4.1.1.1.3. Méthodes¶
7.14.2.4.1.2. Distribution¶
Le distributeur (module distribution) est en charge de décider selon la stratégie de stockage dans quelles offres doit être stocké un objet binaire.
Avant tout, le moteur de stockage récupère le binaire sur le workspace et le démultplie via un tee autant de fois que de copies à réaliser. Pour chaque offre de stockage contenue dans la stratégie le distributeur demande au SPI DriverManager le driver associé. Le distributeur instancie alors pour chaque offre un nouveau thread qui va se charger du transfert vers chacune des offres. Dans chaque thread le driver associé à l’offre est utilisé pour le transfert.
Les thread font un retour OK ou KO. Pour chaque offre en KO, une nouvelle tentative de transfert est faite, jusqu’à trois tentatives. Si encore une offre est en KO après trois tentatives (retry), les binaires déposés sur les offres OK sont supprimés (rollback).
Le distributeur gère la mise à jour du journal des écritures du storage liée à l’opération de stockage d’un objet binaire dans une offre. Toutes les tentatives y sont répertoriées pour chaque offre.
D’un point de vue séquentiel :
- Lors d’un appel de type POST /objects/{id_object} pour stocker un nouvel objet, le service est appelé :
Il vérifie les paramètres d’entrée (nullité et cohérence simple)
Il récupère la stratégie associée à l’ID fourni
Regarde uniquement la partie « offres chaudes »
Récupère le fichier sur le workspace
Pour chaque offre chaude :
- Récupération du Driver associé s’il existe (sinon remontée d’une exception technique)
- Instancie un thread et dans ce trhead : 1. Récupération des paramètres de l’offre : url du service, paramètres additionels 2. Tentative de connection à l’offre et d’upload de l’objet 3. Comparaison du digest hash renvoyé par l’offre avec le digest calculé à la volée lors de l’envoi du stream à l’offre 4. Retour vers le distributeur du résultat (OK ou KO)
- Stockage du résultat de l’upload dans une map temporaire contenant le résultat de l’upload sur chaque offre
Pour chaque offre KO, un nouvelle tentative est faite (jusqu’à trois)
Si tout est OK, génération d’une réponse sérialisable, en mode “succès” si tous les drivers ont correctement stocker l’objet.
Si une offre au moins est KO, suppression des binaires sur les offres en succès et renvoie une exception
7.14.2.4.1.3. DriverManager : SPI¶
Les différents drivers sont chargés via le ServiceLoader de la JDK puis leurs instances sont stockées dans une liste. Cela permet ensuite de configurer les offres sur les différentes instances de driver en passant par une MAP dont la clef est l’identifant de l’offre, la valeur est le driver instancié dans la liste (une référence à ce driver donc, retrouvé par son nom (getName())).
Le distributeur va alors demander au DriverManager le driver correspondant à l’offre définie dans la stratégie afin de réaliser les opérations de stockage.
7.14.2.4.1.3.1. Principe¶
Le driver à ajouter doit implémenter l’interface définie. Dans son jar, il faut donc retrouver l’implémentation du driver ainsi que le fichier permettant au ServiceLoader de fonctionner. Ce fichier DOIT se trouver dans les resources, sous META-INF/services (principe du ServiceLoader de la JDK). Son nom est l’interface implémentée par le driver précédé de son package.
Exemple:
samples/fr.gouv.vitam.storage.driver.Driver
Où VitamDriver est l’interface implémentée.
Son contenu est le nom de la classe qui implémente l’interface (qui est le nom du fichier) précédé de son package.
Exemple:
mon.package.ou.se.trouve.mon.driver.VitameDriverImpl
Où VitamDriverImpl est l’implémentation du driver.
Voici le fichier : fr.gouv.vitam.storage.driver.Driver
Cependant, il faut pouvoir redémarrer Vitam sans perdre l’association driver / offre ou démarrer Vitam avec des drivers et des offres par défaut. Pour se faire, il faut persister la configuration.
7.14.2.4.1.3.2. Persistance¶
On s’appuie sur une interface offrant différentes méthodes afin de récupérer les offres à partir d’un nom de driver, persister la configuration… Cela permet demain de changer la stratégie de persistance sans avoir à modifier le code du SPI.
public interface DriverMapper {
List<String> getOffersFor(String driverName) throws StorageException;
void addOfferTo(String offerId, String driverName) throws StorageException;
void addOffersTo(List<String> offersIdsToAdd, String driverName) throws StorageException;
void removeOfferTo(String offerId, String driverName) throws StorageException;
void removeOffersTo(List<String> offersIdsToRemove, String driverName) throws StorageException;
}
Dans un premier temps, l’implémentation du mapper se fera en passant par un fichier. Dans son implémentation actuelle, le DriverMapper a besoin d’un fichier de configuration, driver-mapping.conf
. Ici, il permet de définir l’emplacement où seront enregistrés les fichiers permettant la persistance via la clef driverMappingPath. Une autre clef est nécessaire afin de définir le délimiteur dans ce fichier via la clef delimiter, le principe étant de mettre en place un fichier par driver comme un fichier CSV, les offres étant séparées par ce délimiteur.