Skip to content

Global lock batch acquire always fails on Dameng(DM): LockStoreDataBaseDAO.doAcquireLocks relies on executeBatch().length #8144

@lhozy

Description

@lhozy

[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:

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:

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).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions