The problem

The same serial number was generated during the stress test. According to log analysis, select… For update does not lock a row of data, resulting in serial number duplication

screening

  1. Select… Select * from update where data is locked; select * from update where data is locked; SQL such as for update, UPDATE, and DELETE need to wait for the lock before performing subsequent operations.
  2. Doubt whether transactions work first, use the TransactionSynchronizationManager# getCurrentTransactionName to print the current affairs, result unexpectedly is null!!!!!! No transactions are naturally mutually exclusive, which explains why duplicate ordinals are generated.
  3. The rest is why the @transactional annotation was added but no transactions were started.
  4. Check the code with the problem. The problem code is shown below. This is the design pattern of a chain of responsibility. For any node in the chain of responsibility, doProcess is called to execute the logic of the subclass itself, and the next SUCC node is executed if successful, and the next fail node is executed if an error is reported, until the end of the chain of responsibility.
  5. Find the doProcess method that declares the transaction, which is called in the process with this.doprocess. We all know that Spring’S AOP transactions must use another object for them to take effect. The method form is what works and that’s why @Transactional doesn’t work. In short, you cannot call up @transactional methods in the same class using methods that do not have @Transactional
public abstract class BaseController {
    private ScBaseController succNext = null;
    private ScBaseController failNext = null;

    public DataMessage process(DataMessage msg) throws ScBaseException {
        DataMessage respMsg = this.doProcess(msg);
        // The execution succeeded
        if (ExecuteResultEnum.SUCCESS.equals(respMsg.getExecuteResult())) {
            if (this.succNext ! =null) {
                return this.succNext.process(respMsg);
            } else {
                returnrespMsg; }}// Failed to execute
        else {
            if (this.failNext ! =null) {
                return this.failNext.process(respMsg);
            } else {
                returnrespMsg; }}}@Transactional     // Transaction annotations are not used here
    public abstract DataMessage doProcess(DataMessage msg);
Copy the code

lesson

  1. It’s important to keep in mind that you can’t call @Transactional methods in the same class using non-transactional methods. This is due to Spring’s dynamic AOP mechanism. As to why dynamic AOP is the way it is, a future article on this topic will explain in more detail.

  2. Fortunately, the problem was discovered during stress tests and did not cause an online malfunction. Some problems cannot be explained by a few transactions, and the robustness of the system must be verified under high concurrency and stress tests.