Grains répliqués
Parfois, plusieurs instances du même grain peuvent être actives, par exemple en cas d’utilisation d’un multicluster et de OneInstancePerClusterAttribute. JournaledGrain est conçu pour prendre en charge les instances répliquées sans difficultés. Il s’appuie sur les fournisseurs de cohérence des journaux pour exécuter les protocoles nécessaires et garantir la concordance de toutes les instances par rapport à la même séquence d’événements. En particulier, il prend en charge les aspects suivants :
Versions cohérentes : toutes les versions de l’état du grain (à l’exception des versions provisoires) sont basées sur la même séquence globale d’événements. En particulier, si deux instances voient le même numéro de version, elles voient le même état.
Événements en compétition : plusieurs instances peuvent déclencher un événement de manière simultanée. Le fournisseur de cohérence résout cette compétition, et vérifie que tout le monde est d’accord sur la même séquence.
Notifications/Réactivité : une fois qu’un événement est déclenché au niveau d’une instance monograin, le fournisseur de cohérence effectue non seulement une mise à jour du stockage, mais notifie également toutes les autres instances de grain.
Pour une discussion générale sur la cohérence, consultez notre TechReport et l’article sur GSP (Global Sequence Protocol).
Événements conditionnels
Les événements en compétition peuvent être problématiques s’ils entrent en conflit, par exemple si deux événements ne doivent pas être validés simultanément pour une raison quelconque. Prenons le cas d’un retrait sur un compte bancaire, deux instances peuvent déterminer indépendamment qu’il existe des fonds suffisants pour ce retrait et émettre un événement de retrait. Toutefois, la combinaison des deux événements peut entraîner un découvert. Pour éviter cela, l’API JournaledGrain
prend en charge une méthode RaiseConditionalEvent.
bool success = await RaiseConditionalEvent(
new WithdrawalEvent() { /* ... */ });
Les événements conditionnels vérifient et revérifient si la version locale correspond à la version présente dans le stockage. Si ce n’est pas le cas, cela signifie que la séquence d’événements s’est développée entre-temps, ce qui implique que cet événement a perdu la compétition face à un autre événement. Dans ce cas, l’événement conditionnel n’est pas ajouté au journal, et RaiseConditionalEvent retourne la valeur false.
Cela équivaut à l’utilisation d’étiquettes d’entités avec des mises à jour de stockage conditionnelles. De plus, cela permet de fournir un mécanisme simple pour éviter la validation d’événements conflictuels.
Il est possible et même judicieux d’utiliser à la fois des événements conditionnels et inconditionnels pour le même grain, par exemple DepositEvent
et WithdrawalEvent
. Les dépôts n’ont pas besoin d’être conditionnels : même si DepositEvent
perd une compétition, il n’a pas à être annulé. Toutefois, il peut toujours être ajouté à la séquence d’événements globale.
Il suffit d’attendre la tâche retournée par RaiseConditionalEvent
pour confirmer l’événement. En d’autres termes, il n’est pas nécessaire d’appeler également ConfirmEvents
.
Synchronisation explicite
Parfois, il est souhaitable de vérifier qu’un grain correspond entièrement à la dernière version. Cela peut être mis en œuvre via l’appel suivant :
await RefreshNow();
Ceci fait deux choses :
- Tous les événements non confirmés sont confirmés.
- La dernière version est chargée à partir du stockage.