eventuate-foundation / eventuate-common

Other
12 stars 20 forks source link

@Transaction annotation does not work with reactive database access #86

Open dartartem opened 3 years ago

dartartem commented 3 years ago

According to documentation: https://spring.io/blog/2019/05/16/reactive-transactions-with-spring it is supported:

Starting with Spring Framework 5.2 M2, Spring supports reactive transaction management through the ReactiveTransactionManager SPI.

ReactiveTransactionManager is a transaction management abstraction for reactive and non-blocking integrations that uses transactional resources. It is a foundation for reactive @Transactional methods that return Publisher types and for programmatic transaction management that uses TransactionalOperator.

ReactiveTransactionManager is defined. But this test does not pass (eventuateTransactionTemplate.executeInTransaction uses @Transactional):

  @Test
  public void testTransactions() throws Exception {
    String payload1 = generateId();
    String dest1 = generateId();

    AtomicReference<IdColumnAndValue> idAndColumn = new AtomicReference<>();

    try {
      eventuateTransactionTemplate.executeInTransaction(() -> {
        insertIntoMessageTableNoBlock(payload1, dest1, Collections.emptyMap())
                .flatMap(idColumnAndValue -> {
                  idAndColumn.set(idColumnAndValue);

                  throw new RuntimeException("123");
                })
                .block();

        return null;
      });

    } catch (Exception e) {
      //ignore
    }

    Assert.assertTrue(getMessages(idAndColumn.get()).isEmpty);
  }
cer commented 3 years ago

A few points:

cer commented 3 years ago

Also, perhaps this is useful for writing Junit tests: https://www.baeldung.com/reactive-streams-step-verifier-test-publisher

dartartem commented 3 years ago

@cer I will prepare answers. But are you sure that it requires investigation, as I wrote in report, I solved issue with transaction, by using transaction explicitly:

https://github.com/eventuate-foundation/eventuate-common/pull/84/files#diff-fd261814dd5c6f9e3c9ec29e2bfe3eb7a22af87e02a1d6b18205d394b11b2256R79

      insertIntoMessageTableNoBlock(payload1, dest1, Collections.emptyMap())
              .flatMap(idColumnAndValue -> {
                idAndColumn.set(idColumnAndValue);
                throw new RuntimeException("Something happened");
              })
              .as(transactionalOperator::transactional)
              .block();

And I think it is better, because transaction scope is much more clear.

Test code that I used in the issue description is example that shows when transaction does not work. Actual test that was present in PR is here:

https://github.com/eventuate-foundation/eventuate-common/pull/84/files#diff-fd261814dd5c6f9e3c9ec29e2bfe3eb7a22af87e02a1d6b18205d394b11b2256R67-R86

cer commented 3 years ago

But are you sure that it requires investigation,

Ok. Defer investigation until writing the example application But in the meantime, please address the points

dartartem commented 3 years ago

Please provide a link to the actual documentation - not a blog post

I found only this: https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/transaction/ReactiveTransactionManager.html

Typically, applications will work with either transactional operators or declarative transaction demarcation through AOP.

Other references from blogs and external resources

Please show the actual usage of @Transactional - where that annotation is used and the Spring component (@Bean)

It is not used anymore. I tried, it did not work, I found other way to use transactions. But, it looked like this:

public class EventuateReactiveTransactionsTemplate extends EventuateTransactionTemplate {
  @Override
  @Transactional
  public <T> T executeInTransaction(Supplier<T> callback) {
    return callback.get();
  }
}
@Configuration
@Import({EventuateSchemaConfiguration.class,
        SqlDialectConfiguration.class,
        EventuateCommonReactiveMysqlConfiguration.class,
        IdGeneratorConfiguration.class})
public class EventuateCommonReactiveDatabaseConfiguration {

  @Bean
  public EventuateTransactionTemplate eventuateTransactionTemplate() {
    return new EventuateReactiveTransactionsTemplate();
  }
  ...

catch (Exception e) { //ignore } doesn't seem wise.`

It was just a draft

block() blocks indefinitely - it should timeout

I will fix

testTransactions() is not very descriptive

I will fix

cer commented 3 years ago

Spring framework documentation:

dartartem commented 3 years ago

@cer thank you