preface

Recent chat with friends, he said that he met in outsourcing projects, distributed transaction problem, ask I have no solution, I could talk to him directly, distributed transaction scheme online a lot of, what TCC and reliable message consistency, best to inform, direct online to find a try, such as direct use ali seata. But I did not do this, because distributed transactions, is a very complex topic, when the real implementation, will find that sometimes a variety of distributed solutions are used together, rather than one solution went to the black.

Therefore, I told him that if he could not use distributed transactions, he could not use them as much as possible. Later, I asked him about his business scenario, which was not very complicated, that is, he invited friends to register, and then he could increase the points. The pseudo code for realizing logic of friends was roughly as follows

@Transactional(rollbackFor = Exception.class) public Boolean inviteUser(..) { userService.add(..) ; integralService.addIntegration(.. , 20)}

IntegralService is a remote integralService, and 20 is the incremental integral value. This code looks fine at first glance, and I think a lot of people would write it this way. Later, I asked my friend whether the following scenarios were allowed in your business scenario

  • Do you allow invited users to successfully enter the database, but fail to enter the database?
  • Do you allow the invited user to enter the database failed, but the integration to enter the database successful?

The friend thought about it for a while and said the second way is not allowed. The first way can increase the integral by compensating.

Looking back at this code, I posed the following two questions for readers to ponder

  • Is there a problem with this code if adding a credit request takes a long time?
  • Is there a problem with this code if there is an exception for adding credits because of network jitter?

Here’s what I think

  • This time is too long and can lead to long transactions, which, in concurrency scenarios, may result in database connections not being released
  • The network jitter exception may cause the add logic of the user service to be rolled back

Solution time is too long, some friends may think of using asynchronous way, integral jitter abnormal, can be added by the fuse mechanism, such as the integral timeout did not respond, directly fuse

Today I’m going to say one more scenario, which is to call the transaction after it has been committed. Rory is going to talk a lot. I’m just about to get to the point

How to make proper remote calls in Spring transactions

Through Spring’s transaction synchronization manager

What the hell is this? This is my literal translation. What does it look like

org.springframework.transaction.support.TransactionSynchronizationManager

What’s the use of this? You can register a transaction synchronizer with it. This transaction synchronizer allows you to do something after the transaction commits. The core code is as follows

TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() { @Override public Void afterCommit() {// Do the business you want}});

If you look at the code, I think you all know how to change the above invite users to add points, right

@Transactional(rollbackFor = Exception.class) public Boolean inviteUser(..) { userService.add(..) ; TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() { @Override public void afterCommit() { integralService.addIntegration(.. , 20)}});

But we found that no, every time to write such a piece of code, looking at it is not very disgusting, there is no reform plan. The answer is yes, through annotations + AOP to integrate the implementation, the specific implementation logic, you can see the following link in the demo

com.github.lybgeek.transactional

I’m not going to post the code here, but why not, because I’m going to introduce another scenario, which is an event driven implementation based on Spring

Through TransactionalEventListener annotations + ApplicationEventPublisher

This is spring event driven, or the observer is implemented, but TransactionalEventListener annotations are spring4.2 version before provide annotations

In this way, how do you modify the above implementation of inviting users to add credits?

1. In the method of inviting users to register, publish events

The pseudocode is as follows

@Transactional(rollbackFor = Exception.class) public Boolean inviteUser(..) { userService.add(..) ; applicationEventPublisher.publishEvent(..) ; });

2. Write a transaction listener that triggers the implementation of adding credits

The pseudocode is as follows

@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT) public void addIntegration(..) { integralService.addIntegration(.. , 20)}

One detail to note here is that the parameters of the listening event should be the same as the parameters of the published event

3, achieve the core source code

@Override public void onApplicationEvent(ApplicationEvent event) { if (TransactionSynchronizationManager.isSynchronizationActive()) { TransactionSynchronization transactionSynchronization = createTransactionSynchronization(event); TransactionSynchronizationManager.registerSynchronization(transactionSynchronization); } else if (this.annotation.fallbackExecution()) { if (this.annotation.phase() == TransactionPhase.AFTER_ROLLBACK && logger.isWarnEnabled()) { logger.warn("Processing " + event + " as a fallback execution on AFTER_ROLLBACK phase"); } processEvent(event); } else { // No transactional event execution at all if (logger.isDebugEnabled()) { logger.debug("No transaction is active - skipping " + event); }}}

Don’t know you found no, he is essentially used TransactionSynchronizationManager, just encapsulate again for him

conclusion

After communicate with friends, find their the outsourcing project developers, and there were only three, and then a split for 10 to service, I asked him the outsourcing business is very complicated, he said it’s good, I ask he says business is not complicated, developers also not much, why not use monomer architecture, and want to use service. The answer he gave me was that Party A’s father thought that their project would carry a large amount of business in the future, so they had to use micro-service, and the mainstream technology stack is micro-service now. Should I say, on hearing this answer, that I’m overengineered or visionary? Technology changes so fast that you never know what will come next. Architecture is never a one-step process, it evolves gradually

The demo link

https://github.com/lyb-geek/springboot-learning/tree/master/springboot-transation-after-commit