From 94b11ecc569096011dc247daa7891cd482b2de0f Mon Sep 17 00:00:00 2001 From: zstan Date: Wed, 13 May 2026 11:48:39 +0300 Subject: [PATCH 1/7] 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/7] 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/7] 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); From d57f6c3eb00a4615656c9850d0e943e500571c3c Mon Sep 17 00:00:00 2001 From: zstan Date: Wed, 20 May 2026 14:03:12 +0300 Subject: [PATCH 4/7] style fix --- .../cache/transactions/TxDeadlockDetection.java | 8 ++++---- .../transactions/TxDeadlockDetectionNoHangsTest.java | 2 -- 2 files changed, 4 insertions(+), 6 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 9708939f45cf5..9003f5625d9dc 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 @@ -56,7 +56,7 @@ public class TxDeadlockDetection { public static final int DFLT_TX_DEADLOCK_DETECTION_TIMEOUT = 60000; /** Deadlock detection maximum iterations. */ - private static final int deadLockTimeout = + private static final int DEAD_LOCK_TIMEOUT = getInteger(IGNITE_TX_DEADLOCK_DETECTION_TIMEOUT, DFLT_TX_DEADLOCK_DETECTION_TIMEOUT); /** Sequence. */ @@ -234,7 +234,7 @@ private TxDeadlockFuture(GridCacheSharedContext cctx, this.topVer = topVer; this.keys = keys; - if (deadLockTimeout > 0) { + if (DEAD_LOCK_TIMEOUT > 0) { timeoutObj = new DeadlockTimeoutObject(); cctx.time().addTimeoutObject(timeoutObj); @@ -542,7 +542,7 @@ private class DeadlockTimeoutObject extends GridTimeoutObjectAdapter { * Default constructor. */ DeadlockTimeoutObject() { - super(deadLockTimeout); + super(DEAD_LOCK_TIMEOUT); } /** {@inheritDoc} */ @@ -551,7 +551,7 @@ private class DeadlockTimeoutObject extends GridTimeoutObjectAdapter { IgniteLogger log = cctx.kernalContext().log(this.getClass()); - U.warn(log, "Deadlock detection was timed out [timeout=" + deadLockTimeout + ", fut=" + this + ']'); + U.warn(log, "Deadlock detection was timed out [timeout=" + DEAD_LOCK_TIMEOUT + ", fut=" + this + ']'); onDone(); } 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 347525de87758..8674430f38d52 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 @@ -38,7 +38,6 @@ import org.apache.ignite.transactions.TransactionConcurrency; import org.junit.Test; -import static org.apache.ignite.IgniteSystemProperties.IGNITE_TX_DEADLOCK_DETECTION_TIMEOUT; 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; @@ -46,7 +45,6 @@ /** * */ -@WithSystemProperty(key = IGNITE_TX_DEADLOCK_DETECTION_TIMEOUT, value = "1200000") public class TxDeadlockDetectionNoHangsTest extends GridCommonAbstractTest { /** Nodes count. */ private static final int NODES_CNT = 3; From 2fd5bb36b0c31e32521fe00d6774294e69a36372 Mon Sep 17 00:00:00 2001 From: zstan Date: Fri, 22 May 2026 16:14:56 +0300 Subject: [PATCH 5/7] test enhacement --- .../TxDeadlockDetectionNoHangsTest.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) 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 8674430f38d52..34bddcceed17e 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 @@ -32,6 +32,8 @@ import org.apache.ignite.internal.IgniteKernal; import org.apache.ignite.internal.util.typedef.internal.U; import org.apache.ignite.testframework.GridTestUtils; +import org.apache.ignite.testframework.ListeningTestLogger; +import org.apache.ignite.testframework.LogListener; import org.apache.ignite.testframework.junits.WithSystemProperty; import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest; import org.apache.ignite.transactions.Transaction; @@ -52,6 +54,12 @@ public class TxDeadlockDetectionNoHangsTest extends GridCommonAbstractTest { /** Cache. */ private static final String CACHE = "cache"; + /** Log listener. */ + private final ListeningTestLogger listeningLog = new ListeningTestLogger(log); + + /** */ + private static LogListener lsnr = LogListener.matches(s -> s.contains("Deadlock detection was timed out")).build(); + /** {@inheritDoc} */ @SuppressWarnings("unchecked") @Override protected IgniteConfiguration getConfiguration(String igniteInstanceName) throws Exception { @@ -66,6 +74,9 @@ public class TxDeadlockDetectionNoHangsTest extends GridCommonAbstractTest { cfg.setCacheConfiguration(ccfg); + listeningLog.registerListener(lsnr); + cfg.setGridLogger(listeningLog); + return cfg; } @@ -80,6 +91,8 @@ public class TxDeadlockDetectionNoHangsTest extends GridCommonAbstractTest { @Override protected void afterTest() throws Exception { super.afterTest(); + assertFalse(lsnr.check()); + stopAllGrids(); } From 41500b02703f03216dc9b7763d810c31fb6aedb2 Mon Sep 17 00:00:00 2001 From: zstan Date: Wed, 27 May 2026 11:01:35 +0300 Subject: [PATCH 6/7] fix after review --- .../transactions/TxDeadlockDetectionNoHangsTest.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 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 34bddcceed17e..b3142e94f60cd 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 @@ -58,7 +58,7 @@ public class TxDeadlockDetectionNoHangsTest extends GridCommonAbstractTest { private final ListeningTestLogger listeningLog = new ListeningTestLogger(log); /** */ - private static LogListener lsnr = LogListener.matches(s -> s.contains("Deadlock detection was timed out")).build(); + private static final LogListener DEAD_LOCK_LSNR = LogListener.matches(s -> s.contains("Deadlock detection was timed out")).build(); /** {@inheritDoc} */ @SuppressWarnings("unchecked") @@ -74,7 +74,9 @@ public class TxDeadlockDetectionNoHangsTest extends GridCommonAbstractTest { cfg.setCacheConfiguration(ccfg); - listeningLog.registerListener(lsnr); + assertFalse(DEAD_LOCK_LSNR.check()); + + listeningLog.registerListener(DEAD_LOCK_LSNR); cfg.setGridLogger(listeningLog); return cfg; @@ -91,7 +93,7 @@ public class TxDeadlockDetectionNoHangsTest extends GridCommonAbstractTest { @Override protected void afterTest() throws Exception { super.afterTest(); - assertFalse(lsnr.check()); + assertFalse(DEAD_LOCK_LSNR.check()); stopAllGrids(); } From 0a8be5a9036470d900163855dbe07d0f931632cd Mon Sep 17 00:00:00 2001 From: zstan Date: Thu, 28 May 2026 11:47:24 +0300 Subject: [PATCH 7/7] fix after review 2 --- .../TxDeadlockDetectionNoHangsTest.java | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 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 b3142e94f60cd..548b67b925d4d 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 @@ -21,6 +21,7 @@ import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Consumer; import org.apache.ignite.Ignite; import org.apache.ignite.IgniteCache; import org.apache.ignite.IgniteCheckedException; @@ -33,7 +34,6 @@ import org.apache.ignite.internal.util.typedef.internal.U; import org.apache.ignite.testframework.GridTestUtils; import org.apache.ignite.testframework.ListeningTestLogger; -import org.apache.ignite.testframework.LogListener; import org.apache.ignite.testframework.junits.WithSystemProperty; import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest; import org.apache.ignite.transactions.Transaction; @@ -57,8 +57,14 @@ public class TxDeadlockDetectionNoHangsTest extends GridCommonAbstractTest { /** Log listener. */ private final ListeningTestLogger listeningLog = new ListeningTestLogger(log); + /** Deadlock timeout, it`s unexpected during these tests. */ + private static final AtomicBoolean DEAD_LOCK_FLAG = new AtomicBoolean(); + /** */ - private static final LogListener DEAD_LOCK_LSNR = LogListener.matches(s -> s.contains("Deadlock detection was timed out")).build(); + private static final Consumer DEAD_LOCK_LSNR = s -> { + if (s.contains("Deadlock detection was timed out")) + DEAD_LOCK_FLAG.set(true); + }; /** {@inheritDoc} */ @SuppressWarnings("unchecked") @@ -74,7 +80,7 @@ public class TxDeadlockDetectionNoHangsTest extends GridCommonAbstractTest { cfg.setCacheConfiguration(ccfg); - assertFalse(DEAD_LOCK_LSNR.check()); + assertFalse(DEAD_LOCK_FLAG.get()); listeningLog.registerListener(DEAD_LOCK_LSNR); cfg.setGridLogger(listeningLog); @@ -93,7 +99,7 @@ public class TxDeadlockDetectionNoHangsTest extends GridCommonAbstractTest { @Override protected void afterTest() throws Exception { super.afterTest(); - assertFalse(DEAD_LOCK_LSNR.check()); + assertFalse(DEAD_LOCK_FLAG.get()); stopAllGrids(); }