Optimisation du mode de copie

WebSphere eXtreme Scale effectue une copie de la valeur en fonction des paramètres CopyMode disponibles. C'est à vous de déterminer lequel de ces paramétrages fonctionne le mieux pour les besoins de votre déploiement.

Vous pouvez utiliser la méthode setCopyMode(CopyMode, valueInterfaceClass) de l'API BackingMap pour définir le mode de copie dans l'une des zones statiques finales suivantes définies dans la classe com.ibm.websphere.objectgrid.CopyMode.

Lorsqu'une application utilise l'interface ObjectMap pour obtenir une référence à une entrée de mappe, n'utilisez cette référence que dans la transaction de grille de données qu'a obtenue cette référence Son utilisation dans un autre transaction peut provoquer des erreurs. Si, par exemple, vous utilisez la stratégie de verrouillage pessimiste pour la mappe de sauvegarde, un appel à la méthode get ou à la méthode getForUpdate acquerra un verrou S (shared) ou U (update), selon la transaction. La méthode get retournera la référence à la valeur et le verrou obtenu sera libéré au terme de la transaction. La transaction doit appeler les méthodes get ou getForUpdate pour verrouiller l'entrée de mappe dans une autre transaction. Chaque transaction doit obtenir sa propre référence à la valeur en appelant les méthodes get ou getForUpdate au lieu de réutiliser la même référence dans plusieurs transactions.

CopyMode pour les mappes d'entités

Lorsque vous utilisez une mappe qui est associée à une entité d'API EntityManager, la mappe retourne toujours directement sans copie les objets Tuple d'entité sans procéder à une copie, sauf si vous utilisez le mode de copie COPY_TO_BYTES. Il est donc important de modifier CopyMode, faute de quoi le tuple ne serait pas copié de manière appropriée en cas de changement.

COPY_ON_READ_AND_COMMIT

Le mode COPY_ON_READ_AND_COMMIT est le mode par défaut. L'argument valueInterfaceClass est ignoré lorsque ce mode est utilisé. Ce mode s'assure qu'une application ne contient pas de référence à l'objet value qui se trouve dans la mappe de sauvegarde. A la place, l'application utilise toujours une copie de la valeur qui se trouve dans l'instance BackingMap. Le mode COPY_ON_READ_AND_COMMIT garantit que l'application ne pourra jamais endommager par inadvertance les données qui sont mises en cache dans la mappe de sauvegarde. Lorsqu'une transaction d'application appelle une méthode ObjectMap.get pour une clé donnée et qu'il s'agit du premier accès à l'entrée ObjectMap de cette clé, c'est une copie de la valeur qui est retournée. Lorsque la transaction est validée, toutes les modifications validées par l'application sont alors copiées vers la mappe de sauvegarde pour garantir que l'application ne dispose d'aucune référence à la valeur validée dans la mappe.

COPY_ON_READ

Par rapport au mode COPY_ON_READ_AND_COMMIT, le mode COPY_ON_READ donne de meilleures performances car il élimine la copie qui est effectuée lors de la validation des transactions. L'argument valueInterfaceClass est ignoré lorsque ce mode est utilisé. Pour conserver l'intégrité des données de la mappe de sauvegarde, l'application s'assure que toutes les références à une entrée sont supprimées une fois la transaction validée. Dans ce mode, la méthode ObjectMap.get retourne une copie de la valeur au lieu d'une référence à celle-ci afin de garantir que les modifications de la valeur opérées par l'application n'affecteront pas la valeur dans la mappe tant que la transaction n'est pas validée. Mais la différence est que, lors de la validation, il n'est effectué aucune copie des modifications. Ce qui est stocké dans la mappe de sauvegarde, c'est la référence à la copie, celle qui a été retournée par la méthode ObjectMap.get. Une fois la transaction validée, l'application détruit toutes les références aux entrées de la mappe. Si l'application ne détruit pas les références, les données mises en cache dans la mappe de sauvegarde risquent d'être endommagées. Si une application utilise ce mode et rencontre des problèmes, passez en mode COPY_ON_READ_AND_COMMIT pour voir si le problème persiste. S'il disparaît, c'est que l'application ne parvient pas à détruire toutes ses références après que la transaction a été validée.

COPY_ON_WRITE

Par rapport au mode COPY_ON_READ_AND_COMMIT, le mode COPY_ON_WRITE donne lui aussi de meilleures performances car il élimine la copie qui est effectuée lorsque la méthode ObjectMap.get est appelée pour la première fois par une transaction pour une clé donnée. La méthode ObjectMap.get retourne un proxy de la valeur au lieu d'une référence directe à l'objet value. Le proxy garantit qu'aucune copie de la valeur n'est effectuée tant que l'application n'appelle pas de méthode set sur l'interface Value qui est spécifiée comme argument valueInterfaceClass. Le proxy fournit une copie lors de l'écriture. Lors de la validation d'une transaction, la mappe de sauvegarde examine le proxy pour déterminer si une copie a été effectuée en tant que résultat de l'appel à une méthode set. Si c'est le cas, la référence à cette copie est stockée dans la mappe de sauvegarde. Ce mode présente donc un énorme avantage : une valeur n'est jamais copiée lors d'une lecture ou lors d'une validation lorsque la transaction ne fait jamais appel à une méthode set pour modifier la valeur.

Les modes COPY_ON_READ_AND_COMMIT et COPY_ON_READ procèdent tous les deux à une copie complète lorsqu'une valeur est extraite de la mappe d'objet. Ces modes ne sont pas optimaux si une application ne modifie que certaines des valeurs qui sont extraites dans une transaction. A cet égard, le mode COPY_ON_WRITE est beaucoup plus efficace, mais il requiert que l'application utilise un pattern simple. Les objets value sont tenus de prendre en charge une interface. L'application doit utiliser les méthodes de cette interface lorsqu'elle interagit avec la valeur dans une session. Si c'est le cas, des proxys sont créés pour les valeurs qui sont retournées à l'application. Le proxy a une référence à la valeur réelle. Si l'application n'effectue que des opérations de lecture, ces dernière s'exécutent toujours sur la copie réelle. Si l'application modifie un attribut dans l'objet, le proxy commence par créer une copie de l'objet réel, puis il effectue la modification dans cette copie. A partir de ce stade, le proxy n'intervient que sur cette copie. L'utilisation de la copie permet à l'opération de copie d'être totalement évitée pour les objets qui ne sont que lus par l'application. Toutes les opérations de modification doivent commencer par le préfixe set. Les JavaBeans Enterprise sont normalement programmés pour nommer de la sorte les méthodes qui modifient les attributs des objets. Il convient donc de respecter cette convention. Tous les objets qui sont modifiés sont copiés au moment même où ils sont modifiés par l'application. Ce scénario de lecture-écriture est le scénario le plus efficace pris en charge par eXtreme Scale. Pour configurer une mappe afin qu'elle utilise le mode COPY_ON_WRITE, utilisez l'exemple qui suit. Dans cet exemple, l'application stocke des objets Person qui sont indexés à l'aide du nom (name) présent dans la mappe. L'objet Person est représenté dans le fragment de code qui suit.

class Person {
    String name;
    int age;
    public Person() {
    }
    public void setName(String n) 	{
        name = n;
    }
    public String getName() {
        return name;
    }
    public void setAge(int a) {	
        age = a;
    }
    public int getAge() {
        return age;
    }
}

L'application n'utilise l'interface IPerson que lorsqu'elle interagit avec des valeurs qui sont extraites d'un ObjectMap. Modifiez l'objet pour qu'il utilise une interface comme dans l'exemple qui suit.

interface IPerson
{
    void setName(String n);
    String getName();
    void setAge(int a);
    int getAge();
}
// On modifie Person pour implémenter l'interface IPerson
class Person implements IPerson {
    ...
}

L'application a alors besoin de configurer la mappe de sauvegarde pour que celle-ci utilise le mode COPY_ON_WRITE, comme dans l'exemple qui suit :
ObjectGrid dg = ...;
BackingMap bm = dg.defineMap("PERSON");
// On utilise COPY_ON_WRITE pour cette mappe
// avec IPerson comme classe valueProxyInfo
bm.setCopyMode(CopyMode.COPY_ON_WRITE,IPerson.class);
// L'application doit alors utiliser
// le pattern suivant lorsqu'on utilise la mappe PERSON.
Session sess = ...;
ObjectMap person = sess.getMap("PERSON");
...
sess.begin();
// l'application transtype en IPerson et non en Person la valeur retournée
IPerson p = (IPerson)person.get("Billy");
p.setAge(p.getAge()+1);
...
// on fabrique un nouveau Person et on l'ajoute à Map
Person p1 = new Person();
p1.setName("Bobby");
p1.setAge(12);
person.insert(p1.getName(), p1);
sess.commit();
// le fragment suivant NE FONCTIONNERA PAS. Il se traduira par une ClassCastException
sess.begin();
// l'erreur ici est que c'est Person qui est utilisé
// et non IPerson
Person a = (Person)person.get("Bobby");
sess.commit();
La première section de l'application extrait une valeur appelée Billy dans la mappe. L'application transtype la valeur renvoyée en objet IPerson, et non en objet Person car le proxy renvoyé implémente deux interfaces :
  • L'interface spécifiée dans l'appel de méthode BackingMap.setCopyMode
  • L'interface com.ibm.websphere.objectgrid.ValueProxyInfo

Vous pouvez transtyper le proxy en deux types. La dernière partie du fragment de code montre ce qui n'est pas autorisé en mode COPY_ON_WRITE. L'application extrait l'enregistrement Bobby et essaie de le transtyper en objet Person. Cette action échoue avec une exception de transtypage de classe car le proxy qui est retourné n'est pas un objet Person. Le proxy retourné implémente en effet l'objet IPerson et l'interface ValueProxyInfo.

L'interface ValueProxyInfo et prise en charge des actualisations partielles : cette interface autorise une application à extraire soit la valeur en lecture seule validée qui est référencée par le proxy, soit l'ensemble des attributs qui ont été modifiés au cours de cette transaction.

public interface ValueProxyInfo {
    List /**/ ibmGetDirtyAttributes();
    Object ibmGetRealValue();
}

La méthode ibmGetRealValue renvoie une copie en lecture seule de l'objet. L'application ne doit pas modifier cette valeur. La méthode ibmGetDirtyAttributes renvoie une liste de chaînes représentant les attributs qui ont été modifiés par l'application au cours de cette transaction. Le cas d'utilisation principal de la méthode ibmGetDirtyAttributes s'applique dans Java Database Connectivity (JDBC) ou un chargeur CMP. Les attributs mentionnés dans la liste sont les seuls qui ont besoin d'être actualisés, que ce soit dans l'instruction SQL ou dans l'objet mappé à la table. Cette pratique apporte une plus grande efficacité au SQL généré par le chargeur. Lorsqu'une transaction copy on write est validée et si un chargeur est connecté, ce dernier peut transtyper vers l'interface ValueProxyInfo les valeurs des objets modifiés pour obtenir ces informations.

Gestion de la méthode equals lors de l'utilisation de COPY_ON_WRITE ou de proxys : le code suivant construit un objet Person qu'il insère ensuite dans un ObjectMap. Ensuite, il extrait ce même objet à l'aide de la méthode ObjectMap.get. La valeur est transtypée vers l'interface. Si la valeur est transtypée vers l'interface Person, une exception ClassCastException est générée car la valeur retournée est un proxy qui implémente l'interface IPerson et ce n'est pas un objet Person. La vérification d'égalité échoue car on utilise l'opération == alors qu'il ne s'agit pas du même objet.

session.begin();
// new sur l'objet Person
Person p = new Person(...);
personMap.insert(p.getName, p);
// On l'extrait à nouveau (pensez à utiliser l'interface pour le transtypage)
IPerson p2 = personMap.get(p.getName());
if(p2 == p) {
    // ils sont identiques
} else {
    // ils ne le sont pas
}

Autre point à prendre en considération : lorsqu'on doit remplacer la méthode equals. La méthode equals doit vérifier que l'argument est un objet qui implémente l'interface IPerson et elle doit transtyper l'argument en objet IPerson. Comme l'argument peut très bien être un proxy qui implémente l'interface IPerson, vous devez utiliser les méthodes getAge et getName lorsque vous comparez l'égalité des variables d'instance. Examinez l'exemple suivant :

{
    if ( obj == null ) return false;
    if ( obj instanceof IPerson ) {
        IPerson x = (IPerson) obj;
        return ( age.equals( x.getAge() ) && name.equals( x.getName() ) )
    }
    return false;
}

Configurations requises pour ObjectQuery et HashIndex : lorsqu'on utilise COPY_ON_WRITE avec le moteur de requête ObjectQuery ou avec un plug-in HashIndex, il est important de configurer le schéma ObjectQuery et le plug-in HashIndex pour que ces derniers puissent accéder aux objets à l'aide des méthodes de propriétés, ce qui est l'accès par défaut. S'ils sont configurés pour utiliser un accès aux zones, le moteur de requête et l'index tenteront d'accéder aux zones de l'objet proxy, ce qui retournera toujours une valeur null ou 0 puisque l'instance d'objet est un proxy.

NO_COPY

Le mode NO_COPY permet à une application d'améliorer ses performances, mais il exige que cette application ne modifie jamais l'objet de valeur obtenu à l'aide d'une méthode ObjectMap.get. L'argument valueInterfaceClass est ignoré lorsque ce mode est utilisé. Si ce mode est utilisé, il n'est jamais effectué de copie de la valeur. Si l'application modifie des instances d'objet de valeur qui sont extraites de ObjectMap ou qui lui sont ajoutées, les données de BackingMap sont endommagées. Le mode NO_COPY est essentiellement utile pour les mappes en lecture seule où les données ne sont jamais modifiées par l'application. Si l'application utilise ce mode et rencontre des problèmes, passez en mode COPY_ON_READ_AND_COMMIT pour voir si le problème persiste. S'il disparaît, c'est que l'application modifie la valeur retournée par la méthode ObjectMap.get, soit pendant la transaction, soit après que celle-ci a été validée. Toutes les mappes associées aux entités de l'API EntityManager utilisent automatiquement ce mode sans tenir compte de ce qui est spécifié dans la configuration d'eXtreme Scale.

Toutes les mappes associées aux entités de l'API EntityManager utilisent automatiquement ce mode sans tenir compte de ce qui est spécifié dans la configuration d'eXtreme Scale.

COPY_TO_BYTES

Il est possible de stocker des objets dans un format sérialisé au lieu du format POJO. En utilisant le mode COPY_TO_BYTES, vous pouvez réduire la mémoire que peut occuper un grand graphique d'objets. Pour plus d'informations, voir Amélioration des performances avec des mappes de tableaux d'octets.

COPY_TO_BYTES_RAW

Avec COPY_TO_BYTES_RAW, vous pouvez directement accéder au formulaire sérialisé de vos données. Ce mode de copie offre un moyen efficace pour interagir avec les octets sérialisés, ce qui vous permet d'ignorer le processus de désérialisation pour accéder aux objets en mémoire.

Dans le fichier descripteur XML d'ObjectGrid, vous pouvez définir le mode de copie COPY_TO_BYTES et définir à l'aide d'un programme le mode de copie COPY_TO_BYTES_RAW dans les instances où vous souhaitez accéder aux données brutes sérialisée. Définissez le mode de copie COPY_TO_BYTES _RAW dans le fichier descripteur XML d'ObjectGrid uniquement lorsque votre application utilise les données brutes dans un processus d'application principal.

Utilisation incorrecte de CopyMode

Des erreurs se produisent lorsqu'une application tente d'améliorer les performances en utilisant les modes COPY_ON_READ, COPY_ON_WRITE ou NO_COPY décrits plus haut. Les erreurs intermittentes ne se produisent pas lorsqu'on passe au mode COPY_ON_READ_AND_COMMIT.

Problème

Le problème peut être dû à des données endommagées dans la mappe ObjectGrid, ce qui est la conséquence de la violation par l'application du contrat de programmation propre au mode de copie utilisé. L'altération des données peut provoquer des erreurs imprévisibles par intermittence ou d'une manière inexpliquée ou inattendue.

Solution

L'application doit respecter le contrat de programmation qui est énoncé pour le mode de copie utilisé. Dans les modes COPY_ON_READ et COPY_ON_WRITE, l'application utilise une référence à un objet valeur extérieur à la portée de la transaction à partir de laquelle la référence a été obtenue. Pour pouvoir utiliser ces modes, l'application doit supprimer la référence à l'objet value après la fin de la transaction et obtenir une nouvelle référence à l'objet value à chaque transaction qui accède à cet objet. En mode NO_COPY, l'application ne doit jamais modifier l'objet value. Dans ce cas, soit programmez l'application pour qu'elle ne touche pas à l'objet value, soit faites-lui utiliser un autre mode de copie.