Skip to content

Reactive Spring Modulith #174

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
nkolosnjaji opened this issue Mar 29, 2023 · 10 comments
Open

Reactive Spring Modulith #174

nkolosnjaji opened this issue Mar 29, 2023 · 10 comments
Assignees
Labels
in: event publication registry Event publication registry type: enhancement Major enhanvements, new features

Comments

@nkolosnjaji
Copy link
Contributor

Not sure for JPA, but it would be nice to have reactive JDBC and Mongo starters.

@odrotbohm
Copy link
Member

I would have to investigate how the Spring event publication would actually interact with reactive infrastructure. Unless there's a technical need to really implement the event publication repository in a reactive way, I think it should just be fine to persist event publications in an imperative way.

@odrotbohm odrotbohm self-assigned this Mar 31, 2023
@odrotbohm odrotbohm added in: event publication registry Event publication registry meta: investigating Stuff that needs more investigation labels Mar 31, 2023
@vincentditlevinz
Copy link

vincentditlevinz commented Apr 30, 2023

Hmm, actually making reactive and non reactive JDBC working together in the same project is not straightforward.
Finally I gave up and use a simple @eventlistener for the moment (you can see this here)

So having a Reactive implementation would be a great addition 👍

@pcuriel
Copy link

pcuriel commented May 23, 2023

Interested in this as well.

Regarding Spring Event Publication, even though it is not fully integrated in the reactive stack (see this Open issue), at least some support for non-blocking listeners exists (see this completed issue).

As detailed in the second issue, declaring listeners like this works:

    @EventListener
    public Mono<Void> handleEvent(Object event) {
        ...
    }

So I think it should be feasible to plug this with a reactive repository.

@vincentditlevinz
Copy link

So I think it should be feasible to plug this with a reactive repository.

Making it work is probably "feasible", but not obvious. In a simple implementation using the provided jdbc starter for the persistence of events, the r2dbc transaction manager complains about handling jdbc queries. Having an r2dbc starter implementation is probably the most straightforward and obvious solution for this first pitfall.

@denniseffing
Copy link

denniseffing commented Aug 1, 2023

I am interested in this as well. This is currently blocking the adaption of Spring Modulith for us.

@pcuriel As of Spring Framework 6.1, Spring Event Publication is fully integrated in the reactive stack.
See spring-projects/spring-framework@450cc21

@denniseffing
Copy link

denniseffing commented Aug 3, 2023

@odrotbohm I think persisting events in an imperative way may be sufficient but (and this is a big one) it is really hard to set up because there are many things to consider.

Configuring a blocking and a reactive data access layer
This is the most obvious one: We have to configure two separate data layers in our application. When we use Spring Boot this means we have to set all of these properties to trigger the correct auto-configurations.

spring.r2dbc.url=r2dbc:postgresql://localhost/test
spring.r2dbc.username=dbuser
spring.r2dbc.password=dbpass

# either jpa/jdbc for spring-modulith-events
spring.datasource.url=jdbc:mysql://localhost/test
spring.datasource.username=dbuser
spring.datasource.password=dbpass

# or mongodb
spring.data.mongodb.host=mongoserver1.example.com
spring.data.mongodb.port=27017
spring.data.mongodb.additional-hosts[0]=mongoserver2.example.com:23456
spring.data.mongodb.database=test
spring.data.mongodb.username=user
spring.data.mongodb.password=secret

Multiple Transaction Manager Beans
Spring R2DBC provides a ReactiveTransactionManager while Spring JDBC/JPA/Mongo provides a PlatformTransactionManager. When we use Spring Boot, TransactionManager beans are auto-configured for us. Since we need Spring Data JDBC, JPA or MongoDB on the classpath for Spring Modulith, this means that we have two TransactionManager beans.

I am not even sure if Spring Boot auto-configures both at the same time. As far as I can tell by only viewing code, this entirely depends on the execution order of the auto-configurations. The DataSourceTransactionManagerAutoConfiguration is very strict, the DataSourceTransactionManager has a @ConditionalOnMissingBean(TransactionManager.class). This means that the bean is only auto-configured, if the ReactiveTransactionManager was not auto-configured beforehand.

The R2dbcTransactionManagerAutoConfiguration is less restrictive because the R2dbcTransactionManager has a @ConditionalOnMissingBean(ReactiveTransactionManager.class). This means it is auto-configured even if a PlatformTransactionManager was auto-configured beforehand.

Spring Modulith Events MongoDB auto-configures a MongoTransactionManager which is a PlatformTransactionManager as well. We don't have any conflicts here due to a very forgiving @ConditionalOnMissingBean without super type as parameter. So Spring Boot should auto-configure a R2dbcTransactionManager at the same time without any issues.

Event listeners can not be reactive as long as the event publication repository is blocking
Since our application code is reactive, we have to publish events with the transactional context in the source property of our events. This is required, because @TransactionalEventListener accesses the transactional context this way. If it is missing, the event listeners will not be executed at all (unless fallbackExecution is set to true). This will be possible starting from Spring Framework 6.1 (see spring-projects/spring-framework@450cc21).

Now we have to consider the @Transactional annotation of our event listener itself. Usually, we would like to have a reactive event listener that returns some kind of Publisher. This implicates that we have to use the R2dbcTransactionManager again. However, this is not possible, because then the EventPublicationRepository.create methods runs in a reactive transaction context even though the repository itself is not reactive at all and requires a PlatformTransactionManager instead. I guess this is the issue @vincentditlevinz encountered. A workaround for this would be not using a Publisher as return type and configuring the qualifier for the transaction manager to use, like this: @Transactional("transactionManager").

Lucky for us, the qualifiers for the reactive and blocking TransactionManager beans are actually different:

The bottom-line is: Event listeners cannot be reactive, they have to be blocking due to the transaction management. As far as I understand this shouldn't be an issue as long as event listeners themselves are not doing any blocking I/O or similar CPU-intense tasks. Even then, declaring them as @Async should work.

@denniseffing
Copy link

denniseffing commented Aug 4, 2023

I have a working example of a reactive Spring application that uses Spring Modulith. Please keep in mind that the sample uses snapshot versions of Spring Framework 6.1 for the fix of spring-projects/spring-framework#21025.
See https://github.com/denniseffing/spring-modulith-test.

I had to jump through a lot of hoops to get this working:

  • When both Spring Data JDBC and Spring Data R2DBC are on the classpath, Spring Boot disables the auto-configuration of a blocking DataSource. I had to re-enable it manually.
  • When both Spring Data JDBC and Spring Data R2DBC are on the classpath, Spring Data JDBC scans ReactiveCrudRepositorys as well and throws an InvalidDataAccessApiUsageException with the message Reactive Repositories are not supported by JDBC. To fix this, I had to limit the Spring Data JDBC repository scan manually to the Spring Modulith package.
  • Due to two TransactionManager beans, the @Transactional annotations in the JdbcEventPublicationRepository cause a NoUniqueBeanDefinitionException. I don't know if it's possible to declare a bean that was auto-configured by Spring Boot as @Primary, so I re-defined the entire TransactionManager bean instead. This is very hacky, because I copy-pasted the definition from the DataSourceTransactionAutoConfiguration but it works.

I don't think it is feasible to do stuff like this in a production application. It is way too convoluted and complex for too minimal gain. I would recommend dropping reactive programming entirely or dropping Spring Modulith instead. At least until Spring Modulith has first-class support for reactive applications.

@odrotbohm Sorry, the example is in Kotlin again 😇

@odrotbohm
Copy link
Member

I can only recommend to hold off experimenting with this for now. As you have already experienced, the current arrangement is not designed to work well on top of a reactive data store. This is primarily due to the way we integrate with Spring's event publication mechanism. It's currently implemented in a very straight-forward way, but at the same time, is exposed to limitations that affect the extent in which we can interact with the data store.

I've been in touch with the Framework team and experts on reactive programming in our broader team, and I think there is a path forward to get this to work cleanly. That said, we're close to 1.0 RC / GA, which means that I'll only get to work on this post those releases. Big feature candidate for 1.1, though.

@odrotbohm odrotbohm added the type: enhancement Major enhanvements, new features label Aug 9, 2023
@banterCZ
Copy link

I am visiting this discussion a year later, so I would like to ask what the current plans are for reactive support.

@odrotbohm
Copy link
Member

We currently don't have any plans to move forward with this.

@odrotbohm odrotbohm removed the meta: investigating Stuff that needs more investigation label Nov 13, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: event publication registry Event publication registry type: enhancement Major enhanvements, new features
Projects
None yet
Development

No branches or pull requests

6 participants