Does Spring Transaction Management Work with Spring WebFlux?

user3303372 :

Does Spring's support for RDBMS transaction management also work in Spring WebFlux?

For example, assuming proper configuration, will a method annotated with the @Transactional annotation use the Spring transaction manager and rollback the transaction if an error occurs?

If transaction management does work, must a @Transactional method actually throw and exception, or must the Mono or Flux return type emit an error signal?

I know JDBC is inherently blocking, and thus any JDBC operations must be bridged from blocking to reactive, or vice-versa.

The Spring transaction manager works by using a ThreadLocal (right?) which I'm assuming won't work in a Reactor environment, since Reactor is frugal with threads and a single thread can swap out one unit of work for another while the first is waiting on I/O. I know Reactor has the Context object which is conceptually similar to ThreadLocal (right?) but I haven't seen any documentation that mentions that transaction makes use of it. Additionally, all JDBC operations that occur within a transaction must use the same Connection which might be tough to do in a reactive context.

My organization has experience with WebFlux and Cassandra, but Cassandra has a native reactive driver.

Thank you!

Liviu Ilea :

AFAIK the Spring standard transaction management does not work with WebFlux.

Using the @Transactional will not work because when an annotated method is called, the transaction mechanism will save the state of the transaction inside the ThreadLocal of the calling thread. As you have said it yourself, this does not work. It blocks AND it shares state.

However, you can use a .runOn(Schedulers.parallel()) in order to send the blocking code to another thread. This way you can have a thread pool with blockable threads which you can configure to have the same size as you DB connection pool.

But even so you still cannot rely on @Transactional because of the way the tread pool reuses the threads. In a standard Servlet architecture you have one thread per HTTP request. When the response is sent back, the thread is stopped, which closes the transaction. In this case though, the Reactor scheduler does not close the threads and reuses them for other events. So even if you CAN block you still have the same problem as before.

You do have the Context option you've mentioned and I think this would work for Mono. I'm not sure if it would work for a Flux (I'm thinking that all events in a Flux share the same context, which is what you don't want).

Another option is to use a Touple2 with T1 as the business object and T2 the transaction context. I cannot recommend this though since you mix business logic with technical stuff and it overcomplicates things.

My best bet would be to do the transaction/connection management yourself:

  1. Get DB connection
  2. Open TX
  3. Do blocking IO stuff
  4. Close TX
  5. Close/release DB connection

all in one code block on a blocking thread.

This will be safer (no leaks) and easier to understand. Also because you basically do everything yourself, you can chose what kind of error handling works best for your scenario.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=36801&siteId=1