Instantanés d'événements dans Axonframework 3, améliorant les performances

Présentation du cadre Axonframework


Axonframework est un cadre qui met en œuvre plusieurs principes et modèles de conception tels que:

CQRS - Traitement partagé des demandes de lecture et d'écriture de données
Le sourçage d'événements est lorsque l'état d'une application est stocké sous forme de chaîne d'événements.
DDD Aggregate - un objet de domaine qui stocke l'état

L'un des inconvénients du stockage de l'état final d'une application sous la forme d'une chaîne d'événements est le nombre d'événements stockés et traités. Heureusement, Axonframework vous permet de créer un événement de capture instantanée, qui contient le résultat de plusieurs événements (événement de domaine).

Instantanés d'événements


Événement d'instantané - ce sont les valeurs résultantes de plusieurs événements (événement de domaine). Cela vous permet de recréer rapidement l'état de l'agrégat. Il est important de comprendre qu'un instantané est créé à partir d'événements qui ont été utilisés pour une unité spécifique avec un identifiant unique.

Par exemple ( Fig. 1 ), nous définissons la configuration pour créer un instantané pour tous les deux événements (seuil = 2 - à des fins d'illustration). Dans ce cas, lorsque deux événements modifient l'état de l'unité, une image est créée avec les valeurs résultantes des deux événements précédents.


Figure 1. Un instantané de deux événements. (seuil = 2)

Prenons un exemple plus compliqué ( Fig. 2 ), la configuration spécifie également un seuil de 2 afin qu'un instantané soit créé tous les deux événements. Lorsque 2 événements changent l'état de l'unité, une image est créée. De plus, les 2 autres événements changent l'état de l'unité et une nouvelle image n'est pas créée, mais celle existante est mise à jour.


Fig.2 Le résultat de la chaîne d'événements dans une image (seuil = 2)

Performances


D'une part, lorsqu'une longue chaîne d'événements s'accumule dans une application, il faut du temps pour lire et traiter un grand nombre d'événements pour recréer l'état de l'unité. En revanche, si vous créez un instantané, l'état de l'unité sera recréé rapidement, mais il faudra du temps pour créer un instantané. Un équilibre doit être trouvé entre ces deux situations.


Fig.3 Performances sans prendre d'instantané


Fig.4 Performance avec instantané (seuil = 3)

Par défaut, un instantané est créé dans le thread qui a appelé la méthode scheduleSnapshot (). Ce réglage n'est pas recommandé pour un environnement de combat (voir Fig. 4 / entrée).

Ci-dessous est un exemple de code utilisant ThreadPoolExecutor (...) qui fournira un thread séparé pour créer un instantané. Dans ce cas, notre client ne remarquera pas de ralentissement dans l'application et le temps imparti pour créer un instantané.

Code


Pour activer la création d'instantanés, vous devez apporter de petites modifications au code de l'application. L'annotation agrégée indique le nom du référentiel utilisé dans le code de la classe de configuration. Dans la classe de configuration, le seuil de création d'instantanés, la méthode de création d'instantanés, les référentiels, etc. sont indiqués.

AxonConfig.java

@Autowired private EventStore eventStore; @Bean public SpringAggregateSnapshotterFactoryBean springAggregateSnapshotterFactoryBean() { return new SpringAggregateSnapshotterFactoryBean(); } @Bean public SpringAggregateSnapshotter snapshotter(ParameterResolverFactory parameterResolverFactory, EventStore eventStore, TransactionManager transactionManager) { Executor executor = Executors.newFixedThreadPool(10); return new SpringAggregateSnapshotter(eventStore, parameterResolverFactory, executor, transactionManager); } @Bean("reservationRepository") public EventSourcingRepository<Reservation> reservationRepository(Snapshotter snapshotter, ParameterResolverFactory parameterResolverFactory) { return new EventSourcingRepository<Reservation>(reservationAggregateFactory(), eventStore, parameterResolverFactory, new EventCountSnapshotTriggerDefinition(snapshotter, 50)); } @Bean(name = "reservationAggregateFactory") public AggregateFactory<Reservation> reservationAggregateFactory() { SpringPrototypeAggregateFactory<Reservation> aggregateFactory = new SpringPrototypeAggregateFactory<>(); aggregateFactory.setPrototypeBeanName("reservation"); return aggregateFactory; } 

Reservation.java

 @Aggregate(repository = "reservationRepository") public class Reservation { //… } 

Il convient de noter que le fil de discussion Google Groupes contient des exemples de code et des discussions utiles.

Sélectionner un seuil pour les instantanés



5.1. Manière théorique

Calculons le nombre d'événements pouvant être appliqués à l'unité dans la classe EventListener. Ensuite, nous estimons théoriquement le nombre moyen d'événements appliqués à l'unité dans une situation typique et fixons une valeur légèrement inférieure à celle-ci comme seuil de création d'images. Cela peut être fait si l'application vient d'être créée et qu'il n'y a pas de données réelles pour l'analyse.

5.2. Manière pratique

Nous analysons les données de la base de données et nous supposons que la base de données est utilisée par MongoDB et fonctionne à l'intérieur du conteneur Docker.

 > docker exec -it <container-id> mongo > show dbs admin 0.000GB axonframework 0.000GB local 0.000GB > use axonframework switched to db axonframework > show collections domainevents sagas snapshotevents > db.domainevents.findOne() { “_id” : ObjectId(“5bb1dc8d4446d63bcc765feb”), “aggregateIdentifier” : “b1e320d5–58aa-4b9b-a667-aa724900592f”, “type” : “Reservation”, “sequenceNumber” : NumberLong(0), “serializedPayload” : “<com.example.ReservationStarted><reservationIdentifier>b1e320d5–58aa-4b9b-a667-aa724900592f</reservationIdentifier><duration resolves-to=\”java.time.Ser\”><byte>1</byte><long>2400</long><int>0</int></duration></com.example.ReservationStarted>”, “timestamp” : “2018–10–01T08:36:29.434Z”, “payloadType” : “com.example.ReservationStarted”, “payloadRevision” : null, “serializedMetaData” : “<meta-data><entry><string>traceId</string><string>b090b86a-ec89–484b-ae9f-e4fa0f9bcd39</string></entry><entry><string>correlationId</string><string>b090b86a-ec89–484b-ae9f-e4fa0f9bcd39</string></entry></meta-data>”, “eventIdentifier” : “f324f021–50b4–4e91–84d0-f8c4425f3eb9” } 

Chaque événement stocké contient un champ d'agrégatIdentifier, par lequel nous calculons le nombre d'événements appliqués à chaque agrégat avec une simple requête:

 db.domainevents.aggregate([ {$group: {_id: "$aggregateIdentifier", count: {$sum: 1} } }, {$sort : {count : -1} } ]); { "_id" : "0d84afd1-f199-45c8-b50e-7d9ebfa4c8fb", "count" : 136 } { "_id" : "49de7c32-38ea-435a-b837-ccdb61ec0baa", "count" : 136 } { "_id" : "12957b0b-af05-47c4-a3d8-968b75cf9ffb", "count" : 136 } { "_id" : "97a24559-ee3a-43e7-a6be-1eb6840b662a", "count" : 132 } { "_id" : "b6aeb1af-0620-4b02-8de3-c2446c2f7d83", "count" : 132 } { "_id" : "b385aaf4-3338-489f-8d1b-4600d5e088b9", "count" : 132 } { "_id" : "5970327f-9551-4945-94e9-3844c0cd3543", "count" : 132 } ... { "_id" : "0182239h-3948-3334-98t5-9643j4ld8346", "count" : 1 } 

Le seuil de création d'instantanés peut être sélectionné en dessous de la moyenne afin que les instantanés soient créés efficacement. Dans ce cas, une valeur de 50 est très bien.

Vérification de l'activation de l'instantané


 > mongo > show dbs admin 0.000GB axonframework 0.000GB local 0.000GB > use axonframework > show collections domainevents sagas snapshotevents > db.domainevents.count() 515 > db.snapshotevents.count() 7 

Si la collection de snapshotevents n'est pas vide et contient des snapshots, la création de snapshots a été activée avec succès.

Autres options d'instantanés


La documentation mentionne d'autres variantes sur la façon d'activer la création d'instantanés, par exemple:

  • le nombre d'événements générés depuis le dernier instantané a dépassé le seuil
  • Délai d'initialisation de l'unité expiré
  • temporisation, etc. etc.

Source: https://habr.com/ru/post/fr435522/


All Articles