lhozy opened a new issue, #8144:
URL: https://github.com/apache/incubator-seata/issues/8144

   # [Bug] Global lock batch acquire always fails on Dameng (DM) — 
`LockStoreDataBaseDAO.doAcquireLocks` relies on `executeBatch().length`
   
   ## Ⅰ. Describe the bug
   
   When the Seata Server uses **DB store mode** with a **Dameng (DM)** 
database, any branch transaction that needs to acquire **two or more row 
locks** in a single `BranchRegister` **always fails** to acquire the global 
lock, even though the `lock_table` is empty and there is no real conflict.
   
   The client side gets:
   
   ```
   io.seata.rm.datasource.exec.LockConflictException: get global lock fail, 
xid:..., lockKeys:XXX:1;YYY:2
   ```
   
   The server side logs:
   
   ```
   LockStoreDataBaseDAO : Global lock batch acquire failed, xid ... branchId 
... pks [1, 2]
   AbstractExceptionHandler : this request cannot acquire global lock ...
   ```
   
   A branch that locks only **one** row works fine. So in practice **any 
business transaction that modifies 2+ rows (master/detail, entity + i18n text, 
etc.) can never commit**, which makes AT mode effectively unusable on DM for 
real workloads.
   
   ## Ⅱ. Root cause
   
   
`org.apache.seata.server.storage.db.lock.LockStoreDataBaseDAO#doAcquireLocks`:
   
   ```java
   protected boolean doAcquireLocks(Connection conn, List<LockDO> lockDOs) 
throws SQLException {
       ...
       for (LockDO lockDO : lockDOs) {
           ... ps.addBatch();
       }
       return ps.executeBatch().length == lockDOs.size();   // <-- here
   }
   ```
   
   It judges batch success by `executeBatch().length == lockDOs.size()`.
   
   The DM JDBC driver (`DmdbPreparedStatement`) implements batch execution via 
`executeBatchByRow()` and **aggregates** the per-statement results through 
`ExecuteRetInfo.union(...)`, so `executeBatch()` returns an `int[]` whose 
**length does not equal the number of batched statements**.
   
   As a result:
   - The lock rows **are actually inserted** into `lock_table`.
   - But `executeBatch().length == lockDOs.size()` evaluates to **false**.
   - `doAcquireLocks` returns `false`, the caller does `conn.rollback()` (the 
inserted lock rows are discarded), and the branch register is reported as a 
lock conflict.
   
   Single-row branches take the `doAcquireLock` (non-batch) path, which does 
not have this check, so they succeed — which is why the failure only appears 
for multi-row branches.
   
   > Per the JDBC spec, the return value of `Statement.executeBatch()` is an 
array of update counts where each element may be a count, `SUCCESS_NO_INFO 
(-2)`, or `EXECUTE_FAILED (-3)`. Assuming `length == number of statements` is 
not guaranteed across drivers; relying on it is the bug.
   
   ## Ⅲ. How to reproduce
   
   1. Seata Server `store.mode=db`, store DB = Dameng (DM8).
   2. Any AT-mode business transaction whose single branch updates/inserts **≥ 
2 rows** (so the branch reports `lockKey` with 2+ pks).
   3. Branch register fails with `Global lock batch acquire failed` against an 
empty `lock_table`.
   
   ## Ⅳ. Expected behavior
   
   The batch lock acquire should succeed when the lock rows are inserted 
without conflict, regardless of the driver-specific length of the 
`executeBatch()` return array. Real conflicts (duplicate `row_key`) still 
surface as `SQLIntegrityConstraintViolationException` and are already handled.
   
   ## Ⅴ. Environment
   
   - Seata version: **2.6.0** (the same code is present on the current `2.x` 
branch).
   - DB store: Dameng (DM8), DM JDBC driver `DmJdbcDriver18`.
   - The fix is proposed in the accompanying PR.
   
   ## Ⅵ. Suggested fix
   
   Stop relying on `executeBatch().length`. Detect failure via 
`Statement.EXECUTE_FAILED` instead:
   
   ```java
   int[] result = ps.executeBatch();
   for (int updated : result) {
       if (updated == java.sql.Statement.EXECUTE_FAILED) {
           return false;
       }
   }
   return true;
   ```
   
   This is correct for drivers that return one element per statement and for 
drivers (like DM) that aggregate the result array, while preserving the 
existing conflict handling (the `catch 
(SQLIntegrityConstraintViolationException ...)` branch).
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to