Skip to content

bugfix: fix global lock batch acquire false-failure on Dameng(DM)#8145

Open
lhozy wants to merge 1 commit into
apache:2.xfrom
lhozy:fix/dm-batch-lock-acquire
Open

bugfix: fix global lock batch acquire false-failure on Dameng(DM)#8145
lhozy wants to merge 1 commit into
apache:2.xfrom
lhozy:fix/dm-batch-lock-acquire

Conversation

@lhozy

@lhozy lhozy commented Jun 17, 2026

Copy link
Copy Markdown

PR: bugfix: fix global lock batch acquire false-failure on Dameng(DM)

Ⅰ. Describe what this PR does

Fix LockStoreDataBaseDAO#doAcquireLocks so that batch global-lock acquisition no longer produces a false "lock acquire failed" on databases (e.g. Dameng / DM) whose JDBC driver returns an executeBatch() array whose length differs from the number of batched statements.

Fixes #8144.

Ⅱ. Why make this change

doAcquireLocks judged batch success with:

return ps.executeBatch().length == lockDOs.size();

The JDBC spec does not guarantee executeBatch() returns exactly one element per batched statement (elements may be counts, SUCCESS_NO_INFO, or EXECUTE_FAILED, and some drivers aggregate). The DM driver aggregates the per-row results (DmdbPreparedStatement.executeBatchByRowExecuteRetInfo.union), so the returned array length ≠ statement count.

Consequence on DM: when a branch locks ≥ 2 rows, the lock rows are inserted successfully but the length == size check returns false, so the caller rolls back the inserted locks and the branch register is reported as a lock conflict. Single-row branches (non-batch doAcquireLock) are unaffected, so the bug only manifests for multi-row branches — making AT mode unusable on DM for typical multi-row business transactions.

Ⅲ. The change

server/src/main/java/org/apache/seata/server/storage/db/lock/LockStoreDataBaseDAO.java

             for (LockDO lockDO : lockDOs) {
                 ...
                 ps.addBatch();
             }
-            return ps.executeBatch().length == lockDOs.size();
+            // Do not rely on executeBatch().length == size: per the JDBC spec the
+            // length is not guaranteed to equal the number of statements, and some
+            // drivers (e.g. Dameng/DM) aggregate the per-statement results, returning
+            // an array of a different length. Detect failure via EXECUTE_FAILED instead;
+            // real conflicts (duplicate row_key) still throw SQLIntegrityConstraintViolationException
+            // and are handled by the catch block below.
+            int[] result = ps.executeBatch();
+            for (int updated : result) {
+                if (updated == java.sql.Statement.EXECUTE_FAILED) {
+                    return false;
+                }
+            }
+            return true;
         } catch (SQLIntegrityConstraintViolationException e) {
             LOGGER.error("Global lock batch acquire error: {}", e.getMessage(), e);
             // return false,let the caller go to conn.rollback()
             return false;

Ⅳ. How tested

  • Existing behavior preserved on databases that return one element per statement (MySQL, Oracle, PostgreSQL…): no EXECUTE_FAILED → returns true; genuine duplicate-row_key conflicts still throw SQLIntegrityConstraintViolationException → handled → false.
  • On DM8 (store.mode=db): a branch acquiring multiple row locks now succeeds; a cross-resource global transaction with multi-row branches both commits and rolls back correctly, where before every multi-row branch register failed with Global lock batch acquire failed.

Ⅴ. Checklist

  • Add/keep unit tests (consider a test asserting doAcquireLocks returns true when executeBatch() returns an aggregated/short array).
  • Changelog entry.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes a JDBC-driver compatibility issue in Seata Server’s DB lock store where batch global-lock acquisition could incorrectly report failure on databases (e.g., Dameng/DM) whose executeBatch() result array length doesn’t match the number of batched statements, even when inserts succeeded.

Changes:

  • Replace executeBatch().length == lockDOs.size() with logic that detects batch failure via Statement.EXECUTE_FAILED.
  • Add explanatory comments documenting the JDBC-spec nuance and DM driver behavior.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +399 to +403
for (int updated : result) {
if (updated == java.sql.Statement.EXECUTE_FAILED) {
return false;
}
}
// aggregate the per-statement results, returning an array of a different length.
// Detect failure via EXECUTE_FAILED instead; real conflicts (duplicate row_key) still
// throw SQLIntegrityConstraintViolationException and are handled by the catch block below.
int[] result = ps.executeBatch();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

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

2 participants