Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to handle Spring transactions inside coroutines?

It is known that Spring transactions are tied to threads: there are thread locals specific for an ongoing transaction. And transactions know nothing about coroutine context. So what if I would like to call a @Transactional method from inside a coroutine: is it safe?

Imagine we have a method inside JobProcessor bean processing a job list. Each job is processed inside async{}. And I would like to update DB after every successful or failed processing using @Transactional methods of bean JobService.

class JobProcessor {
    fun process(jobs: List<MyJob>) =
        jobs.map { job ->
            async {
                try {
                    ....//processing
                    jobService.success(job)
                } catch (t: Throwable) {
                    jobService.failure(job)
                }
        }
} 

class JobService {
    @Transactional
    fun success(job: MyJob) {...}

    @Transactional
    fun failure(job: MyJob) {...}
} 
like image 858
Yaroslav Rudykh Avatar asked Nov 07 '22 02:11

Yaroslav Rudykh


1 Answers

First, please keep in mind that annotating bean methods with @Transactional will not suffice - make sure you enabled declarative transaction handling, e.g. by adding @EnableTransactionManagement to a @Configuration class or by using <tx-annotation-driven /> in XML config.

Regarding your question: There will only be a transactional context while executing Spring bean methods annotated with @Transactional when they are being invoked from a Spring bean outside of their containing class! Declarative transactions in Spring rely on AOP proxy classes being created for the @Transactional annotated classes by Spring. At runtime if a Spring bean A calls a @Transactional method on a Spring bean B, the call will be intercepted by an AOP proxy that transparently spawns a transaction, calls the original method of Spring bean B and commits or rollbacks this transaction afterwards.

Remember: Only external method calls that come in through the proxy will be intercepted – any self-invocation calls, as in your example this.process() calling @Transactional methods this.success() or this.failure() will not start any transaction – even if the method is annotated with @Transactional.

like image 193
Tommy Brettschneider Avatar answered Nov 15 '22 13:11

Tommy Brettschneider