Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

using Async inside a transaction in Spring application

I have a Spring application which updates particular entity details in MySQL DB using a @Transactional method, And within the same method, I am trying to call another endpoint using @Async which is one more Spring application which reads the same entity from MySql DB and updates the value in redis storage.

Now the problem is, every time I update some value for the entity, sometimes its updated in redis and sometimes it's not.

When I tried to debug I found that sometimes the second application when it reads the entity from MySql is picking the old value instead of updated value.

Can anyone suggest me what can be done to avoid this and make sure that second application always picks the updated value of that entity from Mysql?

like image 650
Poornesh Sharma B Avatar asked Dec 04 '22 19:12

Poornesh Sharma B


1 Answers

The answer from M. Deinum is good but there is still another way to achieve this which may be simpler for you case, depending on the state of your current application.

You could simply wrap the call to the async method in an event that will be processed after your current transaction commits so you will read the updated entity from the db correctly every time.

Is quite simple to do this, let me show you:

import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionSynchronization;
import org.springframework.transaction.support.TransactionSynchronizationManager;

 @Transactional
public void doSomething() {

    // application code here

    // this code will still execute async - but only after the
    // outer transaction that surrounds this lambda is completed.
    executeAfterTransactionCommits(() -> theOtherServiceWithAsyncMethod.doIt());

    // more business logic here in the same transaction
}

private void executeAfterTransactionCommits(Runnable task) {
    TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
        public void afterCommit() {
            task.run();
        }
    });
}

Basically what happens here is that we supply an implementation for the current transaction callback and we only override the afterCommit method - there are others methods there that might be useful, check them out. And to avoid typing the same boilerplate code if you want to use this in other parts or simply make the method more readable I extracted that in a helper method.

like image 99
cristian.andrei.stan Avatar answered Dec 11 '22 15:12

cristian.andrei.stan