microservices-patterns / ftgo-application

Example code for the book Microservice patterns
Other
3.35k stars 1.29k forks source link

Saga (CreateOrder) - compensation transactions of earlier steps are not executed #107

Open asn25 opened 3 years ago

asn25 commented 3 years ago

Hello Chris & all,

I'm checking the work of compensating transactions in sagas (CreateOrderSaga). If I simulate failure in Saga in step-5 (step-ConfirmCreate in Kitchen Service, modify it to return .withFailure() unconditionally), then I see calling of compensating transaction of step-3 (step-CreateTicket in Kitchen Service), but there is no call to step-1 compensating transaction (where RejectOrder command should be issued).

As I understand, compensating transactions should be executed as a chain, all of them (in reverse order), so it should be C3, C1. But according to my observation, if Saga fails on step-5, then only step-3 compensation block is performed (C3).

Is it correct behaviour?

So order after that Saga is in state CANCELLED, but probably should be in state REJECTED - ?

Tested on envs with eventuate-tram-saga 0.12.0.RC5 and 0.13.0.

Thanks.

asn25 commented 3 years ago

Seems, it's about Ticket.cancelCreate(), where NotYetImplementedException is thrown. Here is log from Kitchen Service:

Caused by: net.chrisrichardson.ftgo.common.NotYetImplementedException: null at net.chrisrichardson.ftgo.kitchenservice.domain.Ticket.cancelCreate(Ticket.java:68) ~[classes!/:na] at net.chrisrichardson.ftgo.kitchenservice.domain.KitchenService.cancelCreateTicket(KitchenService.java:60) ~[classes!/:na] at net.chrisrichardson.ftgo.kitchenservice.domain.KitchenService$$FastClassBySpringCGLIB$$d8e0b629.invoke() ~[classes!/:na] at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204) ~[spring-core-5.0.7.RELEASE.jar!/:5.0.7.RELEASE] at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:746) ~[spring-aop-5.0.7.RELEASE.jar!/:5.0.7.RELEASE] at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) ~[spring-aop-5.0.7.RELEASE.jar!/:5.0.7.RELEASE] at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:294) ~[spring-tx-5.0.7.RELEASE.jar!/:5.0.7.RELEASE] at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:98) ~[spring-tx-5.0.7.RELEASE.jar!/:5.0.7.RELEASE] at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185) ~[spring-aop-5.0.7.RELEASE.jar!/:5.0.7.RELEASE] at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:688) ~[spring-aop-5.0.7.RELEASE.jar!/:5.0.7.RELEASE] at net.chrisrichardson.ftgo.kitchenservice.domain.KitchenService$$EnhancerBySpringCGLIB$$c4fa96cd.cancelCreateTicket() ~[classes!/:na] at net.chrisrichardson.ftgo.kitchenservice.messagehandlers.KitchenServiceCommandHandler.cancelCreateTicket(KitchenServiceCommandHandler.java:76) ~[classes!/:na] at io.eventuate.tram.sagas.participant.SagaCommandHandlersBuilder.lambda$onMessage$1(SagaCommandHandlersBuilder.java:50) ~[eventuate-tram-sagas-participant-0.12.0.RC5.jar!/:na] at io.eventuate.tram.sagas.participant.SagaCommandHandler.lambda$new$0(SagaCommandHandler.java:25) ~[eventuate-tram-sagas-participant-0.12.0.RC5.jar!/:na] at io.eventuate.tram.commands.consumer.CommandHandler.lambda$new$0(CommandHandler.java:27) ~[eventuate-tram-commands-0.22.0.RC5.jar!/:na] at io.eventuate.tram.commands.consumer.CommandHandler.invokeMethod(CommandHandler.java:62) ~[eventuate-tram-commands-0.22.0.RC5.jar!/:na] at io.eventuate.tram.commands.consumer.CommandDispatcher.invoke(CommandDispatcher.java:92) ~[eventuate-tram-commands-0.22.0.RC5.jar!/:na] at io.eventuate.tram.sagas.participant.SagaCommandDispatcher.invoke(SagaCommandDispatcher.java:80) ~[eventuate-tram-sagas-participant-0.12.0.RC5.jar!/:na] at io.eventuate.tram.commands.consumer.CommandDispatcher.messageHandler(CommandDispatcher.java:76) ~[eventuate-tram-commands-0.22.0.RC5.jar!/:na]


If I remove exception in Ticket.cancelCreate() and return just empty List, then it's ok with compensation transactions, both C3 and C1 compensating transactions are executed and Order becomes in REJECTED state.

Seems, uncaught exceptions (f.e. in main Aggregate of service) are breaking Saga, in particular, compensating transaction chain is not executing in full volume.

cer commented 3 years ago

This is the fault of the application developer :-) Eventuate Tram Command handlers must not throw exceptions.

cer commented 3 years ago

Thanks for spending the time to investigate