From 94b11ecc569096011dc247daa7891cd482b2de0f Mon Sep 17 00:00:00 2001 From: zstan Date: Wed, 13 May 2026 11:48:39 +0300 Subject: [PATCH 1/3] IGNITE-28675 Fix flaky TxDeadlockCauseTest#testCause --- .../transactions/TxDeadlockDetection.java | 50 ++++++------------- .../transactions/TxDeadlockCauseTest.java | 9 ++-- 2 files changed, 20 insertions(+), 39 deletions(-) diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/TxDeadlockDetection.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/TxDeadlockDetection.java index a5cae1194cfd7..9708939f45cf5 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/TxDeadlockDetection.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/TxDeadlockDetection.java @@ -39,6 +39,7 @@ import org.apache.ignite.internal.util.future.GridFutureAdapter; import org.apache.ignite.internal.util.tostring.GridToStringExclude; import org.apache.ignite.internal.util.tostring.GridToStringInclude; +import org.apache.ignite.internal.util.typedef.F; import org.apache.ignite.internal.util.typedef.T2; import org.apache.ignite.internal.util.typedef.internal.S; import org.apache.ignite.internal.util.typedef.internal.U; @@ -55,7 +56,7 @@ public class TxDeadlockDetection { public static final int DFLT_TX_DEADLOCK_DETECTION_TIMEOUT = 60000; /** Deadlock detection maximum iterations. */ - private static int deadLockTimeout = + private static final int deadLockTimeout = getInteger(IGNITE_TX_DEADLOCK_DETECTION_TIMEOUT, DFLT_TX_DEADLOCK_DETECTION_TIMEOUT); /** Sequence. */ @@ -80,7 +81,7 @@ public TxDeadlockDetection(GridCacheSharedContext cctx) { * * @param tx Target tx. * @param keys Keys. - * @return {@link TxDeadlock} if found, otherwise - {@code null}. + * @return {@link TxDeadlockFuture} future. */ TxDeadlockFuture detectDeadlock(IgniteInternalTx tx, Set keys) { GridCacheVersion txId = tx.nearXidVersion(); @@ -101,7 +102,7 @@ TxDeadlockFuture detectDeadlock(IgniteInternalTx tx, Set keys) { * @param wfg Wait-for-graph. * @param txId Tx ID - start vertex for cycle search in graph. */ - static List findCycle(Map> wfg, GridCacheVersion txId) { + static @Nullable List findCycle(Map> wfg, GridCacheVersion txId) { if (wfg == null || wfg.isEmpty()) return null; @@ -181,7 +182,7 @@ static class TxDeadlockFuture extends GridFutureAdapter { /** Pending keys. */ @GridToStringInclude - private Map> pendingKeys = new HashMap<>(); + private final Map> pendingKeys = new HashMap<>(); /** Nodes queue. */ @GridToStringInclude @@ -322,7 +323,7 @@ private void detect(TxLocksResponse res) { * Maps tx keys on nodes. Key can be mapped on some node if this node is primary for given key or * node is near for transaction that holds or requests lock for key. * - * Key will not be be mapped to node if both key and node are already handled. + * Key will not be mapped to node if both key and node are already handled. * * @param txKeys Tx keys. * @param txLocks Tx locks. @@ -348,10 +349,7 @@ private void mapTxKeys(@Nullable Set txKeys, Map mappedKeys = pendingKeys.get(nodeId); - - if (mappedKeys == null) - pendingKeys.put(nodeId, mappedKeys = new HashSet<>()); + Set mappedKeys = pendingKeys.computeIfAbsent(nodeId, k -> new HashSet<>()); mappedKeys.add(txKey); } @@ -363,10 +361,7 @@ private void mapTxKeys(@Nullable Set txKeys, Map mappedKeys = pendingKeys.get(nearNodeId); - - if (mappedKeys == null) - pendingKeys.put(nearNodeId, mappedKeys = new HashSet<>()); + Set mappedKeys = pendingKeys.computeIfAbsent(nearNodeId, k -> new HashSet<>()); mappedKeys.add(txKey); } @@ -387,10 +382,7 @@ private void mapTxKeys(@Nullable Set txKeys, Map mappedKeys = pendingKeys.get(nodeId); - - if (mappedKeys == null) - pendingKeys.put(nodeId, mappedKeys = new HashSet<>()); + Set mappedKeys = pendingKeys.computeIfAbsent(nodeId, k -> new HashSet<>()); mappedKeys.add(txKey); } @@ -417,7 +409,7 @@ private UUID primary(IgniteTxKey txKey) { private void merge(TxLocksResponse res) { Map> txLocks = res.txLocks(); - if (txLocks == null || txLocks.isEmpty()) + if (F.isEmpty(txLocks)) return; for (Map.Entry> e : txLocks.entrySet()) { @@ -425,7 +417,7 @@ private void merge(TxLocksResponse res) { List lockList = e.getValue(); - if (lockList != null && !lockList.isEmpty()) { + if (!F.isEmpty(lockList)) { for (TxLock lock : lockList) { if (lock.owner() || lock.candiate()) { if (txs.get(lock.txId()) == null) @@ -435,18 +427,12 @@ private void merge(TxLocksResponse res) { if (lock.owner()) { GridCacheVersion txId = lock.txId(); - Set keys = txLockedKeys.get(txId); - - if (keys == null) - txLockedKeys.put(txId, keys = new HashSet<>()); + Set keys = txLockedKeys.computeIfAbsent(txId, k -> new HashSet<>()); keys.add(txKey); } else if (lock.candiate()) { - Set txs = txRequestedKeys.get(txKey); - - if (txs == null) - txRequestedKeys.put(txKey, txs = new HashSet<>()); + Set txs = txRequestedKeys.computeIfAbsent(txKey, k -> new HashSet<>()); txs.add(lock.txId()); } @@ -473,10 +459,7 @@ private void updateWaitForGraph(Map> txLocks) { txOwner = lock.txId(); if (keys.contains(e.getKey()) && !txId.equals(lock.txId())) { - Set waitingTxs = wfg.get(txId); - - if (waitingTxs == null) - wfg.put(txId, waitingTxs = new HashSet<>()); + Set waitingTxs = wfg.computeIfAbsent(txId, k -> new HashSet<>()); waitingTxs.add(lock.txId()); } @@ -487,10 +470,7 @@ private void updateWaitForGraph(Map> txLocks) { if (lock.candiate() || lock.owner()) { GridCacheVersion txId0 = lock.txId(); - Set waitForTxs = wfg.get(txId0); - - if (waitForTxs == null) - wfg.put(txId0, waitForTxs = new HashSet<>()); + Set waitForTxs = wfg.computeIfAbsent(txId0, k -> new HashSet<>()); waitForTxs.add(txOwner); } diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/transactions/TxDeadlockCauseTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/transactions/TxDeadlockCauseTest.java index a68543abfb509..660e60e63e49e 100644 --- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/transactions/TxDeadlockCauseTest.java +++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/transactions/TxDeadlockCauseTest.java @@ -28,7 +28,6 @@ import java.util.concurrent.atomic.AtomicReference; import org.apache.ignite.Ignite; import org.apache.ignite.IgniteCache; -import org.apache.ignite.IgniteCheckedException; import org.apache.ignite.cache.CacheAtomicityMode; import org.apache.ignite.configuration.CacheConfiguration; import org.apache.ignite.configuration.DataRegionConfiguration; @@ -183,7 +182,7 @@ private void checkCauseObject( final CyclicBarrier barrier = new CyclicBarrier(2); IgniteInternalFuture fut = GridTestUtils.runMultiThreadedAsync(new CAX() { - @Override public void applyx() throws IgniteCheckedException { + @Override public void applyx() { try (Transaction tx = ignite.transactions().txStart(TransactionConcurrency.PESSIMISTIC, isolation, timeout, keys.size())) { @@ -204,7 +203,9 @@ private void checkCauseObject( tx.commit(); } catch (Exception e) { - ex.compareAndSet(null, e); + // TransactionDeadlockException raised at least for one transaction involved in the deadlock + if (X.hasCause(e, TransactionDeadlockException.class)) + ex.compareAndSet(null, e); } } }, 2, "tx"); @@ -268,7 +269,7 @@ static class Account implements Serializable { /** * Change balance by specified amount. * - * @param amount Amount to add to balance (may be negative). + * @param amount Amount to add to balance (maybe negative). */ void update(double amount) { balance += amount; From f764e52fce97840af5b9c3155d60f5dc5c1e2e0e Mon Sep 17 00:00:00 2001 From: zstan Date: Fri, 15 May 2026 13:29:39 +0300 Subject: [PATCH 2/3] fix final change through reflection --- .../TxDeadlockDetectionNoHangsTest.java | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/transactions/TxDeadlockDetectionNoHangsTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/transactions/TxDeadlockDetectionNoHangsTest.java index 5898f0a370191..347525de87758 100644 --- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/transactions/TxDeadlockDetectionNoHangsTest.java +++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/transactions/TxDeadlockDetectionNoHangsTest.java @@ -39,7 +39,6 @@ import org.junit.Test; import static org.apache.ignite.IgniteSystemProperties.IGNITE_TX_DEADLOCK_DETECTION_TIMEOUT; -import static org.apache.ignite.IgniteSystemProperties.getInteger; import static org.apache.ignite.transactions.TransactionConcurrency.OPTIMISTIC; import static org.apache.ignite.transactions.TransactionConcurrency.PESSIMISTIC; import static org.apache.ignite.transactions.TransactionIsolation.REPEATABLE_READ; @@ -47,6 +46,7 @@ /** * */ +@WithSystemProperty(key = IGNITE_TX_DEADLOCK_DETECTION_TIMEOUT, value = "1200000") public class TxDeadlockDetectionNoHangsTest extends GridCommonAbstractTest { /** Nodes count. */ private static final int NODES_CNT = 3; @@ -85,19 +85,6 @@ public class TxDeadlockDetectionNoHangsTest extends GridCommonAbstractTest { stopAllGrids(); } - /** {@inheritDoc} */ - @Override protected void beforeTestsStarted() throws Exception { - super.beforeTestsStarted(); - - GridTestUtils.setFieldValue(TxDeadlockDetection.class, "deadLockTimeout", (int)(getTestTimeout() * 2)); - } - - /** {@inheritDoc} */ - @Override protected void afterTestsStopped() throws Exception { - GridTestUtils.setFieldValue(TxDeadlockDetection.class, "deadLockTimeout", - getInteger(IGNITE_TX_DEADLOCK_DETECTION_TIMEOUT, 60000)); - } - /** {@inheritDoc} */ @Override protected long getTestTimeout() { return 10 * 60 * 1000; From c4d175a2a512c274d7ffbc9e6dfd8ec423971fbb Mon Sep 17 00:00:00 2001 From: zstan Date: Fri, 15 May 2026 14:29:01 +0300 Subject: [PATCH 3/3] fix testCauseSeveralNodes --- .../processors/cache/transactions/TxDeadlockCauseTest.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/transactions/TxDeadlockCauseTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/transactions/TxDeadlockCauseTest.java index 660e60e63e49e..8d57871626af1 100644 --- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/transactions/TxDeadlockCauseTest.java +++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/transactions/TxDeadlockCauseTest.java @@ -164,6 +164,9 @@ private void checkCauseObject( final TransactionIsolation isolation, final boolean oneOp ) throws Exception { + if (nodes > 1) + awaitPartitionMapExchange(); + final Ignite ignite = grid(new Random().nextInt(nodes)); final IgniteCache cache = ignite.cache(DEFAULT_CACHE_NAME);