using Async inside a transaction in Spring application

狂风中的少年 提交于 2020-12-29 04:58:16

问题


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?


回答1:


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.




回答2:


The solution is not that hard, apparently you want to trigger and update after the data has been written to the database. The @Transactional only commits after the method finished executing. If another @Async method is called at the end of the method, depending on the duration of the commit (or the actual REST call) the transaction might have committed or not.

As something outside of your transaction can only see committed data it might see the updated one (if already committed) or still the old one. This also depends on the serialization level of your transaction but you generally don't want to use an exclusive lock on the database for performance reason.

To fix this the @Async method should not be called from inside the @Transactional but right after it. That way the data is always committed and the other service will see the updated data.

@Service
public class WrapperService {

    private final TransactionalEntityService service1;
    private final AsyncService service2;

    public WrapperService(TransactionalEntityService service1, AsyncService service2) {
        this.service1=service1;
        this.service2=service2;
    }

    public updateAndSyncEntity(Entity entity) {
       service1.update(entity); // Update in DB first
       service2.sync(entity); // After commit trigger a sync with remote system
    }
}

This service is non-transactional and as such the service1.update which, presumable, is @Transactional will update the database. When that is done you can trigger the external sync.



来源:https://stackoverflow.com/questions/51833306/using-async-inside-a-transaction-in-spring-application

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!