Zero business background

Shipping company users print documents at the same time every day to grab orders, resulting in concurrent problems

A problem

When making orders, for example, 200 orders can be printed according to the cost rule, but less than 200 orders are actually printed (it can be seen from the printing log).

Two reasons

Because the number of charge deduction rule boxes and the number of charge deduction rule boxes are not in the same transaction, concurrent operations occur when the customer orders, and optimistic lock is triggered when the log record is printed. Finally, the method of record print log is rolled back, but the number of charge deduction rule boxes is not rolled back. Thus appear less dozen phenomenon

3 Solutions

1 Combing process

The flow chart url https://www.processon.com/view/link/607e273df346fb647a5f5958Copy the code

2 Scheme Selection

1 Place the deduction in the box of expense rules after the invoice, and place the deduction and invoice in the same transaction

Billing depends on the fee rule, that is, the fee rule must run, if the update to the fee rule box after printing, it is the fee rule -> print -> fee rule box update. The advantage of this approach is that the deduction of the fee rule is independent, while the disadvantage is that more code is modified, which requires longer testing due to business sensitivity.

2 Use redis distributed lock

Considering that each order is iterated over when ordering, if each order is locked, then performance may be impressive.

Directly place the deduction and billing log in the same transaction

The advantage of this approach is that the changes are minimal and data consistency is achieved, but the corresponding problem is that the transaction is enlarged and more optimistic locks may occur.

3 Scheme Implementation

Than in more than a few solutions, 3 is more suitable for the actual execution of sorts through some methods, considering the print log records could be rolled back, and send a message in a way that the modification, relaxing way to record a message before print log to send a message, if the whole single now completed, once again to send all the message to be sent

4 Solution execution process

1 The lock table appears after the order rule and single-state large transaction are modified
Three users each hit a large number of the same order, at the same time modify the order rules and single state lead to lock tableCopy the code
2 Narrow the scope of the transaction
Because the transaction is too large, the lock table is generated, so the code is combed, the large transaction is divided into smaller transactions, and finally the transaction control of multiple single transactions is modified into a single transactionCopy the code
3. There is a phenomenon of reducing the buckle of playing single rules
The deduction of the order rule is 156, and the actual order is 200. It is found that the saveOrUpdate method is used in the deduction of the order rule, which does not realize optimistic lock control. After the transaction is elongated, the phenomenon of less deduction occurs because the clippers are not submitted in time.Copy the code
Reduced optimistic lock occurrence
Add lock to the method of modifying order rules and single state to reduce the occurrence of optimistic lock and ensure the more normal execution of the programCopy the code
5 Concurrency control for multiple nodes
Java locks are guaranteed for a single node but not for multiple nodes, which are controlled using optimistic locksCopy the code
6 After optimistic lock error, add a reminder to the user, the order has been printed
The previous prompt is not friendly. We will catch the error of optimistic lock and remind the user that the order has been printedCopy the code
7 Related Issues
1 The transaction does not take effect. If a method with a transaction calls a method with a transaction in the same class, the transaction does not take effect
Because Spring transactions are controlled by dynamic proxies, which essentially generate a proxy class between the calling class and the called class, transactions do not take effect because two method calls in the same class do not generate proxy classes.Copy the code
2 Synchronized a transaction is not locked
@Transactional(rollbackFor = Exception.class) public synchronized List<PrintLog> getExistCntPlacePrintLogs(Map<EdiBooking, PrintingRule.Result> resultMap, Set<String> messages, String identify, String remark, EdiBooking booking) { Container container = countContainerNum(booking); .Copy the code

As with the first point, transactions are executed in the sequence of methods loaded by the lock, so transactions cannot be locked if their operations are not inside the lock.

3 The optimistic lock is not triggered
The version of saveOrUpdate does not trigger the optimistic lock. The version of saveOrUpdate does not trigger the optimistic lock.Copy the code

5 Final Scheme

Complete the overall modification through distributed lock + transaction control.Copy the code