During the development process, a bug was encountered. The reason for the bug was that the spring transaction submitted production messages later than the message queue, resulting in incorrect data obtained when the message queue consumes messages. This article introduces the emergence of problems and the step-by-step resolution process.
1. Problem arising:
Scenario restore: A method in the interface, first modify the order status, and then produce messages from the message queue. The consumer of the message queue obtains the message detection order status and finds that the order status has not changed.
Code:
@Service(orderApi)public class OrderApiImpl implements OrderApi { @Resource MqService mqService; @OrderDao orderDao; public void push(String orderId) { // Update the order status, the previous status was 1 updateStatus(orderId, 3); // Generate the message mqService.produce(orderId); } public viod updateStatus(String orderId, Integer status) { orderDao.updateStatus(orderId, status); }}The reason for the problem: All methods in orderApi have transactions, and the transaction type PROPAGATION_REQUIRED, so the operation of the push method on the data will be submitted after all the push code is executed. Before the transaction is submitted, the message queue message has been generated, so the status of the order consumed in the message queue query from the database may be 1. In order to make the bug more obvious, you can add it at the end of the push method:
try { Thread.sleep(10000);} catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace();}In this way, you will find that the order status must have not been modified when the consumption message is consumed.
2. Solution to the problem:
Solution: When updating data, create a new thing to ensure that after the update code is executed, the transaction that updates the database has been committed. (Make sure the database operation has been submitted before the message is generated)
According to the above scheme, the first thing I think of is to directly modify the transaction type of the updateStatus method; I changed the transaction type of this method to PROPAGATION_REQUIRES_NEW (create a new transaction, if the transaction currently exists, suspend the current transaction).
But there are two inappropriate things to do:
1. Forced modification of updateStaus transaction type may affect other processes.
2. Not working, no new transaction was created in the updateStaus method.
Explanation about the second point: spring adds transactions through BeanNameAutoProxyCreator, which only adds transactions to bean objects. Now calling methods inside the class will not trigger the creation of new things.
So after trying the above, I created a new class:
@Service("orderExtApi")public class OrderExtApiImpl { @Resource OrderApi orderApi; public void updateStatusNewPropagation(String orderId) { orderApi.updateStatus(orderId); }}and add transaction PROPAGATION_REQUIRES_NEW for updateStatusNewPropagation method
This class is just to create a new transaction for the updateStaus method in orderApi.
OK, so far the bug has been resolved.
However, there are still problems in the code: the operation of the database has been submitted, and if there is an exception in the production message, it is still wrong for the business logic. Therefore, it is necessary to detect whether the message generation is completed.
The code in the final orderApi is as follows:
@Service(orderApi)public class OrderApiImpl implements OrderApi { @Resource MqService mqService; @Resource OrderDao orderDao; @Resource OrderExtApiImpl orderExtApi; public void push(String orderId) { // Update the order status, the previous status was 1 orderExtApi.updateStatusNewPropagation(orderId, 3); // Generate message --produce will detect whether an exception occurs. When 1 is returned, it means that the production message is successful. Response response = mqService.produce(orderId); if (response.getCode() != 1) { log.info("Message Queue Production Message Exception: " + response.getErrorMsg()) // Production Message Exception, reset the status and wait for the next re-execution orderExtApi.updateStatusNewPropagation(orderId, 1); } } public viod updateStatus(String orderId, Integer status) { orderDao.updateStatus(orderId, status); }}The above is all the content of this article. I hope it will be helpful to everyone's learning and I hope everyone will support Wulin.com more.