Skip to content

Gestion de la concurrence

Ewt implémente deux méthodes de gestion de la concurrence : une méthode optimiste et une méthode pessimiste. Il est possible de spécifier pour chaque modèle le type de lock à utiliser. Sans indication particulière, Ewt applique le type optimiste par défaut.

Prérequis

Pour être fonctionnelle, la gestion du lock nécessite deux éléments au niveau de la base de données:

  1. L'ajout d'une colonne dédiée dans les tables de données. Cette colonne sert à la fois pour le lock optimiste et le lock pessimiste.
    La colonne allouée à ce rôle doit être déclarée via l'entrée admin.lockingColName du fichier de configuration. Le nom de colonne par défaut est ewt_locking.
  2. Une table ewt_locking dans la base de données. Cette table permet d'enregistrer des informations complémentaires sur le type de lock, la date/heure à laquelle le lock est posé, l'utilisateur concerné, etc. Cette table est reprise automatiquement dans les scripts de création de tables.

Gestion optimiste

Le modèle de gestion optimiste est largement documenté en ligne. Le tutoriel en ligne disponible à l'adresse handling-concurrency donne une bonne vision de la manière selon laquelle la gestion optimiste de la concurrence est implémentée dans Ewt. De nombreuses autres sources peuvent être facilement trouvées sur la toile.

L'idée est de rester optimiste et de se dire que les conflits liés à la concurrence sont peu fréquents. La gestion de concurrence optimiste signifie qu'on laisse le conflit se produire et que l'on prend les mesures appropriées uniquement lorsque le conflit est constaté.

Une gestion optimiste s'applique en général aux enregistrements, c'est-à-dire aux tuples dans le cas de Ewt. Ainsi, pour implémenter ce type de gestion, chaque tuple contient une colonne réservée qui enregistre le numéro de version du tuple. Lorsqu'un utilisateur ouvre un dossier, le numéro de version est lu et conservé en mémoire au niveau du tuple (comme c'est le cas pour les autres données du tuple). Au moment de mettre à jour la valeur en base de données, le moteur adapte la requête de mise à jour de la manière suivante:

1
2
3
4
UPDATE table
SET ...,                       --> ensemble de champs à mettre à jour
    _version = _version + 1
WHERE idTuple=? AND _version=? 

Si la requête réussit, cela signifie qu'aucun autre utilisateur n'a modifié le tuple. Si la requête échoue (c.-à-d. si elle ne modifie aucun tuple), cela signifie que le tuple a été modifié par un autre utilisateur (ou par le même utilisateur dans une autre session). L'application demande alors à l'utilisateur de régler le conflit.

On parle de gestion basée sur les tuples car le numéro de version est associé aux tuples. Toutefois il est possible de demander à Ewt d'agir au niveau du dossier et non uniquement au niveau du tuple lorsqu'un problème de concurrence est détecté.

Pour chaque modèle, on indiquera via l'attribut locklevel le niveau de lock attendu. Cet attribut peut prendre 3 valeurs:

  • tuple : Cette valeur indique qu'un conflit de concurrence détecté par le moteur au niveau d'un tuple ne bloque l'enregistrement que du tuple en question. Les autres tuples du dossier (pour lesquels aucun conflit n'est détecté) sont quant à eux mis à jour dans la base de données.
  • doc : Cette valeur indique qu'un conflit de concurrence détecté par le moteur a une conséquence au niveau de tout le dossier et pas uniquement au niveau du/des tuple(s) concerné(s) par le conflit. En clair : dès que le moteur détecte un conflit sur l'un des tuples du dossier, aucune valeur du dossier n'est mise à jour en base de données.
  • none : Dans ce cas, aucun test de concurrence n'est effectué sur le modèle. Chaque enregistrement écrase la valeur précédente dans la mesure où elle existe encore. Il s'agit donc d'un mode "Client Wins" ou "Last in Wins". Ce type de lock est dangereux et n'est à utiliser que pour les cas où une perte de données est sans conséquence.

Il est également possible d'indiquer au modèle le comportement que doit avoir le moteur en cas de conflit. En règle générale, on propose deux choix à l'utilisateur en cas de conflit: forcer la mise à jour des valeurs ou recharger les valeurs de la base de données.

Exceptions

Lorsqu'un conflit de concurrence optimiste est détecté, le moteur génère une ou plusieurs exceptions que l'on retrouve dans l'arbre de sortie.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
<exceptions count="2">
  <exception type="lock" subtype="optimistic" level="tuple"
             target="c4c8658e-e80d-4575-91fe-2a3064d2fc37"
             cause="tupleModifiedElsewhere">
    <databaseValues>
      <field target="2b1e2ffa-7d5c-44f7-aecb-c606b53bcb47">12:05</field>
      <field target="faba464f-d080-4540-91d8-d4736ac00c8f"></field>
    </databaseValues>
  </exception>
  <exception type="lock" subtype="optimistic" level="doc"
             target="fa35f7fb-b1f9-4af0-9b81-26d6ce1d4486"
             cause="documentModifiedElsewhere"/>
</exceptions>

Qu'il s'agisse de concurrence optimiste comme ici ou pessimiste comme présenté plus bas, les exceptions liées à un problème de concurrence ont toutes le type lock.

L'attribut subtype reprend le type de lock appliqué. Dans le cas de la gestion optimiste, la valeur est optimistic.

L'attribut level renseigne sur le niveau auquel l'exception est détectée. Les valeurs fournies par cet attribut correspondent aux valeurs de l'attribut locklevel décrit plus haut dans ce document.

L'attribut target contient l'hash du tuple sur lequel porte l'exception.

L'attribut cause contient un identifiant d'erreur qui donne plus d'information sur la nature du conflit rencontré. L'attribut peut prendre les valeurs suivantes:

  • tupleModifiedElsewhere
  • tupleModifiedElsewhereWithSameValues
  • tupleNotFoundInDatabase
  • documentModifiedElsewhere
  • documentNotFoundInDatabase

Les exceptions de niveau "tuple" contiennent également un détail de champs dont la valeur en base de données diffère de celle du dossier courant. Chaque champ référencé contient un attribut target qui reprend l'identifiant de contexte du champ et une valeur.

Gestion pessimiste

Le modèle de gestion pessimiste est également largement documenté sur internet. Ici l'idée est de poser un lock systématique sur la ressource que l'on souhaite modifier, afin que personne d'autre ne puisse la modifier tant que le lock n'est pas libéré.

Lorsqu'un utilisateur ouvre un dossier, le moteur recherche s'il existe déjà un lock sur le dossier. Si c'est le cas, le dossier n'est accessible qu'en lecture. Si aucun lock préalable n'est posé, alors un lock est posé pour l'utilisateur. Le lock est levé à la fermeture ou à la suppression du dossier.

Dans Ewt, le principe est similaire, mais le lock est posé au niveau des tuples: la colonne gérant le locking (ewt_locking ou autre en fonction de ce qui est défini dans la propriété admin.lockingColName de la config de l'application) est mise à jour sur chaque tuple du dossier. La table ewt_locking quant à elle enregistre des informations relatives au contexte de verrouillage (date/heure, auteur, etc.)

Exceptions

Lorsqu'un conflit de concurrence pessimiste se produit, le moteur génère une ou plusieurs exceptions que l'on retrouve dans l'arbre de sortie. La structure est sensiblement la même que dans le cas de la gestion optimiste, à quelques différences près:

  • L'exception ne contient pas d'élément databaseValues
  • L'attribut subtype prend la valeur pessimistic
  • L'attribut cause peut prendre les valeurs suivantes:

    • cannotAcquireLock
    • cannotReleaseLock
    • corruptedLockingTable
    • tupleLockedElsewhere
    • tupleNotFoundInDatabase
    • documentNotFoundInDatabase
    • fatalLockingException