eventuate-tram / eventuate-tram-sagas Goto Github PK
View Code? Open in Web Editor NEWSagas for microservices
License: Other
Sagas for microservices
License: Other
repeated failures:
io.eventuate.examples.tram.sagas.ordersandcustomers.spring.reactive.integrationtests.CustomersAndOrdersIntegrationTest
io.eventuate.util.test.async.EventuallyException: Failed after 60 iterations every 500 milliseconds
at io.eventuate.util.test.async.Eventually.eventuallyReturning(Eventually.java:70)
at io.eventuate.util.test.async.Eventually.eventually(Eventually.java:37)
at io.eventuate.util.test.async.Eventually.eventually(Eventually.java:33)
at io.eventuate.examples.tram.sagas.ordersandcustomers.spring.reactive.integrationtests.CustomersAndOrdersIntegrationTest.assertOrderState(CustomersAndOrdersIntegrationTest.java:83)
at io.eventuate.examples.tram.sagas.ordersandcustomers.spring.reactive.integrationtests.CustomersAndOrdersIntegrationTest.shouldReject(CustomersAndOrdersIntegrationTest.java:65)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.springframework.test.context.junit4.statements.RunBeforeTestExecutionCallbacks.evaluate(RunBeforeTestExecutionCallbacks.java:74)
at org.springframework.test.context.junit4.statements.RunAfterTestExecutionCallbacks.evaluate(RunAfterTestExecutionCallbacks.java:84)
at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75)
at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86)
at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:251)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:97)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:190)
at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.runTestClass(JUnitTestClassExecutor.java:110)
at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.execute(JUnitTestClassExecutor.java:58)
at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.execute(JUnitTestClassExecutor.java:38)
at org.gradle.api.internal.tasks.testing.junit.AbstractJUnitTestClassProcessor.processTestClass(AbstractJUnitTestClassProcessor.java:62)
at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.processTestClass(SuiteTestClassProcessor.java:51)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:36)
at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
at org.gradle.internal.dispatch.ContextClassLoaderDispatch.dispatch(ContextClassLoaderDispatch.java:33)
at org.gradle.internal.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:94)
at com.sun.proxy.$Proxy2.processTestClass(Unknown Source)
at org.gradle.api.internal.tasks.testing.worker.TestWorker.processTestClass(TestWorker.java:119)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:36)
at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
at org.gradle.internal.remote.internal.hub.MessageHubBackedObjectConnection$DispatchWrapper.dispatch(MessageHubBackedObjectConnection.java:182)
at org.gradle.internal.remote.internal.hub.MessageHubBackedObjectConnection$DispatchWrapper.dispatch(MessageHubBackedObjectConnection.java:164)
at org.gradle.internal.remote.internal.hub.MessageHub$Handler.run(MessageHub.java:414)
at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64)
at org.gradle.internal.concurrent.ManagedExecutorImpl$1.run(ManagedExecutorImpl.java:48)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
at org.gradle.internal.concurrent.ThreadFactoryImpl$ManagedThreadRunnable.run(ThreadFactoryImpl.java:56)
at java.base/java.lang.Thread.run(Thread.java:834)
Caused by: org.junit.ComparisonFailure: expected:<[REJECTED]> but was:<[PENDING]>
at org.junit.Assert.assertEquals(Assert.java:115)
at org.junit.Assert.assertEquals(Assert.java:144)
at io.eventuate.examples.tram.sagas.ordersandcustomers.spring.reactive.integrationtests.CustomersAndOrdersIntegrationTest.lambda$assertOrderState$0(CustomersAndOrdersIntegrationTest.java:86)
at io.eventuate.util.test.async.Eventually.lambda$eventually$0(Eventually.java:38)
at io.eventuate.util.test.async.Eventually.eventuallyReturning(Eventually.java:59)
... 59 more
Something like:
.step()
.invokeParticipant(this::reserveCredit)
.withTimeout(timeout, this::cancelReserveCredit)
The arguments to withTimeout()
are
Unfortunately, you need to get them from the bintray repository: https://dl.bintray.com/eventuateio-oss/eventuate-maven-release
Gradle build.gradle:
repositories {
maven {
url 'https://dl.bintray.com/eventuateio-oss/eventuate-maven-release'
}
}
Maven pom.xml:
<repositories>
<repository>
<id>eventuate</id>
<name>Eventuate repo</name>
<url>https://dl.bintray.com/eventuateio-oss/eventuate-maven-release</url>
</repository>
</repositories>
If a local step throws an exception the saga is rolled back.
Application code, e.g. onSagaRolledBack()
does not have access to the exception and so cannot doesn't know the reason.
Also, the rollback occurs for any exception, not just 'business-related' exceptions. Consequently, a transient technical failure will result in a rollback
.step()
.invokeLocal(steps::approveOrder)
.onException(InvalidOrderException.class, LocalExceptionCreateOrderSagaData::saveInvalidOrder)
.onExceptionRollback(InvalidOrderException.class)
onException()
is the equivalent of a try-catch that updates the saga data with the reason for the error. Note: an exception can't be simply stored in the saga data because of the requirement to (de)serialize to/from JSONonExceptionRollback()
onExceptionRollback()
is not used, then all exceptions cause a rollback (original behavior)From email thread:
Is it possible to postpone a participant's response in the orchestrator saga?
One of my saga's participants is connecting to an external service. When the participant receives the command it send a file to the external service. The service's response to the file could take hours or days, so I cannot send command reply (withSuccess/withFailure) immediately. The response is important because it effects the outcome of the saga.
Can I separate the command consumption and response generation somehow? I would like my saga to stop and wait for the participant's response (which waits for external system's response) before going to the next step.
step() onReply()
must be used to update the SagaData
with the errorDefine Spring @beans that implement the following interface:
interface SagaParticipantFailureHandler<T SagaData> extends Ordered {
void handleFailure(T, SagaData sagaData);
}
If a saga participant throws an exception, the first (sorted by Ordered) applicable - to exception and saga data - is invoked to update the SagaData prior to starting compensation.
Analogous to #86
I have created all the eventuate framework tables and order and also customer micro service specific tables in the same customer_order_service's eventuate schema.
On http://localhost:8082/api/orders POST call getting below error in customer-service micro service.
java.lang.RuntimeException: No method for io.eventuate.tram.messaging.common.MessageImpl@5ae52f04[payload={"orderId":2,"orderTotal":{"amount":210.0},"customerId":2},headers={command_saga_id=00000186da5861a3-0000000000e00000, PARTITION_ID=849aa8cc-12b3-45b6-9a54-85611a3fdc44, DATE=Mon, 13 Mar 2023 09:41:26 GMT, command_type=com.mt.unity.orderservice.saga.apimessaging.command.ReserveCreditCommand, command_reply_to=com.mt.unity.orderservice.saga.CreateOrderSaga-reply, DESTINATION=customerService, command_saga_type=com.mt.unity.orderservice.saga.CreateOrderSaga, command__destination=customerService, ID=00000186da58a0af-0000000000e00000}]
at io.eventuate.tram.commands.consumer.CommandDispatcher.messageHandler(CommandDispatcher.java:61) ~[eventuate-tram-commands-0.31.0.RELEASE.jar:na]
at io.eventuate.tram.sagas.participant.SagaCommandDispatcher.messageHandler(SagaCommandDispatcher.java:38) ~[eventuate-tram-sagas-participant-0.20.0.RELEASE.jar:na]
at io.eventuate.tram.consumer.common.DecoratedMessageHandlerFactory.lambda$decorate$0(DecoratedMessageHandlerFactory.java:34) ~[eventuate-tram-consumer-common-0.31.0.RELEASE.jar:na]
at io.eventuate.tram.consumer.common.PrePostHandlerMessageHandlerDecorator.accept(PrePostHandlerMessageHandlerDecorator.java:27) ~[eventuate-tram-consumer-common-0.31.0.RELEASE.jar:na]
at io.eventuate.tram.consumer.common.PrePostHandlerMessageHandlerDecorator.accept(PrePostHandlerMessageHandlerDecorator.java:12) ~[eventuate-tram-consumer-common-0.31.0.RELEASE.jar:na]
at io.eventuate.tram.consumer.common.MessageHandlerDecoratorChainBuilder.lambda$buildChain$0(MessageHandlerDecoratorChainBuilder.java:33) ~[eventuate-tram-consumer-common-0.31.0.RELEASE.jar:na]
at io.eventuate.tram.consumer.common.DuplicateDetectingMessageHandlerDecorator.lambda$accept$0(DuplicateDetectingMessageHandlerDecorator.java:16) ~[eventuate-tram-consumer-common-0.31.0.RELEASE.jar:na]
at io.eventuate.tram.consumer.jdbc.TransactionalNoopDuplicateMessageDetector.lambda$doWithMessage$0(TransactionalNoopDuplicateMessageDetector.java:28) ~[eventuate-tram-consumer-jdbc-0.31.0.RELEASE.jar:na]
at io.eventuate.common.spring.jdbc.EventuateSpringTransactionTemplate.lambda$executeInTransaction$0(EventuateSpringTransactionTemplate.java:18) ~[eventuate-common-spring-jdbc-0.16.0.RELEASE.jar:na]
at org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:140) ~[spring-tx-6.0.6.jar:6.0.6]
at io.eventuate.common.spring.jdbc.EventuateSpringTransactionTemplate.executeInTransaction(EventuateSpringTransactionTemplate.java:18) ~[eventuate-common-spring-jdbc-0.16.0.RELEASE.jar:na]
at io.eventuate.tram.consumer.jdbc.TransactionalNoopDuplicateMessageDetector.doWithMessage(TransactionalNoopDuplicateMessageDetector.java:26) ~[eventuate-tram-consumer-jdbc-0.31.0.RELEASE.jar:na]
at io.eventuate.tram.consumer.common.DuplicateDetectingMessageHandlerDecorator.accept(DuplicateDetectingMessageHandlerDecorator.java:16) ~[eventuate-tram-consumer-common-0.31.0.RELEASE.jar:na]
at io.eventuate.tram.consumer.common.DuplicateDetectingMessageHandlerDecorator.accept(DuplicateDetectingMessageHandlerDecorator.java:6) ~[eventuate-tram-consumer-common-0.31.0.RELEASE.jar:na]
at io.eventuate.tram.consumer.common.MessageHandlerDecoratorChainBuilder.lambda$buildChain$0(MessageHandlerDecoratorChainBuilder.java:33) ~[eventuate-tram-consumer-common-0.31.0.RELEASE.jar:na]
at io.eventuate.tram.consumer.common.PrePostReceiveMessageHandlerDecorator.accept(PrePostReceiveMessageHandlerDecorator.java:26) ~[eventuate-tram-consumer-common-0.31.0.RELEASE.jar:na]
at io.eventuate.tram.consumer.common.PrePostReceiveMessageHandlerDecorator.accept(PrePostReceiveMessageHandlerDecorator.java:12) ~[eventuate-tram-consumer-common-0.31.0.RELEASE.jar:na]
at io.eventuate.tram.consumer.common.MessageHandlerDecoratorChainBuilder.lambda$buildChain$0(MessageHandlerDecoratorChainBuilder.java:33) ~[eventuate-tram-consumer-common-0.31.0.RELEASE.jar:na]
at io.eventuate.tram.consumer.common.MessageConsumerImpl.lambda$subscribe$0(MessageConsumerImpl.java:40) ~[eventuate-tram-consumer-common-0.31.0.RELEASE.jar:na]
at io.eventuate.tram.consumer.kafka.EventuateTramKafkaMessageConsumer.lambda$subscribe$0(EventuateTramKafkaMessageConsumer.java:29) ~[eventuate-tram-consumer-kafka-0.31.0.RELEASE.jar:na]
at io.eventuate.messaging.kafka.consumer.MessageConsumerKafkaImpl.lambda$subscribe$0(MessageConsumerKafkaImpl.java:46) ~[eventuate-messaging-kafka-consumer-0.16.0.RELEASE.jar:na]
at io.eventuate.messaging.kafka.consumer.MessageConsumerKafkaImpl.handle(MessageConsumerKafkaImpl.java:99) ~[eventuate-messaging-kafka-consumer-0.16.0.RELEASE.jar:na]
at io.eventuate.messaging.kafka.consumer.MessageConsumerKafkaImpl.lambda$subscribeWithReactiveHandler$1(MessageConsumerKafkaImpl.java:59) ~[eventuate-messaging-kafka-consumer-0.16.0.RELEASE.jar:na]
at io.eventuate.messaging.kafka.consumer.SwimlaneDispatcher.processQueuedMessage(SwimlaneDispatcher.java:72) ~[eventuate-messaging-kafka-consumer-0.16.0.RELEASE.jar:na]
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136) ~[na:na]
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635) ~[na:na]
at java.base/java.lang.Thread.run(Thread.java:833) ~[na:na]
Hello,
Getting below issues while setting up SAGA , plain setup even without any code:
Issues no1:
Parameter 1 of method sagaInstanceRepository in io.eventuate.tram.sagas.spring.orchestration.SagaOrchestratorConfiguration required a bean of type 'io.eventuate.common.id.IdGenerator' that could not be found.
The injection point has the following annotations:
- @org.springframework.beans.factory.annotation.Autowired(required=true)
- @org.springframework.beans.factory.annotation.Qualifier("xyzSagaServiceImpl")
Dependency used for Issue 1:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>io.eventuate.tram.sagas</groupId>
<artifactId>eventuate-tram-sagas-spring-orchestration</artifactId>
<version>${eventuateTramSagasVersion}</version>
</dependency>
<dependency>
<groupId>io.eventuate.tram.sagas</groupId>
<artifactId>eventuate-tram-sagas-spring-orchestration-simple-dsl</artifactId>
<version>${eventuateTramSagasVersion}</version>
</dependency>
<dependency>
<groupId>io.eventuate.tram.core</groupId>
<artifactId>eventuate-tram-spring-optimistic-locking</artifactId>
<version>${eventuateTramVersion}</version>
</dependency>
<dependency>
<groupId>io.eventuate.tram.core</groupId>
<artifactId>eventuate-tram-spring-commands</artifactId>
<version>${eventuateTramVersion} </version>
</dependency>
<dependency>
<groupId>io.eventuate.tram.core</groupId>
<artifactId>eventuate-tram-spring-producer-jdbc</artifactId>
<version>${eventuateTramVersion} </version>
</dependency>
<dependency>
<groupId>io.eventuate.tram.core</groupId>
<artifactId>eventuate-tram-spring-consumer-kafka</artifactId>
<version>${eventuateTramVersion} </version>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>9.4-1200-jdbc41</version>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
</exclusion>
</exclusions>
</dependency>
Issues No2:
below error comes while including
Parameter 2 of method duplicateMessageDetector in io.eventuate.tram.spring.consumer.jdbc.TramConsumerJdbcAutoConfiguration required a bean of type 'io.eventuate.common.jdbc.EventuateJdbcStatementExecutor' that could not be found.
Problem:
This is the likely solution: spring-projects/spring-boot#3805
Eventuate libraries (including JSonMapper) need to use ThreadLocal.getThread().getContextClassLoader()
Build failed: https://app.circleci.com/pipelines/github/eventuate-tram/eventuate-tram-sagas/63/workflows/686e4476-9603-4809-a0b1-e23b67eb8b9f/jobs/176
After restart passed.
Locally is not reproducible.
I researched logs and debugged failed test and found that there 2 saga managers in the same time:
2021-02-17 15:12:10.262 DEBUG 10221 --- [pool-1-thread-2] i.e.t.s.orchestration.SagaManagerImpl : handle message invoked io.eventuate.tram.messaging.common.MessageImpl@274a0f74[payload={},headers={commandreply_saga_id=00000177b08c70e0-5acbf80076950000, DATE=Wed, 17 Feb 2021 15:12:10 GMT, reply_outcome-type=SUCCESS, commandreply__destination=customerService, commandreply_reply_to=io.eventuate.examples.tram.sagas.ordersandcustomers.orders.sagas.createorder.LocalCreateOrderSaga-reply, commandreply_type=io.eventuate.examples.tram.sagas.ordersandcustomers.orders.sagas.participants.ReserveCreditCommand, DESTINATION=io.eventuate.examples.tram.sagas.ordersandcustomers.orders.sagas.createorder.LocalCreateOrderSaga-reply, commandreply_saga_type=io.eventuate.examples.tram.sagas.ordersandcustomers.orders.sagas.createorder.LocalCreateOrderSaga, reply_type=io.eventuate.examples.tram.sagas.ordersandcustomers.customers.service.CustomerCreditReserved, reply_to_message_id=00000177b08c7142-5acbf80076950000, ID=00000177b08c71fb-5acbf80076950000}]
2021-02-17 15:12:10.302 DEBUG 10221 --- [pool-1-thread-3] i.e.t.s.orchestration.SagaManagerImpl : handle message invoked io.eventuate.tram.messaging.common.MessageImpl@274a0f74[payload={},headers={commandreply_saga_id=00000177b08c70e0-5acbf80076950000, DATE=Wed, 17 Feb 2021 15:12:10 GMT, reply_outcome-type=SUCCESS, commandreply__destination=customerService, commandreply_reply_to=io.eventuate.examples.tram.sagas.ordersandcustomers.orders.sagas.createorder.LocalCreateOrderSaga-reply, commandreply_type=io.eventuate.examples.tram.sagas.ordersandcustomers.orders.sagas.participants.ReserveCreditCommand, DESTINATION=io.eventuate.examples.tram.sagas.ordersandcustomers.orders.sagas.createorder.LocalCreateOrderSaga-reply, commandreply_saga_type=io.eventuate.examples.tram.sagas.ordersandcustomers.orders.sagas.createorder.LocalCreateOrderSaga, reply_type=io.eventuate.examples.tram.sagas.ordersandcustomers.customers.service.CustomerCreditReserved, reply_to_message_id=00000177b08c7142-5acbf80076950000, ID=00000177b08c71fb-5acbf80076950000}]
2 identical message handlings from different threads. So, it seems it is concurrency issue.
I found that there 2 saga managers.
Since SagaInstanceFactory is newer, second place is redundant.
can we deploy CDC Services using MySQL Bin Log to multiple Nodes and still works in co-ordination or it has to be single instance running so that it doesn't mess up ? Maintaining the Single instance is easy in Docker but when it comes to OpenStack IaaS based deployment; we have to manage the Multiple Instances. A Guidance would be appreciated.
First of all, thank you for the framework Chris, you did a fantastic job.
I intend to use eventuate to handle movie renting transactions, I follow your recommendation to use the orchestrated sagas, I like to have the business of the transaction centralized in one place.
I'm facing an issue with our workflow, in the the middle of the saga we have to wait until the UI confirm that the video stream is available to bill the customer, that's break the command/reply principle of the orchestration and sounds like a choreography interaction.
I found a way to send the reply whenever the confirm message arrives thru the io.eventuate.tram.messaging.producer.common.MessageProducerImplementation class, when the ConfimStreamCommand arrives I persist the message in a DB table that I added and I use the message later to construct the reply.
This works but I'm wondering if there is a cleaner way to do it thru the framework.
Enhancement of #76
Now, I config a saga dsl as below:
step()
.invokeParticipant(this::step1)
.withCompensation(this::compensation1)
.step()
.invokeParticipant(this::step2)
.withCompensation(this::compensation2)
.step()
.invokeParticipant(this::step3)
.build();
When the step 3 fail, I want the compensation1 and compensation2 are run. But now it seen maybe only compensation2 is run as default.
Anyone have a solution?
Stack Trace:
2023-03-09T04:53:23.687+05:30 ERROR 40264 --- [nio-8080-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed: org.springframework.jdbc.BadSqlGrammarException: PreparedStatementCallback; bad SQL grammar [INSERT INTO eventuate.saga_instance(saga_type, saga_id, state_name, last_request_id, saga_data_type, saga_data_json, end_state, compensating) VALUES(?, ?, ?, ?, ?, ?, ?, ?)]] with root cause
org.postgresql.util.PSQLException: ERROR: relation "eventuate.saga_instance" does not exist
Position: 13
at org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2676) ~[postgresql-42.5.4.jar:42.5.4]
at org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:2366) ~[postgresql-42.5.4.jar:42.5.4]
at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:356) ~[postgresql-42.5.4.jar:42.5.4]
at org.postgresql.jdbc.PgStatement.executeInternal(PgStatement.java:496) ~[postgresql-42.5.4.jar:42.5.4]
at org.postgresql.jdbc.PgStatement.execute(PgStatement.java:413) ~[postgresql-42.5.4.jar:42.5.4]
at org.postgresql.jdbc.PgPreparedStatement.executeWithFlags(PgPreparedStatement.java:190) ~[postgresql-42.5.4.jar:42.5.4]
at org.postgresql.jdbc.PgPreparedStatement.executeUpdate(PgPreparedStatement.java:152) ~[postgresql-42.5.4.jar:42.5.4]
at com.zaxxer.hikari.pool.ProxyPreparedStatement.executeUpdate(ProxyPreparedStatement.java:61) ~[HikariCP-5.0.1.jar:na]
at com.zaxxer.hikari.pool.HikariProxyPreparedStatement.executeUpdate(HikariProxyPreparedStatement.java) ~[HikariCP-5.0.1.jar:na]
at org.springframework.jdbc.core.JdbcTemplate.lambda$update$2(JdbcTemplate.java:965) ~[spring-jdbc-6.0.6.jar:6.0.6]
at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:651) ~[spring-jdbc-6.0.6.jar:6.0.6]
at org.springframework.jdbc.core.JdbcTemplate.update(JdbcTemplate.java:960) ~[spring-jdbc-6.0.6.jar:6.0.6]
at org.springframework.jdbc.core.JdbcTemplate.update(JdbcTemplate.java:1015) ~[spring-jdbc-6.0.6.jar:6.0.6]
at org.springframework.jdbc.core.JdbcTemplate.update(JdbcTemplate.java:1025) ~[spring-jdbc-6.0.6.jar:6.0.6]
at io.eventuate.common.common.spring.jdbc.EventuateSpringJdbcStatementExecutor.update(EventuateSpringJdbcStatementExecutor.java:23) ~[eventuate-common-common-spring-jdbc-0.9.0.RELEASE.jar:na]
at io.eventuate.tram.sagas.orchestration.SagaInstanceRepositoryJdbc.save(SagaInstanceRepositoryJdbc.java:89) ~[eventuate-tram-sagas-orchestration-0.13.0.RELEASE.jar:na]
at io.eventuate.tram.sagas.orchestration.SagaManagerImpl.create(SagaManagerImpl.java:87) ~[eventuate-tram-sagas-orchestration-0.13.0.RELEASE.jar:na]
at io.eventuate.tram.sagas.orchestration.SagaManagerImpl.create(SagaManagerImpl.java:69) ~[eventuate-tram-sagas-orchestration-0.13.0.RELEASE.jar:na]
at io.eventuate.tram.sagas.orchestration.SagaInstanceFactory.create(SagaInstanceFactory.java:21) ~[eventuate-tram-sagas-orchestration-0.13.0.RELEASE.jar:na]
at com.mt.unity.orderservice.saga.OrderSagaService.createOrder(OrderSagaService.java:37) ~[classes/:na]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[na:na]
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
at java.base/java.lang.reflect.Method.invoke(Method.java:568) ~[na:na]
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:343) ~[spring-aop-6.0.6.jar:6.0.6]
at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:196) ~[spring-aop-6.0.6.jar:6.0.6]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) ~[spring-aop-6.0.6.jar:6.0.6]
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:750) ~[spring-aop-6.0.6.jar:6.0.6]
Application.yml
spring:
datasource:
driver-class-name: org.postgresql.Driver
username: postgres
password: admin
url: jdbc:postgresql://${DATABASE_HOST:localhost}:5432/${DB_POSTGRES_DATABASE_NAME:orders}
jpa:
properties:
'[hibernate.default_schema]': public note: I used eventuate instead of public but that also doesn't work.
show-sql: true
generate-ddl: true
hibernate:
ddl-auto: update
database: postgresql
eventuatelocal.kafka.bootstrap.servers: ${DOCKER_HOST_IP:localhost}:9092
eventuatelocal.zookeeper.connection.string: ${DOCKER_HOST_IP:localhost}:2181
Main Class:
@SpringBootApplication
@slf4j
@import({
SagaOrchestratorConfiguration.class,
TramMessageProducerJdbcConfiguration.class,
EventuateTramKafkaMessageConsumerConfiguration.class
})
public class OrderServiceApplication {
public static void main(String[] args) {
SpringApplication.run(OrderServiceApplication.class, args);
}
@Bean
public ChannelMapping channelMapping() {
return DefaultChannelMapping.builder().build();
}
//@Bean
//public DuplicateMessageDetector duplicateMessageDetector() {
// return new NoopDuplicateMessageDetector();
//}
@Bean
public DuplicateMessageDetector duplicateMessageDetector(EventuateSchema eventuateSchema,
//String currentTimeInMillisecondsSql,
EventuateJdbcStatementExecutor eventuateJdbcStatementExecutor,
EventuateTransactionTemplate eventuateTransactionTemplate) {
return new SqlTableBasedDuplicateMessageDetector(eventuateSchema, null, eventuateJdbcStatementExecutor, eventuateTransactionTemplate);
}
@Bean
public HttpMessageConverters customConverters() {
HttpMessageConverter<?> additional = new MappingJackson2HttpMessageConverter();
return new HttpMessageConverters(additional);
}
@Bean
public OncePerRequestFilter logFilter() {
return new OncePerRequestFilter() {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
log.info("Path: {}", request.getRequestURI());
filterChain.doFilter(request, response);
log.info("Path: {} {}", request.getRequestURI(), response.getStatus());
}
};
}
}
Pom.xml
<java.version>17</java.version>
<org.mapstruct.version>1.4.2.Final</org.mapstruct.version>
0.24.0.RELEASE
0.13.0.RELEASE
<dependency>
<groupId>io.eventuate.tram.sagas</groupId>
<artifactId>eventuate-tram-sagas-spring-orchestration-simple-dsl</artifactId>
<version>${eventuateTramSagasVersion}</version>
</dependency>
<!-- Eventuate Tram dependencies -->
<dependency>
<groupId>io.eventuate.tram.core</groupId>
<artifactId>eventuate-tram-spring-jdbc-kafka</artifactId>
<version>${eventuateTramVersion}</version>
</dependency>
<dependency>
<groupId>io.eventuate.tram.core</groupId>
<artifactId>eventuate-tram-spring-optimistic-locking</artifactId>
<version>${eventuateTramVersion}</version>
</dependency>
If a service crashes when there are outstanding replies, then those replies aren't processed until another saga is started
Hi,
First of all, thank you for great job. I have a problem for withReply() saga method. I check everything according to your project but i could not find it. I'll be happy if you can help me. Thanks..
private Message createMail(CommandMessage cm) {
......
return withLock(Mail.class, mail.getId()).withSuccess(reply); // this method is called but no message is passed to kafka. I trace it
// on kafdrop.
}
so the following method did not handle because there is no message from kafka..
......
.onReply(CreateMailCommandReply.class, CreateObligedSagaState::handleCreateMailCommandReply)
........
I tried to start basic saga - but i did not find how to start it.
In documentation: https://eventuate.io/docs/manual/eventuate-tram/latest/getting-started-eventuate-tram-sagas.html#getting-started-tram-sagas
I see:
Creating an saga orchestrator
The OrderService creates the saga:
public class OrderService {
@Autowired
private SagaManager createOrderSagaManager;@Autowired
private OrderRepository orderRepository;@transactional
public Order createOrder(OrderDetails orderDetails) {
ResultWithEvents oe = Order.createOrder(orderDetails);
Order order = oe.result;
orderRepository.save(order);
CreateOrderSagaData data = new CreateOrderSagaData(order.getId(), orderDetails);
createOrderSagaManager.create(data, Order.class, order.getId());
return order;
}
}
But i only wanted to create saga, so i need to use
createOrderSagaManager.create(data);
?
Why it is not described in documentation?
I am wondering what this code actually do - i can not find any information in the documentation.
Can i start Saga with passing some DTO wihout these lines:
ResultWithEvents<Order> oe = Order.createOrder(orderDetails);
Order order = oe.result;
orderRepository.save(order);
? Can you add video when you tell how this framework work line by line?
And this:
@Autowired private SagaManager<CreateOrderSagaData> createOrderSagaManager;
From where is this createOrderSagaManager injected?
Add preSend(String sagaId, Data data , Message message)
callback to Saga
I have tested the "eventuate-tram-sagas-examples-customers-and-orders" and "eventuate-tram-sagas-reactive-examples-customers-and-orders". I found that the reactive version doesn't update the state_name, end_state correctly when the saga completed successfully.
The bug is on line 233 where sendCommands could return empty stream. Also, line 234 should be a flatMap instead of map.
https://github.com/eventuate-tram/eventuate-tram-sagas/blob/master/eventuate-tram-sagas-reactive-orchestration/src/main/java/io/eventuate/tram/sagas/reactive/orchestration/ReactiveSagaManagerImpl.java#L233
Mono<SagaActions<Data>> nextActions = sagaCommandProducer
.sendCommands(this.getSagaType(), sagaId, acts.getCommands(), this.makeSagaReplyChannel())
.map(Optional::of)
.defaultIfEmpty(Optional.empty())
.flatMap(lastId -> {
sagaInstance.setLastRequestId(lastId.orElse(null));
updateState(sagaInstance, acts);
sagaInstance.setSerializedSagaData(SagaDataSerde.serializeSagaData(acts.getUpdatedSagaData().orElse(sagaData)));
if (acts.isEndState()) {
return performEndStateActions(sagaId, sagaInstance, acts.isCompensating(), sagaData).thenReturn(lastId);
}
return Mono.just(lastId);
})
.then(Mono.defer(() -> sagaInstanceRepository.update(sagaInstance)))
.then(Mono.defer(() -> {
if (!acts.isLocal()) return Mono.empty();
else return Mono.just(acts);
}))
.flatMap(newActs ->
Mono.from(getStateDefinition()
.handleReply(newActs.getUpdatedState().get(),
newActs.getUpdatedSagaData().get(),
MessageBuilder
.withPayload("{}")
.withHeader(ReplyMessageHeaders.REPLY_OUTCOME, CommandReplyOutcome.SUCCESS.name())
.withHeader(ReplyMessageHeaders.REPLY_TYPE, Success.class.getName())
.build())));
after handling the empty stream like above, it is working correctly like the blocking version.
Cannot invoke "java.util.Map.size()" because the return value of "org.springframework.jdbc.support.KeyHolder.getKeys()" is null
is the error message.
Failed to load ApplicationContext
java.lang.IllegalStateException: Failed to load ApplicationContext
at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:132)
at org.springframework.test.context.support.DefaultTestContext.getApplicationContext(DefaultTestContext.java:123)
at org.springframework.test.context.web.ServletTestExecutionListener.setUpRequestContextIfNecessary(ServletTestExecutionListener.java:190)
at org.springframework.test.context.web.ServletTestExecutionListener.prepareTestInstance(ServletTestExecutionListener.java:132)
at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:244)
at org.springframework.test.context.junit.jupiter.SpringExtension.postProcessTestInstance(SpringExtension.java:98)
at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$invokeTestInstancePostProcessors$5(ClassBasedTestDescriptor.java:341)
at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.executeAndMaskThrowable(ClassBasedTestDescriptor.java:346)
at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$invokeTestInstancePostProcessors$6(ClassBasedTestDescriptor.java:341)
at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193)
at java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:175)
at java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1382)
at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:482)
at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:472)
at java.util.stream.StreamSpliterators$WrappingSpliterator.forEachRemaining(StreamSpliterators.java:312)
at java.util.stream.Streams$ConcatSpliterator.forEachRemaining(Streams.java:743)
at java.util.stream.Streams$ConcatSpliterator.forEachRemaining(Streams.java:742)
at java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:580)
at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.invokeTestInstancePostProcessors(ClassBasedTestDescriptor.java:340)
at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.instantiateAndPostProcessTestInstance(ClassBasedTestDescriptor.java:263)
at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$testInstancesProvider$2(ClassBasedTestDescriptor.java:256)
at java.util.Optional.orElseGet(Optional.java:267)
at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$testInstancesProvider$3(ClassBasedTestDescriptor.java:255)
at org.junit.jupiter.engine.execution.TestInstancesProvider.getTestInstances(TestInstancesProvider.java:29)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$prepare$0(TestMethodTestDescriptor.java:108)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.prepare(TestMethodTestDescriptor.java:107)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.prepare(TestMethodTestDescriptor.java:71)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$prepare$1(NodeTestTask.java:107)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.prepare(NodeTestTask.java:107)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:75)
at java.util.ArrayList.forEach(ArrayList.java:1257)
at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:139)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:125)
at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:135)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:123)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:122)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:80)
at java.util.ArrayList.forEach(ArrayList.java:1257)
at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:139)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:125)
at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:135)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:123)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:122)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:80)
at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:32)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:51)
at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:248)
at org.junit.platform.launcher.core.DefaultLauncher.lambda$execute$5(DefaultLauncher.java:211)
at org.junit.platform.launcher.core.DefaultLauncher.withInterceptedStreams(DefaultLauncher.java:226)
at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:199)
at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:132)
at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor$CollectAllTestClassesExecutor.processAllTestClasses(JUnitPlatformTestClassProcessor.java:99)
at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor$CollectAllTestClassesExecutor.access$000(JUnitPlatformTestClassProcessor.java:79)
at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor.stop(JUnitPlatformTestClassProcessor.java:75)
at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.stop(SuiteTestClassProcessor.java:61)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:36)
at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
at org.gradle.internal.dispatch.ContextClassLoaderDispatch.dispatch(ContextClassLoaderDispatch.java:33)
at org.gradle.internal.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:94)
at com.sun.proxy.$Proxy2.stop(Unknown Source)
at org.gradle.api.internal.tasks.testing.worker.TestWorker.stop(TestWorker.java:132)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:36)
at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
at org.gradle.internal.remote.internal.hub.MessageHubBackedObjectConnection$DispatchWrapper.dispatch(MessageHubBackedObjectConnection.java:182)
at org.gradle.internal.remote.internal.hub.MessageHubBackedObjectConnection$DispatchWrapper.dispatch(MessageHubBackedObjectConnection.java:164)
at org.gradle.internal.remote.internal.hub.MessageHub$Handler.run(MessageHub.java:413)
at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64)
at org.gradle.internal.concurrent.ManagedExecutorImpl$1.run(ManagedExecutorImpl.java:48)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at org.gradle.internal.concurrent.ThreadFactoryImpl$ManagedThreadRunnable.run(ThreadFactoryImpl.java:56)
at java.lang.Thread.run(Thread.java:748)
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'sagaListener': Unsatisfied dependency expressed through field 'sagaInstanceFactory'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'sagaInstanceFactory' defined in class path resource [io/eventuate/tram/sagas/spring/orchestration/SagaOrchestratorConfiguration.class]: Unsatisfied dependency expressed through method 'sagaInstanceFactory' parameter 0; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'sagaInstanceRepository' defined in class path resource [io/eventuate/tram/sagas/spring/orchestration/SagaOrchestratorConfiguration.class]: Unsatisfied dependency expressed through method 'sagaInstanceRepository' parameter 1; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'io.eventuate.common.id.IdGenerator' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:643)
at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:130)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:399)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1422)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:594)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:517)
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:323)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:226)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:321)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:895)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:878)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:550)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:758)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:750)
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:397)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:315)
at org.springframework.boot.test.context.SpringBootContextLoader.loadContext(SpringBootContextLoader.java:120)
at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContextInternal(DefaultCacheAwareContextLoaderDelegate.java:99)
at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:124)
... 88 more
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'sagaInstanceFactory' defined in class path resource [io/eventuate/tram/sagas/spring/orchestration/SagaOrchestratorConfiguration.class]: Unsatisfied dependency expressed through method 'sagaInstanceFactory' parameter 0; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'sagaInstanceRepository' defined in class path resource [io/eventuate/tram/sagas/spring/orchestration/SagaOrchestratorConfiguration.class]: Unsatisfied dependency expressed through method 'sagaInstanceRepository' parameter 1; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'io.eventuate.common.id.IdGenerator' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:798)
at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:539)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1338)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1177)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:557)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:517)
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:323)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:226)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:321)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:276)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1306)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1226)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:640)
... 107 more
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'sagaInstanceRepository' defined in class path resource [io/eventuate/tram/sagas/spring/orchestration/SagaOrchestratorConfiguration.class]: Unsatisfied dependency expressed through method 'sagaInstanceRepository' parameter 1; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'io.eventuate.common.id.IdGenerator' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:798)
at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:539)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1338)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1177)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:557)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:517)
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:323)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:226)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:321)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:276)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1306)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1226)
at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:885)
at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:789)
... 120 more
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'io.eventuate.common.id.IdGenerator' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoMatchingBeanFound(DefaultListableBeanFactory.java:1716)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1272)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1226)
at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:885)
at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:789)
... 134 more
Hi,
how can we retry the execution of a command handler method in case of failure instead of triggering the compensation logic immediately?
public CommandHandlers commandHandlerDefinitions() {
return SagaCommandHandlersBuilder.fromChannel("CHANNEL_NAME")
.onMessage(Command.class, this::handleCommand)
.build();
}
public Message handleCommand(final CommandMessage<Command> command) {
try {
//logic
return withSuccess();
} catch (Exception e) {
// in case of failure, I want to retry the method instead of directly triggering the compensations action
return withFailure(SagaError.of(e));
}
Hi,
I see the description attached to Kafka as message broker. Is there any support in the near future so that users can specify message broker at his will.
For example, I am using K8S in GCP which has google-cloud-PubSub service. I do not want to use dedicated Kafka pods installed in K8s since I have to maintain it manually even though with K8S help.
Thanks
These lines are duplicated in non-reactive repository:
Consider moving into SagaInstanceRepositorySql
and renaming it to ...Mapper
.
Hi all,
I just want to start off by saying how much I like your framework, even though I haven't got my sagas working yet, Eventuate Sagas makes developing the sagas much easier rather than having to implement all of this stuff ourselves!
Just a quick question about the Eventuate CDC (from your Docker image) - do I need to link this and all of my spring cloud services to a Postgres database that runs on your image specifically? I thought the only difference between the DB created in the saga-customers-and-orders-example
https://github.com/eventuate-tram/eventuate-tram-sagas-examples-customers-and-orders was that some tables were already created - and that the CDC would just create them anyway? Or does it not work like that?
Here is my stacktrace coming from the CDC:
java.lang.RuntimeException: Cannot get table io.eventuate.local.common.SchemaAndTable@50514dec[schema=eventuate,tableName=message]: result set is empty
at io.eventuate.local.polling.PollingDao.queryPrimaryKey(PollingDao.java:235) [eventuate-local-java-cdc-connector-polling-0.10.0-SNAPSHOT.jar!/:na]
at io.eventuate.local.polling.PollingDao.lambda$getPrimaryKey$4(PollingDao.java:208) [eventuate-local-java-cdc-connector-polling-0.10.0-SNAPSHOT.jar!/:na]
at io.eventuate.local.common.DaoUtils.handleConnectionLost(DaoUtils.java:22) ~[eventuate-local-java-cdc-connector-common-0.10.0-SNAPSHOT.jar!/:na]
at io.eventuate.local.polling.PollingDao.getPrimaryKey(PollingDao.java:206) [eventuate-local-java-cdc-connector-polling-0.10.0-SNAPSHOT.jar!/:na]
at io.eventuate.local.polling.PollingDao.processEvents(PollingDao.java:113) [eventuate-local-java-cdc-connector-polling-0.10.0-SNAPSHOT.jar!/:na]
at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193) ~[na:1.8.0_252]
at java.util.Spliterators$ArraySpliterator.forEachRemaining(Spliterators.java:948) ~[na:1.8.0_252]
at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:482) ~[na:1.8.0_252]
at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:472) ~[na:1.8.0_252]
at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:708) ~[na:1.8.0_252]
at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234) ~[na:1.8.0_252]
at java.util.stream.ReferencePipeline.reduce(ReferencePipeline.java:541) ~[na:1.8.0_252]
at io.eventuate.local.polling.PollingDao.start(PollingDao.java:93) [eventuate-local-java-cdc-connector-polling-0.10.0-SNAPSHOT.jar!/:na]
at java.lang.Thread.run(Thread.java:748) ~[na:1.8.0_252]
2021-01-20 06:05:26.708 ERROR 6 --- [ Thread-6] io.eventuate.local.common.DaoUtils : Could not access database Cannot get table io.eventuate.local.common.SchemaAndTable@50514dec[schema=eventuate,tableName=message
]: result set is empty - retrying in 500 milliseconds
In my Kubernetes Deployment for this, I have pointed at the image to it's default tag, like this eventuateio/eventuate-cdc-service
- do I need to specify a specific version number as well?
Also, do all of my services still need to be linked to the same database or is that happening just for less complication for the example?
Thanks,
Ben
Failed to execute here:
eventuate-tram-sagas/.circleci/config.yml
Line 32 in 878e60d
With error:
/bin/bash: ./.circleci/save-containers-and-tests.sh: Permission denied
However permissions are:
ls -lt ./.circleci/save-containers-and-tests.sh
-rwxr-xr-x 1 cer staff 345 Feb 16 07:55 ./.circleci/save-containers-and-tests.sh
Support saga command handlers that
SagaReplyInfo
- that contains the data needed to construct a reply, e.g. message headers etc.SagaReplyInfo
can be passed to a SagaonXXX()
- can use the SagaReplyInfo
to send a reply to the commandThis breaks reply message handling.
failed
state.test failed: o.eventuate.examples.tram.sagas.ordersandcustomers.integrationtests.micronaut > OrdersAndCustomersInMemoryIntegrationTest
initializationError
Caused by: io.micronaut.context.exceptions.NoSuchBeanException: No bean of type [javax.persistence.EntityManager] exists
https://gist.github.com/dartartem/62ee7c5104161642830a56c4c8d015f6
I found the change that produced that error: dartartem/eventuate-common-1@42fc179
I rebuilt snapshots and retest to confirm, without this dartartem/eventuate-common-1@42fc179#diff-d7d990cf61eaeeb108e551894e03ecfbR15 tests passed.
Then I found that tests worked without that change because there is real datasource configured:
https://github.com/eventuate-tram/eventuate-tram-sagas/blob/master/orders-and-customers-micronaut-in-memory-integration-tests/src/test/resources/application.yml#L2
For some reason custom datasource bean creates the error on replacement. (Or if just remove the real datasource)
Unfortunately I did not find way to solve it.
Looks like micronaut does not allow to replace datasource bean for jpa.
But since we have tests on real database probably we can remove in-memory tests?
This won't work as intended. The steps will be executed one after the other. But there is no guarantee that the saga will be completed successfully/compensated if any kind of failure occurs. Local and non-local steps should be interleaved.
Either:
Due to unconfigured mock() a local action through an exception. This was hidden
Hi @cer. I set up a project with reactive saga with PostgreSQL DB. But when I try to create a new saga instance I got:
java.lang.RuntimeException: org.springframework.dao.InvalidDataAccessApiUsageException: No parameter specified for [param5] in query [INSERT INTO eventuate.saga_instance(saga_type, saga_id, state_name, last_request_id, saga_data_type, saga_data_json, end_state, compensating, failed) VALUES(:param1, :param2, :param3, NULL, :param5, :param6, :param7, :param8, :param9)] at io.eventuate.common.spring.jdbc.reactive.EventuateSpringReactiveJdbcStatementExecutor.handleDuplicateKeyException(EventuateSpringReactiveJdbcStatementExecutor.java:127) ~[eventuate-common-spring-reactive-jdbc-0.16.0.RELEASE.jar:na
I debug your framework and found that you have a custom logic for postgres null parameters but looks like this code have a bug.
My setup:
spring boot version 2.7.9
pg image: eventuateio/eventuate-tram-sagas-mysql:0.20.0.RELEASE
eventuate dependencies:
implementation(platform("io.eventuate.platform:eventuate-platform-dependencies:2022.2.RELEASE"))
implementation("io.eventuate.tram.core:eventuate-tram-spring-reactive-jdbc-kafka")
implementation("io.eventuate.tram.sagas:eventuate-tram-sagas-spring-reactive-orchestration-simple-dsl-starter")
Hint: I tried change DB to MySQL and I don't get this error but in my case I need PostgreSql
Thanks for bring us this great project! I know this repo from a book, which is Microservices Patterns. I really want to try it, can this framework use in production now?
To replicate the issue below are process:-
We are connecting 3 services in Saga flow
while executing the saga we are facing some unhandled exceptions, which can't be handled by try-catch.
Please refer example
public Message reserveCredit(CommandMessage<ReserveCreditCommand> cm) {
ReserveCreditCommand cmd = cm.getCommand();
long customerId = cmd.getCustomerId();
Customer customer = customerDao.findById(customerId);
// TODO null check
try {
customer.reserveCredit(cmd.getOrderId(), cmd.getOrderTotal()); //An Unhandled exception occurs here.
return withSuccess(new CustomerCreditReserved());
} catch (CustomerCreditLimitExceededException e) {
return withFailure(new CustomerCreditReservationFailed());
}
}
@cer @dartartem We are looking for a resolution for these kinds of cases when exceptions are not caught inside command handles and Saga got blocked as a result.
Please refer to "Health check endpoint" in the below doc
https://eventuate.io/docs/manual/eventuate-tram/latest/cdc-configuration.html
Originally posted by @cer in eventuate-tram/eventuate-tram-sagas-examples-customers-and-orders#49 (comment)
Hey,
great work on the framework, love implementing with it and runs really nice.
One question that came up while designing more complex sagas. Given an order with e.g. 3 products, is there any way a saga could process those products concurrently? Like in the form of forking out a saga for each product and on successful or failed completion join them again to the main saga.
Thanks
is it safe to delete saga instance where end_state = 1 on the saga_instance table ?
I am new to SAGA design pattern and just started to try some sample with your eventuate tram saga framework but I am unable to build project due to below issues.
Please help me to solve this issue.
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'io.eventuate.tram.sagas.orchestration.SagaInstanceFactory' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
My Reference Link:
https://eventuate.io/docs/manual/eventuate-tram/latest/getting-started-eventuate-tram-sagas.html
Added dependencies:
<properties>
<java.version>17</java.version>
<eventuateTramVersion>0.31.0.RELEASE</eventuateTramVersion>
<eventuateTramSagasVersion>0.20.0.RELEASE</eventuateTramSagasVersion>
</properties>
<dependency>
<groupId>io.eventuate.tram.sagas</groupId>
<artifactId>eventuate-tram-sagas-spring-orchestration-simple-dsl-starter</artifactId>
<version>${eventuateTramSagasVersion}</version>
</dependency>
<dependency>
<groupId>io.eventuate.tram.core</groupId>
<artifactId>eventuate-tram-spring-jdbc-kafka</artifactId>
<version>${eventuateTramVersion}</version>
</dependency>
@Service
public class OrderSagaService {
@Autowired
private OrderRepository orderRepository;
@Autowired
private SagaInstanceFactory sagaInstanceFactory;
@Autowired
private CreateOrderSaga createOrderSaga;
@Transactional
public Order createOrder(OrderDetails orderDetails) {
CreateOrderSagaData data = new CreateOrderSagaData(orderDetails);
sagaInstanceFactory.create(createOrderSaga, data);
return orderRepository.findById(data.getOrderId()).get();
}
}
What i have tried:
I added below import statement:
@import(SagaOrchestratorConfiguration.class)
public class OrderServiceApplication {
public static void main(String[] args) {
SpringApplication.run(OrderServiceApplication.class, args);
}
}
But getting below issues:
'io.eventuate.tram.messaging.producer.MessageProducer' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'orderSagaService': Unsatisfied dependency expressed through field 'sagaInstanceFactory': Error creating bean with name 'sagaInstanceFactory' defined in io.eventuate.tram.sagas.spring.orchestration.SagaOrchestratorConfiguration: Unsatisfied dependency expressed through method 'sagaInstanceFactory' parameter 1: Error creating bean with name 'commandProducer' defined in io.eventuate.tram.spring.commands.producer.TramCommandProducerConfiguration: Unsatisfied dependency expressed through method 'commandProducer' parameter 0: No qualifying bean of type 'io.eventuate.tram.messaging.producer.MessageProducer' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'sagaInstanceFactory' defined in io.eventuate.tram.sagas.spring.orchestration.SagaOrchestratorConfiguration: Unsatisfied dependency expressed through method 'sagaInstanceFactory' parameter 1: Error creating bean with name 'commandProducer' defined in io.eventuate.tram.spring.commands.producer.TramCommandProducerConfiguration: Unsatisfied dependency expressed through method 'commandProducer' parameter 0: No qualifying bean of type 'io.eventuate.tram.messaging.producer.MessageProducer' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'commandProducer' defined in io.eventuate.tram.spring.commands.producer.TramCommandProducerConfiguration: Unsatisfied dependency expressed through method 'commandProducer' parameter 0: No qualifying bean of type 'io.eventuate.tram.messaging.producer.MessageProducer' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'io.eventuate.tram.messaging.producer.MessageProducer' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
Requires: eventuate-tram/eventuate-tram-core#174
Motivation is the concurrent execution of compensating transactions and retriable transactions, which are saga steps that by definition cannot fail.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.