This is an automated email from the ASF dual-hosted git repository.
funky-eyes pushed a commit to branch 2.x
in repository https://gitbox.apache.org/repos/asf/incubator-seata.git
The following commit(s) were added to refs/heads/2.x by this push:
new cb9c092cc0 bugfix: fix global lock batch acquire false-failure on
Dameng(DM) (#8145)
cb9c092cc0 is described below
commit cb9c092cc062f479117726bb8833ea2b6e15286d
Author: LH <[email protected]>
AuthorDate: Mon Jun 22 16:57:05 2026 +0800
bugfix: fix global lock batch acquire false-failure on Dameng(DM) (#8145)
---
changes/en-us/2.x.md | 4 +
changes/zh-cn/2.x.md | 3 +
.../storage/db/lock/LockStoreDataBaseDAO.java | 13 ++-
.../lock/LockStoreDataBaseDAOBatchAcquireTest.java | 130 +++++++++++++++++++++
4 files changed, 149 insertions(+), 1 deletion(-)
diff --git a/changes/en-us/2.x.md b/changes/en-us/2.x.md
index 280ba232df..b3e7ee1683 100644
--- a/changes/en-us/2.x.md
+++ b/changes/en-us/2.x.md
@@ -24,6 +24,8 @@ Add changes here for all PR submitted to the 2.x branch.
### bugfix:
+- [#8145](https://github.com/apache/incubator-seata/pull/8145) fix global lock
batch acquire false-failure on Dameng(DM)
+
### optimize:
@@ -45,7 +47,9 @@ Thanks to these contributors for their code commits. Please
report an unintended
<!-- Please make sure your Github ID is in the list below -->
- [slievrly](https://github.com/slievrly)
+- [lhozy](https://github.com/lhozy)
- [Zhengcy05](https://github.com/Zhengcy05)
+
Also, we receive many valuable issues, questions and advices from our
community. Thanks for you all.
diff --git a/changes/zh-cn/2.x.md b/changes/zh-cn/2.x.md
index 37bbf1cacb..07018ef3fe 100644
--- a/changes/zh-cn/2.x.md
+++ b/changes/zh-cn/2.x.md
@@ -24,6 +24,8 @@
### bugfix:
+- [#8145](https://github.com/apache/incubator-seata/pull/8145)
修复达梦(DM)数据库下全局锁批量获取被误判为失败的问题
+
### optimize:
@@ -45,6 +47,7 @@
<!-- 请确保您的 GitHub ID 在以下列表中 -->
- [slievrly](https://github.com/slievrly)
+- [lhozy](https://github.com/lhozy)
- [Zhengcy05](https://github.com/Zhengcy05)
diff --git
a/server/src/main/java/org/apache/seata/server/storage/db/lock/LockStoreDataBaseDAO.java
b/server/src/main/java/org/apache/seata/server/storage/db/lock/LockStoreDataBaseDAO.java
index f1c5cea86e..aac85454f7 100644
---
a/server/src/main/java/org/apache/seata/server/storage/db/lock/LockStoreDataBaseDAO.java
+++
b/server/src/main/java/org/apache/seata/server/storage/db/lock/LockStoreDataBaseDAO.java
@@ -390,7 +390,18 @@ public class LockStoreDataBaseDAO implements LockStore {
ps.setInt(8, lockDO.getStatus());
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()
diff --git
a/server/src/test/java/org/apache/seata/server/storage/db/lock/LockStoreDataBaseDAOBatchAcquireTest.java
b/server/src/test/java/org/apache/seata/server/storage/db/lock/LockStoreDataBaseDAOBatchAcquireTest.java
new file mode 100644
index 0000000000..cfe18a5f94
--- /dev/null
+++
b/server/src/test/java/org/apache/seata/server/storage/db/lock/LockStoreDataBaseDAOBatchAcquireTest.java
@@ -0,0 +1,130 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.seata.server.storage.db.lock;
+
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.ArrayList;
+import java.util.List;
+import javax.sql.DataSource;
+
+import org.apache.seata.common.ConfigurationKeys;
+import org.apache.seata.config.ConfigurationFactory;
+import org.apache.seata.core.model.LockStatus;
+import org.apache.seata.core.store.LockDO;
+import org.apache.seata.server.BaseSpringBootTest;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+import org.springframework.context.ApplicationContext;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+/**
+ * Unit test for {@link LockStoreDataBaseDAO#doAcquireLocks} batch-result
handling.
+ *
+ * <p>Batch lock acquisition must not rely on {@code executeBatch().length ==
lockDOs.size()}.
+ * The JDBC spec does not guarantee that the returned array has one element
per batched
+ * statement, and some drivers (e.g. Dameng/DM) aggregate the per-statement
results into an
+ * array of a different length. The previous length comparison then evaluated
to {@code false}
+ * even though every row was inserted, producing a bogus "global lock batch
acquire failed"
+ * for any branch that acquires two or more locks at once.
+ *
+ * <p>The JDBC layer is mocked, so no database is required and the test runs
in CI by default
+ * (unlike {@link LockStoreDataBaseDAOTest}, which is gated behind a live DB).
+ */
+public class LockStoreDataBaseDAOBatchAcquireTest extends BaseSpringBootTest {
+
+ private static LockStoreDataBaseDAO lockStoreDataBaseDAO;
+
+ @BeforeAll
+ public static void setUp(ApplicationContext context) {
+ // The DAO's static CONFIG needs the Spring-Boot test context (from
BaseSpringBootTest)
+ // to initialize; feed it a db type so the constructor passes, then
mock the data source
+ // since this test stubs the JDBC layer rather than hitting a real
database.
+
ConfigurationFactory.getInstance().putConfig(ConfigurationKeys.STORE_DB_TYPE,
"mysql");
+
ConfigurationFactory.getInstance().putConfig(ConfigurationKeys.LOCK_DB_TABLE,
"lock_table");
+ lockStoreDataBaseDAO = new
LockStoreDataBaseDAO(mock(DataSource.class));
+ }
+
+ /**
+ * Driver aggregates the batch result into an array shorter than the
number of batched
+ * statements, with no {@link Statement#EXECUTE_FAILED}. Acquisition must
still succeed.
+ * Before the fix this returned {@code false}.
+ */
+ @Test
+ public void doAcquireLocksSucceedsWhenDriverAggregatesBatchResult() throws
SQLException {
+ // 3 statements batched, driver returns a single aggregated update
count
+ Connection conn = mockConnReturning(new int[] {3});
+ assertTrue(
+ lockStoreDataBaseDAO.doAcquireLocks(conn, locks(3)),
+ "batch acquire must succeed when the driver aggregates
executeBatch() results");
+ }
+
+ /**
+ * A spec-conforming driver returns one update count per statement.
Acquisition succeeds.
+ */
+ @Test
+ public void doAcquireLocksSucceedsWhenDriverReturnsOneCountPerStatement()
throws SQLException {
+ Connection conn = mockConnReturning(new int[] {1, 1, 1});
+ assertTrue(
+ lockStoreDataBaseDAO.doAcquireLocks(conn, locks(3)),
+ "batch acquire must succeed for a spec-conforming driver");
+ }
+
+ /**
+ * Any per-statement {@link Statement#EXECUTE_FAILED} in the returned
array is a real
+ * failure and acquisition must report it.
+ */
+ @Test
+ public void doAcquireLocksFailsWhenAnyStatementReportsExecuteFailed()
throws SQLException {
+ Connection conn = mockConnReturning(new int[] {1,
Statement.EXECUTE_FAILED, 1});
+ assertFalse(
+ lockStoreDataBaseDAO.doAcquireLocks(conn, locks(3)),
+ "batch acquire must fail when any statement reports
EXECUTE_FAILED");
+ }
+
+ private Connection mockConnReturning(int[] batchResult) throws
SQLException {
+ PreparedStatement ps = mock(PreparedStatement.class);
+ when(ps.executeBatch()).thenReturn(batchResult);
+ Connection conn = mock(Connection.class);
+ when(conn.prepareStatement(anyString())).thenReturn(ps);
+ return conn;
+ }
+
+ private List<LockDO> locks(int n) {
+ List<LockDO> lockDOs = new ArrayList<>();
+ for (int i = 1; i <= n; i++) {
+ LockDO lockDO = new LockDO();
+ lockDO.setXid("test-xid");
+ lockDO.setTransactionId(1L);
+ lockDO.setBranchId(1L);
+ lockDO.setResourceId("test-resource");
+ lockDO.setTableName("test_table");
+ lockDO.setPk(String.valueOf(i));
+ lockDO.setRowKey("test-resource^^^test_table^^^" + i);
+ lockDO.setStatus(LockStatus.Locked.getCode());
+ lockDOs.add(lockDO);
+ }
+ return lockDOs;
+ }
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]