Open dartartem opened 3 years ago
To solve it, for quarkus I propose to use TransactionManager: https://quarkus.io/guides/transaction
@Inject
TransactionManager transactionManager;
transactionManager.getTransaction().registerSynchronization(new Synchronization() {
@Override
public void beforeCompletion() {
}
@Override
public void afterCompletion(int status) {
}
});
In case if there is no transaction transactionManager.getTransaction()
returns null (tested)
See: https://app.circleci.com/pipelines/github/eventuate-tram/eventuate-tram-sagas-quarkus/6/workflows/0a2077f3-c149-43e8-9db2-13f70fcb3264/jobs/16
shouldRejectOrder() - io.eventuate.examples.tram.sagas.ordersandcustomers.integrationtests.quarkus.OrdersAndCustomersInMemoryIntegrationTest
failed with
In the logs:
Locally is not reproducible.
Problem:
I missed that InMemoryMessageProducer uses spring PlatformTransactionManager:
https://github.com/eventuate-tram/eventuate-tram-core/blob/wip-db-id-gen/eventuate-tram-in-memory/src/main/java/io/eventuate/tram/inmemory/InMemoryMessageProducer.java#L26
and it does not work with quarkus.
Instead of executing callback of
withContext
method after transaction, it does it immediately.withContext
is used by https://github.com/eventuate-tram/eventuate-tram-core/blob/wip-db-id-gen/eventuate-tram-messaging-producer-common/src/main/java/io/eventuate/tram/messaging/producer/common/MessageProducerImpl.java#L36 for senging messagesin case of InMemoryMessageProducer it means executing in-memory consumers in separate thread:
https://github.com/eventuate-tram/eventuate-tram-core/blob/wip-db-id-gen/eventuate-tram-in-memory/src/main/java/io/eventuate/tram/inmemory/InMemoryMessageConsumer.java#L44
How it is related to saga:
Order created inside a transaction:
https://github.com/eventuate-tram/eventuate-tram-sagas/blob/wip-db-id-gen/orders-and-customers/src/main/java/io/eventuate/examples/tram/sagas/ordersandcustomers/orders/service/OrderService.java#L41
Saga is saved:
https://github.com/eventuate-tram/eventuate-tram-sagas/blob/wip-db-id-gen/eventuate-tram-sagas-orchestration/src/main/java/io/eventuate/tram/sagas/orchestration/SagaManagerImpl.java#L87
Then, message is dispatching here:
https://github.com/eventuate-tram/eventuate-tram-sagas/blob/wip-db-id-gen/eventuate-tram-sagas-orchestration/src/main/java/io/eventuate/tram/sagas/orchestration/SagaManagerImpl.java#L105
https://github.com/eventuate-tram/eventuate-tram-sagas/blob/wip-db-id-gen/eventuate-tram-sagas-orchestration/src/main/java/io/eventuate/tram/sagas/orchestration/SagaManagerImpl.java#L116
Instead of dispatching after transaction, message is dispatched during not finished transaction. So, saved saga can be not visible for other threads, if they accessed to db before transaction is finished:
https://github.com/eventuate-tram/eventuate-tram-sagas/blob/wip-db-id-gen/eventuate-tram-sagas-orchestration/src/main/java/io/eventuate/tram/sagas/orchestration/SagaManagerImpl.java#L143
https://github.com/eventuate-tram/eventuate-tram-sagas/blob/wip-db-id-gen/eventuate-tram-sagas-orchestration/src/main/java/io/eventuate/tram/sagas/orchestration/SagaManagerImpl.java#L155
https://github.com/eventuate-tram/eventuate-tram-sagas/blob/wip-db-id-gen/eventuate-tram-sagas-orchestration/src/main/java/io/eventuate/tram/sagas/orchestration/SagaManagerImpl.java#L172
To reproduce it locally, I added Thread.sleep here:
https://github.com/eventuate-tram/eventuate-tram-sagas/blob/wip-db-id-gen/eventuate-tram-sagas-orchestration/src/main/java/io/eventuate/tram/sagas/orchestration/SagaManagerImpl.java#L106