Instantáneas de eventos en Axonframework 3, mejorando el rendimiento

Descripción general del marco Axonframework


Axonframework es un marco que implementa varios principios y patrones de diseño, tales como:

CQRS : separa el procesamiento de solicitudes de lectura y escritura de datos
El abastecimiento de eventos es cuando el estado de una aplicación se almacena como una cadena de eventos.
Agregado DDD : un objeto de dominio que almacena el estado

Uno de los inconvenientes de almacenar el estado final de una aplicación en forma de una cadena de eventos es la cantidad de eventos almacenados y procesados. Afortunadamente, Axonframework le permite crear un evento de instantánea, que contiene el resultado de varios eventos (evento de dominio).

Instantáneas de eventos


Evento de instantánea: estos son los valores resultantes de varios eventos (evento de dominio). Esto le permite recrear rápidamente el estado del Agregado. Es importante comprender que se crea una instantánea a partir de eventos que se usaron para una Unidad específica con un identificador único.

Por ejemplo ( Fig. 1 ), establecemos la configuración para crear una instantánea para cada dos eventos (umbral = 2 - con fines ilustrativos). En este caso, cuando dos eventos cambian el estado de la Unidad, se creará una imagen con los valores resultantes de los dos eventos anteriores.


Figura 1. Una instantánea de dos eventos. (umbral = 2)

Considere un ejemplo más complicado ( Fig. 2 ), la configuración también especifica un umbral de 2 para que se cree una instantánea cada dos eventos. Cuando 2 eventos cambian el estado de la Unidad, se creará una imagen. Además, los otros 2 eventos cambian el estado de la Unidad y no se crea una nueva imagen, pero se actualiza la existente.


Fig. 2 El resultado de la cadena de eventos en una imagen (umbral = 2)

Rendimiento


Por un lado, cuando una larga cadena de eventos se acumula en una aplicación, lleva tiempo leer y procesar una gran cantidad de eventos para recrear el estado de la Unidad. Por otro lado, si crea una instantánea, el estado de la Unidad se recreará rápidamente, pero llevará tiempo crear una instantánea. Debe lograrse un equilibrio entre estas dos situaciones.


Fig. 3 Rendimiento sin tomar una instantánea


Fig. 4 Rendimiento con instantánea (umbral = 3)

De forma predeterminada, se crea una instantánea en el hilo que llamó al método scheduleSnapshot (). Esta configuración no se recomienda para un entorno de combate (ver Fig. 4 / entrada).

A continuación se muestra un ejemplo de código utilizando ThreadPoolExecutor (...) que proporcionará un hilo separado para crear una instantánea. En este caso, nuestro cliente no notará una desaceleración en la aplicación y el tiempo asignado para tomar una foto.

Código


Para activar la creación de instantáneas, debe realizar pequeños cambios en el código de la aplicación. La anotación agregada indica el nombre del repositorio que se usa en el código de la clase de configuración. En la clase de configuración, se indica el umbral para crear instantáneas, el método para crear instantáneas, repositorios, etc.

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 { //… } 

Vale la pena señalar que el hilo de discusión de Grupos de Google contiene ejemplos de código útiles y discusiones.

Seleccionar umbral para instantáneas



5.1. Forma teórica

Calculemos el número de eventos que se pueden aplicar a la Unidad en la clase EventListener. Luego, en teoría, evaluamos el número promedio de eventos aplicados a la Unidad en una situación típica y establecemos un valor ligeramente menor que esto como umbral para crear imágenes. Esto se puede hacer si la aplicación acaba de crearse y no hay datos reales para el análisis.

5.2. Forma práctica

Analizamos los datos de la base de datos, y suponemos que MongoDB utiliza la base de datos y funciona dentro del contenedor acoplable.

 > 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” } 

Cada evento almacenado contiene un campo agreggateIdentifier, mediante el cual calculamos el número de eventos aplicados a cada agregado con una consulta simple:

 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 } 

El umbral para crear instantáneas se puede seleccionar por debajo del promedio para que las instantáneas se creen de manera eficiente. En este caso, un valor de 50 está bien.

Comprobación de la activación de la instantánea


 > 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 colección de eventos de instantáneas no está vacía y contiene instantáneas, la creación de instantáneas se ha activado correctamente.

Otras opciones de instantáneas


La documentación menciona otras variaciones sobre cómo activar la creación de instantáneas, por ejemplo:

  • El número de eventos generados desde que la última instantánea superó el umbral
  • La inicialización de la unidad ha excedido el tiempo de espera
  • retraso de tiempo, etc. etc.

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


All Articles