Comment assurer l'intégrité des données en architecture distribuée microservices. Focus sur l'intégrité transactionnelle avec les Long Running Actions et les SAGA.
Transaction ACID locale
• Simplicité de développement
• Performant et scalable
• Eprouvé en production
• Historiquement supporté : base de données SQL, broker JMS
Transaction ACID distribuée
• Simplicité de développement mais …
• Fort couplage technique
• Coût élevé de l’isolation
• Fragilité liée à la coordination synchrone
• Non supporté par NoSQL, Kafka, RabbitMQ, REST, GraphQL, gRPC…
Un point moins ACID SVP
Service
1
BdD
1
Start Confirmation
Service
2
Service
3
BdD
2
BdD
3
LRA/SAGA
ACID locale 1 ACID locale 2 ACID locale 3
Compensation
Compliqué ?
ACID LRA/SAGA
Erreur technique Rollback global
Service fautif: rollback local
Autres services : code métier exécuté par
action de compensation
Erreur fonctionnelle
Code métier exécuté de
manière spécifique
Service fautif: code métier
Autres services : code métier exécuté par
action de compensation
Limiter les erreurs de compensation
• Isolation des erreurs entre service
• Action de compensation simple, rapide, idempotent
Principes LRA/SAGA
• Des transactions ACID locales
• Une coordination globale légère, faiblement couplée
• Des opérations de compensation explicites
• Perte de l’isolation globale : de ACID à ACD
Les solutions
Ne rien faire
———————————————————————————————————————————————————————————————————————————-
Log + rattrapage à postériori
// Holiday Service
@POST @Path("/book")
@LRA(value = LRA.Type.REQUIRED, end = true,
timeLimit = 2, timeUnit = ChronoUnit.SECONDS,
cancelOn = {Response.Status.INTERNAL_SERVER_ERROR},
cancelOnFamily = {Response.Status.Family.CLIENT_ERROR}
)
public Response book(@HeaderParam(LRA_HTTP_CONTEXT_HEADER) String lraId …) {
// Business logic here
}
// Holiday Service
@PUT @Path("/compensate")
@Compensate
public Response compensate(@HeaderParam(LRA_HTTP_CONTEXT_HEADER) String lraId) {
// Required: compensation action here
}
@PUT @Path(«/complete")
@Complete
public Response complete(@HeaderParam(LRA_HTTP_CONTEXT_HEADER) String lraId) {
// Optional: clean up actions if any
}
// Trip Service
@POST @Path(« /book")
@LRA(value = LRA.Type.SUPPORTS, end = false)
public Response book(@HeaderParam(LRA_HTTP_CONTEXT_HEADER) String lraId …) { … }
@PUT @Path(« /compensate")
@Compensate
public Response compensate(@HeaderParam(LRA_HTTP_CONTEXT_HEADER) String lraId) { … }
@PUT @Path(« /complete")
@Complete
public Response complete(@HeaderParam(LRA_HTTP_CONTEXT_HEADER) String lraId) { … }
// Trip Service Command Handler
@Transactional
public Message bookTrip(CommandMessage<BookTripCommand> cm) {
BookTripCommand cmd = cm.getCommand();
Trip trip = new Trip(cmd);
tripService.book(trip);
if (trip.businessError != null)
return withFailure(new BookTripFailed(trip.id, trip.businessError));
TripBooked tripBooked = trip.toReply();
return withSuccess(tripBooked);
}
@Transactional
public Message confirmTrip(CommandMessage<ConfirmTripCommand> cm) { … }
@Transactional
public Message cancelTrip(CommandMessage<CancelTripCommand> cm) { … }
Change Data Capture
Publisher Consumer
message
table
CDC
SQL polling Transaction log tailing
Local ACID transaction
CDC
Publish Subscribe
Local ACID transaction
received
message
Business logic Business logic
Base de données Eventuate
Holiday Trip
cdc_monitoring X X
entities X X
events X X
message X X
offset_store X X
received_message X X
Infrastructure X X
saga_instance X
saga_instance_partition X
saga_lock_table X
snapshots X
En résumé
• Une problématique complexe … des solutions mais pas de miracle !
• Accepter le principe de compensation : c’est possible !
• MicroProfile LRA OK si : « Full REST» (ou Apache CAMEL)
• SAGA chorégraphie OK si : complexité fonctionnelle modérée
• SAGA orchestration OK si : complexité fonctionnelle élevée, transaction
longue durée
Pour terminer
• Si trop de transaction distribuée : revoir le découpage en service
• Solution minimaliste : tracer les erreurs et compenser en arrière plan
• Des solutions plus avancées existent
• Ne pas négliger les performances et le monitoring
• Actions de compensation simples, rapides et idempotent
Pour aller plus loin
• Mes 2 démos sur GitHub :
• https://github.com/jefrajames/lra-demo
• https://github.com/jefrajames/saga-demo
• Eventuate : le site microservices.io, le site Eventuate et les exemples
• LRA: le blog Narayana, les exemples Quarkus, Open Liberty, Helidon
Pour aller encore plus loin
• Un exemple LRA avec SpringBoot (Jersey) et Axon
• Le module LRA Camel
• Configurer le CDC en transaction log tailing sur PostgreSQL
• Oubtbox Quarkus Extension