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.
*