Contenu connexe

LyonJUG-2023-v1.0.pdf

  1. @jefrajames Microservices et cohérence des données … Mais on fait comment pour de vrai ?
  2. whoami { "name" : "JF James", "experience_ in_it" : 36, "company" : "Worldline", "domain" : "Payment & secured transactions" , "skills" : [ " Software architecture" , "Java", "Open Source" , "DevRel" ] }
  3. Du monolithe aux microservices … •Définir et borner les services •Garantir la cohérence d’ensemble des données
  4. Cohérence des données • Accés aux données d’un service tiers • Requête multi-services • Cohérence globale si plusieurs services modifiés
  5. Transaction ACID locale Module 1 BdD Begin Commit Module 2 Module 3 Rollback
  6. Atomicity-Consistency-isolation-Durability Read Uncommitted Read Committed Repeatable Read Serializable
  7. Transaction ACID locale • Simplicité de développement • Performant et scalable • Eprouvé en production • Historiquement supporté : base de données SQL, broker JMS
  8. Transaction ACID distribuée BdD 1 BdD 2 BdD 3 Transaction Manager Service 1 Service 2 Service 3 Begin Commit Rollback
  9. 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…
  10. 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
  11. C’est trop compliqué ! Et les erreurs de compensation ?
  12. 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
  13. Limiter les erreurs de compensation • Isolation des erreurs entre service • Action de compensation simple, rapide, idempotent
  14. 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
  15. Les solutions Ne rien faire ———————————————————————————————————————————————————————————————————————————- Log + rattrapage à postériori
  16. MicroProfile GraphQL Config Fault Tolerance Fallback Retry Timeout Circuit breaker Bulkhead OpenAPI Rest Client JWT API Jakarta EE Foundation Health Metrics Open Tracing Observability Reactive Messaging Reactive Streams Context Propagation Long Running Action Core JAX-RS CDI JSON Standalone specs
  17. Un peu de (bonne) lecture https://microservices.io https://eventuate.io/
  18. MicroProfile LRA Eventuate SAGA Origine OASIS WS-Composite Application Framework (2006) SAGA: H. Garcia-Molina & K. Salem 1987 Association for Computing Eventuate: 2017 Nature Spécification (64 p) Plateforme Microservices Open Source Version Utilisée Version 1.0, avril 2021 0.5.1.RELEASE (Quarkus), oct 2022 Implémentations Quarkus, OpenLiberty, Helidon, Wildfly, Camel, SpringBoot (Jersey) SpringBoot, Micronaut, Quarkus Modèle de programmation Annotations Orchestration (DSL) ou Chorégraphie (domain events) Echange REST/HTTP Synchrone Messaging Asynchrone Infrastructure LRA Coordinatorr BdD, Messaging, CDC
  19. En action !
  20. Dispo sur GitHub ! https://github.com/jefrajames/lra-demo https://github.com/jefrajames/saga-demo https://github.com/jefrajames/choreo-demo
  21. Démo 21 Holiday Service Hotel Service Trip Service Car Service 1. Contrôle client 2. Appel Trip 3. Appel Hotel 4. Appel Car 5. Contrôle montant total a.Contrôle départ b.Contrôle destination c.Choix transport & horaire HolidayBookRequest PENDING REJECTED ACCEPTED CANCELED PENDING REJECTED ACCEPTED CANCELED
  22. MicroProfile LRA
  23. MicroProfile LRA Architecture Holiday Service Trip Service REST/HTTP Synchrone Ports: • Jeager: 3306 • Postgres: 5432 • LRA Coordinator: 50000 • Trip : 8082 • Holiday : 8080
  24. // 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 }
  25. // 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 }
  26. // 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) { … }
  27. Un modèle simple mais … @LRA @Complete @Compensate ∆t?
  28. Eventuate
  29. Eventuate Architecture Holiday Service Trip Service Ports Zookeeper : 2181 Kafka : 9092,29092 Kafka GUI : 9088 MySQL-Holiday: 3306 MySQL-Trip: 3308 Cdc-holiday: 9086 Cdc-trip: 9084 Quarkus-Trip : 9082 Quarkus-Holiday : 9080 Kafka howl kowl CDC CDC
  30. // Holiday Service DSL-based SAGA definition private SagaDefinition<HolidayBookSagaData> sagaDefinition = step() .invokeLocal(this::create) .withCompensation(this::reject) .step() .invokeLocal(this::checkCustomer) .step() .invokeParticipant(this::bookTrip) .onReply(TripBooked.class, this::handleTripBooked) .onReply(BookTripFailed.class, this::handleBookTripFailed) .withCompensation(this::cancelTrip) .step() .invokeLocal(this::checkPricing) .step() .invokeParticipant(this::confirmTrip) .step() .invokeLocal(this::approve) .build(); Standard Execution Compensation
  31. // 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) { … }
  32. 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
  33. 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
  34. Benchmark
  35. Quelques chiffres Lines of code Overhead Response time ms Overhead Monolith ACID local 918 32 REST 1435 1,56 57 1,78 REST + LRA 1495 1,63 126 3,94 Eventuate choreographie 1419 1,55 130 4,06 Eventuate orchestration 1951 2,13 244 7,63
  36. MicroProfile LRA Eventuate SAGA Composants clés SPOF potentiels Narayana CDC, Messaging, BdD Courbe d’apprentissage **** *** Productivité **** **** Simplicité de configuration **** *** Découplage fonctionnel **** *** Découplage technique ** **** Observabilité *** **** Généricité *** **** Roadmap **** ***
  37. Alternatives Apache Camel et Debezium
  38. Apache Camel LRA Module // action from("direct:reserveCredit") .saga() .propagation(SagaPropagation.MANDATORY) .compensation("direct:refundCredit") .transform().header(Exchange.SAGA_LONG_RUNNING_ACTION) .bean(creditService, "reserveCredit") .log("Credit ${header.amount} reserved in action ${body}"); // compensation from("direct:refundCredit") .transform().header(Exchange.SAGA_LONG_RUNNING_ACTION) .bean(creditService, "refundCredit") .log("Credit for action ${body} refunded");
  39. Debezium
  40. Debezium Outbox
  41. Conclusion
  42. 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
  43. Merci
  44. 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
  45. 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
  46. 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