This is an automated email from the ASF dual-hosted git repository.

alexpl pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ignite.git


The following commit(s) were added to refs/heads/master by this push:
     new d042b1dc091 IGNITE-26166 Add tx deadlocks count metric - Fixes #12259.
d042b1dc091 is described below

commit d042b1dc091e45fb8eda1f6f1338faa7172ab5b4
Author: Aleksey Plekhanov <[email protected]>
AuthorDate: Fri Sep 26 16:44:48 2025 +0300

    IGNITE-26166 Add tx deadlocks count metric - Fixes #12259.
    
    Signed-off-by: Aleksey Plekhanov <[email protected]>
---
 docs/_docs/monitoring-metrics/new-metrics.adoc     |   1 +
 .../internal/TransactionMetricsMxBeanImpl.java     |   5 +
 .../transactions/TransactionMetricsAdapter.java    |  27 +++++
 .../cache/transactions/TxDeadlockDetection.java    |   5 +-
 .../ignite/transactions/TransactionMetrics.java    |   7 ++
 ...dCacheTransactionalAbstractMetricsSelfTest.java | 127 +++++++++++++++++++++
 6 files changed, 171 insertions(+), 1 deletion(-)

diff --git a/docs/_docs/monitoring-metrics/new-metrics.adoc 
b/docs/_docs/monitoring-metrics/new-metrics.adoc
index c1e6a1904e5..862f27a3391 100644
--- a/docs/_docs/monitoring-metrics/new-metrics.adoc
+++ b/docs/_docs/monitoring-metrics/new-metrics.adoc
@@ -169,6 +169,7 @@ Register name: `tx`
 |totalNodeUserTime   |long|    Total transactions user time on node.
 |txCommits   |integer| Number of transaction commits.
 |txRollbacks |integer| Number of transaction rollbacks.
+|txDeadlocks |integer| Number of transaction deadlocks.
 |===
 
 
diff --git 
a/modules/core/src/main/java/org/apache/ignite/internal/TransactionMetricsMxBeanImpl.java
 
b/modules/core/src/main/java/org/apache/ignite/internal/TransactionMetricsMxBeanImpl.java
index f7b3f7ee0a8..8ed77ed4bfc 100644
--- 
a/modules/core/src/main/java/org/apache/ignite/internal/TransactionMetricsMxBeanImpl.java
+++ 
b/modules/core/src/main/java/org/apache/ignite/internal/TransactionMetricsMxBeanImpl.java
@@ -77,6 +77,11 @@ public class TransactionMetricsMxBeanImpl implements 
TransactionMetricsMxBean {
         return transactionMetrics.txRollbacks();
     }
 
+    /** {@inheritDoc} */
+    @Override public int txDeadlocks() {
+        return transactionMetrics.txDeadlocks();
+    }
+
     /** {@inheritDoc} */
     @Override public Map<String, String> getAllOwnerTransactions() {
         return transactionMetrics.getAllOwnerTransactions();
diff --git 
a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/TransactionMetricsAdapter.java
 
b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/TransactionMetricsAdapter.java
index 7e60dc49ea4..d5cd833179e 100644
--- 
a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/TransactionMetricsAdapter.java
+++ 
b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/TransactionMetricsAdapter.java
@@ -76,6 +76,9 @@ public class TransactionMetricsAdapter implements 
TransactionMetrics {
     /** Number of transaction rollbacks. */
     private final IntMetricImpl txRollbacks;
 
+    /** Number of detected deadlocks. */
+    private final IntMetricImpl txDeadlocks;
+
     /** Last commit time. */
     private final AtomicLongMetric commitTime;
 
@@ -104,6 +107,7 @@ public class TransactionMetricsAdapter implements 
TransactionMetrics {
 
         txCommits = mreg.intMetric("txCommits", "Number of transaction 
commits.");
         txRollbacks = mreg.intMetric("txRollbacks", "Number of transaction 
rollbacks.");
+        txDeadlocks = mreg.intMetric("txDeadlocks", "Number of transaction 
deadlocks.");
         commitTime = mreg.longMetric("commitTime", "Last commit time.");
         rollbackTime = mreg.longMetric("rollbackTime", "Last rollback time.");
         totalTxSystemTime = mreg.longAdderMetric(METRIC_TOTAL_SYSTEM_TIME, 
"Total transactions system time on node.");
@@ -164,6 +168,11 @@ public class TransactionMetricsAdapter implements 
TransactionMetrics {
         return txRollbacks.value();
     }
 
+    /** {@inheritDoc} */
+    @Override public int txDeadlocks() {
+        return txDeadlocks.value();
+    }
+
     /** {@inheritDoc} */
     @Override public Map<String, String> getAllOwnerTransactions() {
         return getNearTxs(0);
@@ -217,6 +226,13 @@ public class TransactionMetricsAdapter implements 
TransactionMetrics {
         txRollbacks.increment();
     }
 
+    /**
+     * Transaction deadlock callback.
+     */
+    public void onTxDeadlock() {
+        txDeadlocks.increment();
+    }
+
     /**
      * Callback for completion of near transaction. Writes metrics of single 
near transaction.
      *
@@ -245,6 +261,7 @@ public class TransactionMetricsAdapter implements 
TransactionMetrics {
         txCommits.reset();
         rollbackTime.reset();
         txRollbacks.reset();
+        txDeadlocks.reset();
     }
 
     /** @return Current metrics values. */
@@ -403,6 +420,9 @@ public class TransactionMetricsAdapter implements 
TransactionMetrics {
         /** Number of transaction rollbacks. */
         private volatile int txRollbacks;
 
+        /** Number of transaction deadlocks. */
+        private volatile int txDeadlocks;
+
         /** Last commit time. */
         private volatile long commitTime;
 
@@ -444,6 +464,11 @@ public class TransactionMetricsAdapter implements 
TransactionMetrics {
             return adapter != null ? adapter.txRollbacks() : txRollbacks;
         }
 
+        /** {@inheritDoc} */
+        @Override public int txDeadlocks() {
+            return adapter != null ? adapter.txDeadlocks() : txDeadlocks;
+        }
+
         /** {@inheritDoc} */
         @Override public Map<String, String> getAllOwnerTransactions() {
             return adapter != null ? adapter.getAllOwnerTransactions() : null;
@@ -485,6 +510,7 @@ public class TransactionMetricsAdapter implements 
TransactionMetrics {
             out.writeLong(rollbackTime);
             out.writeInt(txCommits);
             out.writeInt(txRollbacks);
+            out.writeInt(txDeadlocks);
         }
 
         /** {@inheritDoc} */
@@ -493,6 +519,7 @@ public class TransactionMetricsAdapter implements 
TransactionMetrics {
             rollbackTime = in.readLong();
             txCommits = in.readInt();
             txRollbacks = in.readInt();
+            txDeadlocks = in.readInt();
         }
     }
 }
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 6d767814898..f65485dc7b8 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
@@ -309,8 +309,11 @@ public class TxDeadlockDetection {
 
             List<GridCacheVersion> cycle = findCycle(wfg, txId);
 
-            if (cycle != null)
+            if (cycle != null) {
+                cctx.txMetrics().onTxDeadlock();
+
                 onDone(new TxDeadlock(cycle, txs, txLockedKeys, 
txRequestedKeys));
+            }
             else
                 map(res.keys(), res.txLocks());
         }
diff --git 
a/modules/core/src/main/java/org/apache/ignite/transactions/TransactionMetrics.java
 
b/modules/core/src/main/java/org/apache/ignite/transactions/TransactionMetrics.java
index e0b7b9fbeb8..4ae37eeee57 100644
--- 
a/modules/core/src/main/java/org/apache/ignite/transactions/TransactionMetrics.java
+++ 
b/modules/core/src/main/java/org/apache/ignite/transactions/TransactionMetrics.java
@@ -51,6 +51,13 @@ public interface TransactionMetrics {
      */
     public int txRollbacks();
 
+    /**
+     * Gets total number of transaction deadlocks.
+     *
+     * @return Number of transaction deadlocks.
+     */
+    public int txDeadlocks();
+
     /**
      * Gets a map of all transactions for which the local node is the 
originating node.
      *
diff --git 
a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/GridCacheTransactionalAbstractMetricsSelfTest.java
 
b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/GridCacheTransactionalAbstractMetricsSelfTest.java
index b07615067d7..b47f11c0e3a 100644
--- 
a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/GridCacheTransactionalAbstractMetricsSelfTest.java
+++ 
b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/GridCacheTransactionalAbstractMetricsSelfTest.java
@@ -18,10 +18,14 @@
 package org.apache.ignite.internal.processors.cache;
 
 import java.util.Arrays;
+import java.util.concurrent.CountDownLatch;
+import org.apache.ignite.Ignite;
 import org.apache.ignite.IgniteCache;
 import org.apache.ignite.IgniteTransactions;
 import org.apache.ignite.cache.CacheMetrics;
+import org.apache.ignite.internal.IgniteInternalFuture;
 import org.apache.ignite.internal.processors.metric.impl.HistogramMetricImpl;
+import org.apache.ignite.internal.util.typedef.G;
 import org.apache.ignite.internal.util.typedef.internal.U;
 import org.apache.ignite.testframework.GridTestUtils;
 import org.apache.ignite.transactions.Transaction;
@@ -47,6 +51,23 @@ public abstract class 
GridCacheTransactionalAbstractMetricsSelfTest extends Grid
     /** Transaction timeout. */
     private static final long TX_TIMEOUT = 500L;
 
+    /** */
+    private static Ignite client;
+
+    /** {@inheritDoc} */
+    @Override protected void beforeTestsStarted() throws Exception {
+        super.beforeTestsStarted();
+
+        client = startClientGrid();
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void afterTest() throws Exception {
+        super.afterTest();
+
+        client.transactions().resetMetrics();
+    }
+
     /**
      * @throws Exception If failed.
      */
@@ -239,6 +260,54 @@ public abstract class 
GridCacheTransactionalAbstractMetricsSelfTest extends Grid
         testRollbacks(PESSIMISTIC, SERIALIZABLE, false);
     }
 
+    /**
+     * @throws Exception If failed.
+     */
+    @Test
+    public void testPessimisticReadCommittedDeadlocks() throws Exception {
+        testDeadlocks(PESSIMISTIC, READ_COMMITTED, false);
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    @Test
+    public void testPessimisticRepeatableReadDeadlocks() throws Exception {
+        testDeadlocks(PESSIMISTIC, REPEATABLE_READ, false);
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    @Test
+    public void testPessimisticSerializableDeadlocks() throws Exception {
+        testDeadlocks(PESSIMISTIC, SERIALIZABLE, false);
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    @Test
+    public void testPessimisticReadCommittedDeadlocksOnClient() throws 
Exception {
+        testDeadlocks(PESSIMISTIC, READ_COMMITTED, true);
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    @Test
+    public void testPessimisticRepeatableReadDeadlocksOnClient() throws 
Exception {
+        testDeadlocks(PESSIMISTIC, REPEATABLE_READ, true);
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    @Test
+    public void testPessimisticSerializableDeadlocksOnClient() throws 
Exception {
+        testDeadlocks(PESSIMISTIC, SERIALIZABLE, true);
+    }
+
     /**
      * @throws Exception If failed.
      */
@@ -393,6 +462,64 @@ public abstract class 
GridCacheTransactionalAbstractMetricsSelfTest extends Grid
         }
     }
 
+    /**
+     * @param concurrency Concurrency control.
+     * @param isolation Isolation level.
+     * @param useClient Use client node as transaction coordinator.
+     * @throws Exception If failed.
+     */
+    private void testDeadlocks(
+        TransactionConcurrency concurrency,
+        TransactionIsolation isolation,
+        boolean useClient
+    ) throws Exception {
+        Ignite ignite = useClient ? client : grid(0);
+
+        IgniteCache<Integer, Integer> cache = ignite.cache(DEFAULT_CACHE_NAME);
+        long txTimeout = 500;
+
+        CountDownLatch key0locked = new CountDownLatch(1);
+        CountDownLatch key1locked = new CountDownLatch(1);
+
+        IgniteInternalFuture<?> fut0 = GridTestUtils.runAsync(() -> {
+            Transaction tx = ignite.transactions().txStart(concurrency, 
isolation, txTimeout, 0);
+
+            cache.put(0, 0);
+
+            key0locked.countDown();
+            key1locked.await();
+
+            cache.put(1, 1);
+        });
+
+        IgniteInternalFuture<?> fut1 = GridTestUtils.runAsync(() -> {
+            Transaction tx = ignite.transactions().txStart(concurrency, 
isolation, txTimeout * 2, 0);
+
+            cache.put(1, 0);
+
+            key1locked.countDown();
+            key0locked.await();
+
+            cache.put(0, 1);
+
+            tx.commit();
+        });
+
+        try {
+            fut0.get();
+        }
+        catch (Exception ignore) {
+            // Expected.
+        }
+
+        fut1.get();
+
+        for (Ignite g : G.allGrids()) {
+            assertEquals(g == ignite ? 1 : 0, 
g.transactions().metrics().txRollbacks());
+            assertEquals(g == ignite ? 1 : 0, 
g.transactions().metrics().txDeadlocks());
+        }
+    }
+
     /**
      * Metrics test for transaction timeout rollback.
      *

Reply via email to