micronaut-data icon indicating copy to clipboard operation
micronaut-data copied to clipboard

Propagate R2DBC transaction in Kotlin coroutines

Open dstepanov opened this issue 3 years ago • 4 comments
trafficstars

Feature description

It would be nice to have transactional propagation working for coroutines and R2DBC

dstepanov avatar Jan 20 '22 06:01 dstepanov

Hi @dstepanov

Could you clarify if this affects normal @JdbcRepository repositories?

I'm trying to use @Transactional on a service with save() calls to a CoroutineCrudRepository but transactions don't seem to work at all (throwing an exception doesn't rollback the changes) unless I make everything blocking.

I've seen #1274 so I'd assume that it should work in 3.3.0-M1, but no luck so far... I can try to provide a minimal repro project if needed.

marcmarcet avatar Feb 16 '22 08:02 marcmarcet

JDBC should work see test service from the PR PersonSuspendRepositoryService. Maybe if you can provide an example or extend existing test with your use-case I can take a look.

dstepanov avatar Feb 16 '22 09:02 dstepanov

Would you mind taking a very quick look to this minimal example?

As it is now, the companyRepository.count()keeps increasing on every POST to /companies. However, if you remove the supend modifier from the repository save() and make it blocking, then works as expected and always returns 0.

https://github.com/marcmarcet/micronaut-data-repro/blob/main/src/main/kotlin/com/example/Application.kt

marcmarcet avatar Feb 16 '22 10:02 marcmarcet

I've been debugging a little bit and it seems that at some point there is a call to TransactionSynchronizationManager.getSynchronizations() that ends up throwing a IllegalStateException("Transaction synchronization is not active") which seems to break the rollback.

But I have no idea what it means 😕

Edit: After digging further...the issue seems to be that the transaction begins in the event loop thread, but then the rollback happens in the executors-io thread, which causes SYNCRONIZATIONS.get() to return null and then throws the exception above.

The only thing that has worked for me so far is to just use non-suspending repository methods with @ExecuteOn(IO).

marcmarcet avatar Feb 16 '22 11:02 marcmarcet