You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by gv...@apache.org on 2019/01/31 16:28:26 UTC

[ignite] branch master updated: IGNITE-11029: Public API for edge-chasing deadlock detection configuration. This closes #5896.

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

gvvinblade 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 07a0a6a  IGNITE-11029: Public API for edge-chasing deadlock detection configuration. This closes #5896.
07a0a6a is described below

commit 07a0a6a76e768f36d4caaa5ba7ea5ff48fc1983c
Author: ipavlukhin <vo...@gmail.com>
AuthorDate: Thu Jan 31 19:27:59 2019 +0300

    IGNITE-11029: Public API for edge-chasing deadlock detection configuration. This closes #5896.
---
 .../org/apache/ignite/IgniteSystemProperties.java  |  12 --
 .../configuration/TransactionConfiguration.java    |  58 +++++++-
 .../processors/cache/GridCacheProcessor.java       |  38 +++--
 .../cache/mvcc/DeadlockDetectionManager.java       |  13 +-
 .../platform/utils/PlatformConfigurationUtils.java |   6 +-
 .../mvcc/CacheMvccSqlTxQueriesAbstractTest.java    |  17 +--
 ...cheMvccSqlTxQueriesWithReducerAbstractTest.java |  21 +--
 .../mvcc/MvccDeadlockDetectionConfigTest.java      | 159 +++++++++++++++++++++
 .../cache/mvcc/MvccDeadlockDetectionTest.java      |  26 +---
 .../testsuites/IgniteCacheMvccSqlTestSuite.java    |   2 +
 .../Apache.Ignite.Core/IgniteConfiguration.cs      |   4 +-
 .../IgniteConfigurationSection.xsd                 |  20 ++-
 .../Transactions/TransactionConfiguration.cs       |  13 +-
 13 files changed, 300 insertions(+), 89 deletions(-)

diff --git a/modules/core/src/main/java/org/apache/ignite/IgniteSystemProperties.java b/modules/core/src/main/java/org/apache/ignite/IgniteSystemProperties.java
index a2d1565..b9413e2 100644
--- a/modules/core/src/main/java/org/apache/ignite/IgniteSystemProperties.java
+++ b/modules/core/src/main/java/org/apache/ignite/IgniteSystemProperties.java
@@ -261,18 +261,6 @@ public final class IgniteSystemProperties {
     public static final String IGNITE_TX_DEADLOCK_DETECTION_TIMEOUT = "IGNITE_TX_DEADLOCK_DETECTION_TIMEOUT";
 
     /**
-     * Specifies delay in milliseconds before starting deadlock detection procedure when tx encounters locked key.
-     * <p>
-     * Following values could be used:
-     * <ul>
-     *     <li>&lt; 0 disable detection;</li>
-     *     <li>0 start detection without a delay;</li>
-     *     <li>&gt; 0 start detection after a specified number of milliseconds.</li>
-     * </ul>
-     */
-    public static final String IGNITE_TX_DEADLOCK_DETECTION_INITIAL_DELAY = "IGNITE_TX_DEADLOCK_DETECTION_INITIAL_DELAY";
-
-    /**
      * System property to enable pending transaction tracker.
      * Affects impact of {@link IgniteSystemProperties#IGNITE_DISABLE_WAL_DURING_REBALANCING} property:
      * if this property is set, WAL anyway won't be disabled during rebalancing triggered by baseline topology change.
diff --git a/modules/core/src/main/java/org/apache/ignite/configuration/TransactionConfiguration.java b/modules/core/src/main/java/org/apache/ignite/configuration/TransactionConfiguration.java
index e669bcf..91048eaf 100644
--- a/modules/core/src/main/java/org/apache/ignite/configuration/TransactionConfiguration.java
+++ b/modules/core/src/main/java/org/apache/ignite/configuration/TransactionConfiguration.java
@@ -18,7 +18,9 @@
 package org.apache.ignite.configuration;
 
 import java.io.Serializable;
+import java.util.ArrayList;
 import javax.cache.configuration.Factory;
+import org.apache.ignite.cache.CacheAtomicityMode;
 import org.apache.ignite.internal.util.TransientSerializable;
 import org.apache.ignite.internal.util.typedef.internal.S;
 import org.apache.ignite.lang.IgniteProductVersion;
@@ -35,6 +37,9 @@ public class TransactionConfiguration implements Serializable {
     private static final IgniteProductVersion TX_PME_TIMEOUT_SINCE = IgniteProductVersion.fromString("2.5.1");
 
     /** */
+    private static final IgniteProductVersion DEADLOCK_TIMEOUT_SINCE = IgniteProductVersion.fromString("2.7.3");
+
+    /** */
     private static final long serialVersionUID = 0L;
 
     /** Default value for 'txSerializableEnabled' flag. */
@@ -52,6 +57,9 @@ public class TransactionConfiguration implements Serializable {
     /** Transaction timeout on partition map synchronization. */
     public static final long TX_TIMEOUT_ON_PARTITION_MAP_EXCHANGE = 0;
 
+    /** Default timeout before starting deadlock detection. */
+    public static final long DFLT_DEADLOCK_TIMEOUT = 10_000;
+
     /** Default size of pessimistic transactions log. */
     public static final int DFLT_PESSIMISTIC_TX_LOG_LINGER = 10_000;
 
@@ -67,9 +75,15 @@ public class TransactionConfiguration implements Serializable {
     /** Default transaction timeout. */
     private long dfltTxTimeout = DFLT_TRANSACTION_TIMEOUT;
 
-    /** Transaction timeout on partition map exchange. */
+    /**
+     * Transaction timeout on partition map exchange.
+     * Volatile in order to be changed dynamically.
+     */
     private volatile long txTimeoutOnPartitionMapExchange = TX_TIMEOUT_ON_PARTITION_MAP_EXCHANGE;
 
+    /** Timeout before starting deadlock detection. */
+    private long deadlockTimeout = DFLT_DEADLOCK_TIMEOUT;
+
     /** Pessimistic tx log size. */
     private int pessimisticTxLogSize;
 
@@ -103,6 +117,7 @@ public class TransactionConfiguration implements Serializable {
         dfltIsolation = cfg.getDefaultTxIsolation();
         dfltTxTimeout = cfg.getDefaultTxTimeout();
         txTimeoutOnPartitionMapExchange = cfg.getTxTimeoutOnPartitionMapExchange();
+        deadlockTimeout = cfg.getDeadlockTimeout();
         pessimisticTxLogLinger = cfg.getPessimisticTxLogLinger();
         pessimisticTxLogSize = cfg.getPessimisticTxLogSize();
         txSerEnabled = cfg.isTxSerializableEnabled();
@@ -240,6 +255,38 @@ public class TransactionConfiguration implements Serializable {
     }
 
     /**
+     * Transaction deadlocks occurred for caches configured with {@link CacheAtomicityMode#TRANSACTIONAL_SNAPSHOT}
+     * can be resolved automatically.
+     * <p>
+     * Deadlock detection starts when one transaction is waiting for an entry lock more than a timeout specified by
+     * this property.
+     * <p>
+     * Timeout is specified in milliseconds and {@code 0} means that automatic deadlock detection is disabled. Default
+     * value is defined by {@link #DFLT_DEADLOCK_TIMEOUT}.
+     *
+     * @return Timeout before starting deadlock detection.
+     */
+    public long getDeadlockTimeout() {
+        return deadlockTimeout;
+    }
+
+    /**
+     * Sets a timeout before starting deadlock detection for caches configured with
+     * {@link CacheAtomicityMode#TRANSACTIONAL_SNAPSHOT}.
+     * <p>
+     * Timeout is specified in milliseconds and {@code 0} means that automatic deadlock detection is disabled. Default
+     * value is defined by {@link #DFLT_DEADLOCK_TIMEOUT}.
+     *
+     * @param deadlockTimeout Timeout value in milliseconds.
+     * @return {@code this} for chaining.
+     */
+    public TransactionConfiguration setDeadlockTimeout(long deadlockTimeout) {
+        this.deadlockTimeout = deadlockTimeout;
+
+        return this;
+    }
+
+    /**
      * Gets size of pessimistic transactions log stored on node in order to recover transaction commit if originating
      * node has left grid before it has sent all messages to transaction nodes.
      * <p>
@@ -402,9 +449,14 @@ public class TransactionConfiguration implements Serializable {
      */
     @SuppressWarnings("unused")
     private static String[] transientSerializableFields(IgniteProductVersion ver) {
+        ArrayList<String> transients = new ArrayList<>(2);
+
         if (TX_PME_TIMEOUT_SINCE.compareToIgnoreTimestamp(ver) >= 0)
-            return new String[] { "txTimeoutOnPartitionMapExchange" };
+            transients.add("txTimeoutOnPartitionMapExchange");
+
+        if (DEADLOCK_TIMEOUT_SINCE.compareToIgnoreTimestamp(ver) >= 0)
+            transients.add("deadlockTimeout");
 
-        return null;
+        return transients.isEmpty() ? null : transients.toArray(new String[transients.size()]);
     }
 }
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheProcessor.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheProcessor.java
index 84dfe37..6082bd1 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheProcessor.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheProcessor.java
@@ -4513,20 +4513,42 @@ public class GridCacheProcessor extends GridProcessorAdapter {
      * @throws IgniteCheckedException If check failed.
      */
     private void checkTransactionConfiguration(ClusterNode rmt) throws IgniteCheckedException {
-        TransactionConfiguration txCfg = rmt.attribute(ATTR_TX_CONFIG);
+        TransactionConfiguration rmtTxCfg = rmt.attribute(ATTR_TX_CONFIG);
 
-        if (txCfg != null) {
+        if (rmtTxCfg != null) {
             TransactionConfiguration locTxCfg = ctx.config().getTransactionConfiguration();
 
-            if (locTxCfg.isTxSerializableEnabled() != txCfg.isTxSerializableEnabled())
-                throw new IgniteCheckedException("Serializable transactions enabled mismatch " +
-                    "(fix txSerializableEnabled property or set -D" + IGNITE_SKIP_CONFIGURATION_CONSISTENCY_CHECK + "=true " +
-                    "system property) [rmtNodeId=" + rmt.id() +
-                    ", locTxSerializableEnabled=" + locTxCfg.isTxSerializableEnabled() +
-                    ", rmtTxSerializableEnabled=" + txCfg.isTxSerializableEnabled() + ']');
+            checkDeadlockDetectionConfig(rmt, rmtTxCfg, locTxCfg);
+
+            checkSerializableEnabledConfig(rmt, rmtTxCfg, locTxCfg);
         }
     }
 
+    /** */
+    private void checkDeadlockDetectionConfig(ClusterNode rmt, TransactionConfiguration rmtTxCfg,
+        TransactionConfiguration locTxCfg) {
+        boolean locDeadlockDetectionEnabled = locTxCfg.getDeadlockTimeout() > 0;
+        boolean rmtDeadlockDetectionEnabled = rmtTxCfg.getDeadlockTimeout() > 0;
+
+        if (locDeadlockDetectionEnabled != rmtDeadlockDetectionEnabled) {
+            U.warn(log, "Deadlock detection is enabled on one node and disabled on another. " +
+                "Disabled detection on one node can lead to undetected deadlocks. [rmtNodeId=" + rmt.id() +
+                ", locDeadlockTimeout=" + locTxCfg.getDeadlockTimeout() +
+                ", rmtDeadlockTimeout=" + rmtTxCfg.getDeadlockTimeout());
+        }
+    }
+
+    /** */
+    private void checkSerializableEnabledConfig(ClusterNode rmt, TransactionConfiguration rmtTxCfg,
+        TransactionConfiguration locTxCfg) throws IgniteCheckedException {
+        if (locTxCfg.isTxSerializableEnabled() != rmtTxCfg.isTxSerializableEnabled())
+            throw new IgniteCheckedException("Serializable transactions enabled mismatch " +
+                "(fix txSerializableEnabled property or set -D" + IGNITE_SKIP_CONFIGURATION_CONSISTENCY_CHECK + "=true " +
+                "system property) [rmtNodeId=" + rmt.id() +
+                ", locTxSerializableEnabled=" + locTxCfg.isTxSerializableEnabled() +
+                ", rmtTxSerializableEnabled=" + rmtTxCfg.isTxSerializableEnabled() + ']');
+    }
+
     /**
      * @param rmt Remote node to check.
      * @throws IgniteCheckedException If check failed.
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/DeadlockDetectionManager.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/DeadlockDetectionManager.java
index 9b1969b..880a4cc 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/DeadlockDetectionManager.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/DeadlockDetectionManager.java
@@ -37,7 +37,6 @@ import org.apache.ignite.internal.processors.timeout.GridTimeoutObjectAdapter;
 import org.apache.ignite.internal.transactions.IgniteTxRollbackCheckedException;
 
 import static java.util.Collections.singleton;
-import static org.apache.ignite.IgniteSystemProperties.IGNITE_TX_DEADLOCK_DETECTION_INITIAL_DELAY;
 import static org.apache.ignite.internal.GridTopic.TOPIC_DEADLOCK_DETECTION;
 import static org.apache.ignite.internal.managers.communication.GridIoPolicy.SYSTEM_POOL;
 import static org.apache.ignite.internal.processors.cache.mvcc.MvccUtils.belongToSameTx;
@@ -53,10 +52,12 @@ import static org.apache.ignite.internal.processors.cache.mvcc.MvccUtils.belongT
  */
 public class DeadlockDetectionManager extends GridCacheSharedManagerAdapter {
     /** */
-    private final long detectionStartDelay = Long.getLong(IGNITE_TX_DEADLOCK_DETECTION_INITIAL_DELAY, 10_000);
+    private long detectionStartDelay;
 
     /** {@inheritDoc} */
     @Override protected void start0() throws IgniteCheckedException {
+        detectionStartDelay = cctx.kernalContext().config().getTransactionConfiguration().getDeadlockTimeout();
+
         cctx.gridIO().addMessageListener(TOPIC_DEADLOCK_DETECTION, (nodeId, msg, plc) -> {
             if (msg instanceof DeadlockProbe) {
                 if (log.isDebugEnabled())
@@ -79,14 +80,8 @@ public class DeadlockDetectionManager extends GridCacheSharedManagerAdapter {
      * @return Cancellable computation.
      */
     public DelayedDeadlockComputation initDelayedComputation(MvccVersion waiterVer, MvccVersion blockerVer) {
-        if (detectionStartDelay < 0)
-            return null;
-
-        if (detectionStartDelay == 0) {
-            startComputation(waiterVer, blockerVer);
-
+        if (detectionStartDelay <= 0)
             return null;
-        }
 
         return new DelayedDeadlockComputation(waiterVer, blockerVer, detectionStartDelay);
     }
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/utils/PlatformConfigurationUtils.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/utils/PlatformConfigurationUtils.java
index a316b59..4e0140c 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/utils/PlatformConfigurationUtils.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/utils/PlatformConfigurationUtils.java
@@ -69,7 +69,6 @@ import org.apache.ignite.configuration.PersistentStoreConfiguration;
 import org.apache.ignite.configuration.SqlConnectorConfiguration;
 import org.apache.ignite.configuration.TransactionConfiguration;
 import org.apache.ignite.configuration.WALMode;
-import org.apache.ignite.spi.encryption.EncryptionSpi;
 import org.apache.ignite.events.Event;
 import org.apache.ignite.failure.FailureHandler;
 import org.apache.ignite.failure.NoOpFailureHandler;
@@ -100,8 +99,9 @@ import org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi;
 import org.apache.ignite.spi.discovery.tcp.ipfinder.TcpDiscoveryIpFinder;
 import org.apache.ignite.spi.discovery.tcp.ipfinder.multicast.TcpDiscoveryMulticastIpFinder;
 import org.apache.ignite.spi.discovery.tcp.ipfinder.vm.TcpDiscoveryVmIpFinder;
-import org.apache.ignite.spi.encryption.noop.NoopEncryptionSpi;
+import org.apache.ignite.spi.encryption.EncryptionSpi;
 import org.apache.ignite.spi.encryption.keystore.KeystoreEncryptionSpi;
+import org.apache.ignite.spi.encryption.noop.NoopEncryptionSpi;
 import org.apache.ignite.spi.eventstorage.EventStorageSpi;
 import org.apache.ignite.spi.eventstorage.NoopEventStorageSpi;
 import org.apache.ignite.spi.eventstorage.memory.MemoryEventStorageSpi;
@@ -788,6 +788,7 @@ public class PlatformConfigurationUtils {
             tx.setDefaultTxTimeout(in.readLong());
             tx.setPessimisticTxLogLinger(in.readInt());
             tx.setTxTimeoutOnPartitionMapExchange(in.readLong());
+            tx.setDeadlockTimeout(in.readLong());
 
             cfg.setTransactionConfiguration(tx);
         }
@@ -1393,6 +1394,7 @@ public class PlatformConfigurationUtils {
             w.writeLong(tx.getDefaultTxTimeout());
             w.writeInt(tx.getPessimisticTxLogLinger());
             w.writeLong(tx.getTxTimeoutOnPartitionMapExchange());
+            w.writeLong(tx.getDeadlockTimeout());
         }
         else
             w.writeBoolean(false);
diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccSqlTxQueriesAbstractTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccSqlTxQueriesAbstractTest.java
index a16df2a..5c53215 100644
--- a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccSqlTxQueriesAbstractTest.java
+++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccSqlTxQueriesAbstractTest.java
@@ -43,6 +43,8 @@ import org.apache.ignite.IgniteCheckedException;
 import org.apache.ignite.cache.query.FieldsQueryCursor;
 import org.apache.ignite.cache.query.SqlFieldsQuery;
 import org.apache.ignite.cache.query.annotations.QuerySqlField;
+import org.apache.ignite.configuration.IgniteConfiguration;
+import org.apache.ignite.configuration.TransactionConfiguration;
 import org.apache.ignite.internal.IgniteEx;
 import org.apache.ignite.internal.processors.cache.GridCacheContext;
 import org.apache.ignite.internal.processors.cache.IgniteCacheProxy;
@@ -65,7 +67,6 @@ import org.apache.ignite.transactions.Transaction;
 import org.junit.Ignore;
 import org.junit.Test;
 
-import static org.apache.ignite.IgniteSystemProperties.IGNITE_TX_DEADLOCK_DETECTION_INITIAL_DELAY;
 import static org.apache.ignite.cache.CacheWriteSynchronizationMode.FULL_SYNC;
 import static org.apache.ignite.internal.processors.cache.mvcc.CacheMvccAbstractTest.ReadMode.SQL;
 import static org.apache.ignite.internal.processors.cache.mvcc.CacheMvccAbstractTest.ReadMode.SQL_SUM;
@@ -80,17 +81,9 @@ import static org.apache.ignite.transactions.TransactionIsolation.REPEATABLE_REA
  */
 public abstract class CacheMvccSqlTxQueriesAbstractTest extends CacheMvccAbstractTest {
     /** {@inheritDoc} */
-    @Override protected void beforeTestsStarted() throws Exception {
-        super.beforeTestsStarted();
-
-        System.setProperty(IGNITE_TX_DEADLOCK_DETECTION_INITIAL_DELAY, "-1");
-    }
-
-    /** {@inheritDoc} */
-    @Override protected void afterTestsStopped() throws Exception {
-        System.clearProperty(IGNITE_TX_DEADLOCK_DETECTION_INITIAL_DELAY);
-
-        super.afterTestsStopped();
+    @Override protected IgniteConfiguration getConfiguration(String gridName) throws Exception {
+        return super.getConfiguration(gridName)
+            .setTransactionConfiguration(new TransactionConfiguration().setDeadlockTimeout(0));
     }
 
     /**
diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccSqlTxQueriesWithReducerAbstractTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccSqlTxQueriesWithReducerAbstractTest.java
index f7a5a80..ea868f9 100644
--- a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccSqlTxQueriesWithReducerAbstractTest.java
+++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccSqlTxQueriesWithReducerAbstractTest.java
@@ -32,6 +32,8 @@ import org.apache.ignite.IgniteCache;
 import org.apache.ignite.cache.query.FieldsQueryCursor;
 import org.apache.ignite.cache.query.SqlFieldsQuery;
 import org.apache.ignite.configuration.CacheConfiguration;
+import org.apache.ignite.configuration.IgniteConfiguration;
+import org.apache.ignite.configuration.TransactionConfiguration;
 import org.apache.ignite.internal.IgniteEx;
 import org.apache.ignite.internal.processors.query.IgniteSQLException;
 import org.apache.ignite.internal.transactions.IgniteTxTimeoutCheckedException;
@@ -41,7 +43,6 @@ import org.apache.ignite.testframework.GridTestUtils;
 import org.apache.ignite.transactions.Transaction;
 import org.junit.Test;
 
-import static org.apache.ignite.IgniteSystemProperties.IGNITE_TX_DEADLOCK_DETECTION_INITIAL_DELAY;
 import static org.apache.ignite.cache.CacheWriteSynchronizationMode.FULL_SYNC;
 import static org.apache.ignite.testframework.GridTestUtils.assertThrowsWithCause;
 import static org.apache.ignite.testframework.GridTestUtils.runMultiThreaded;
@@ -52,23 +53,15 @@ import static org.apache.ignite.transactions.TransactionIsolation.REPEATABLE_REA
  * Tests for transactional SQL.
  */
 public abstract class CacheMvccSqlTxQueriesWithReducerAbstractTest extends CacheMvccAbstractTest  {
-    /** {@inheritDoc} */
-    @Override protected void beforeTestsStarted() throws Exception {
-        super.beforeTestsStarted();
-
-        System.setProperty(IGNITE_TX_DEADLOCK_DETECTION_INITIAL_DELAY, "-1");
-    }
+    /** */
+    private static final int TIMEOUT = 3000;
 
     /** {@inheritDoc} */
-    @Override protected void afterTestsStopped() throws Exception {
-        System.clearProperty(IGNITE_TX_DEADLOCK_DETECTION_INITIAL_DELAY);
-
-        super.afterTestsStopped();
+    @Override protected IgniteConfiguration getConfiguration(String gridName) throws Exception {
+        return super.getConfiguration(gridName)
+            .setTransactionConfiguration(new TransactionConfiguration().setDeadlockTimeout(0));
     }
 
-    /** */
-    private static final int TIMEOUT = 3000;
-
     /** {@inheritDoc} */
     @Override protected void beforeTest() throws Exception {
         super.beforeTest();
diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/MvccDeadlockDetectionConfigTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/MvccDeadlockDetectionConfigTest.java
new file mode 100644
index 0000000..50157da
--- /dev/null
+++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/MvccDeadlockDetectionConfigTest.java
@@ -0,0 +1,159 @@
+/*
+ * 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.ignite.internal.processors.cache.mvcc;
+
+import java.util.concurrent.CyclicBarrier;
+import org.apache.ignite.Ignite;
+import org.apache.ignite.IgniteCache;
+import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.configuration.CacheConfiguration;
+import org.apache.ignite.configuration.IgniteConfiguration;
+import org.apache.ignite.configuration.TransactionConfiguration;
+import org.apache.ignite.internal.IgniteInternalFuture;
+import org.apache.ignite.internal.transactions.IgniteTxRollbackCheckedException;
+import org.apache.ignite.internal.transactions.IgniteTxTimeoutCheckedException;
+import org.apache.ignite.internal.util.typedef.X;
+import org.apache.ignite.testframework.GridTestUtils;
+import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
+import org.apache.ignite.transactions.Transaction;
+import org.junit.After;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import static org.apache.ignite.cache.CacheAtomicityMode.TRANSACTIONAL_SNAPSHOT;
+import static org.apache.ignite.transactions.TransactionConcurrency.PESSIMISTIC;
+import static org.apache.ignite.transactions.TransactionIsolation.REPEATABLE_READ;
+
+/** */
+@RunWith(JUnit4.class)
+public class MvccDeadlockDetectionConfigTest extends GridCommonAbstractTest  {
+    /** */
+    private boolean deadlockDetectionEnabled;
+
+    /** */
+    @After
+    public void stopCluster() {
+        stopAllGrids();
+    }
+
+    /** {@inheritDoc} */
+    @Override protected IgniteConfiguration getConfiguration(String igniteInstanceName) throws Exception {
+        int timeout = deadlockDetectionEnabled ? 1 : 0;
+
+        return super.getConfiguration(igniteInstanceName)
+            .setTransactionConfiguration(new TransactionConfiguration().setDeadlockTimeout(timeout));
+    }
+
+    /** */
+    @Test
+    public void deadlockDetectionDisabled() throws Exception {
+        deadlockDetectionEnabled = false;
+
+        Ignite ign = startGrid();
+
+        IgniteCache<Object, Object> cache = ign.createCache(new CacheConfiguration<>(DEFAULT_CACHE_NAME)
+            .setAtomicityMode(TRANSACTIONAL_SNAPSHOT));
+
+        CyclicBarrier b = new CyclicBarrier(2);
+
+        int txTimeout = 3_000;
+
+        IgniteInternalFuture<?> futA = GridTestUtils.runAsync(() -> {
+            try (Transaction tx = ign.transactions().txStart(PESSIMISTIC, REPEATABLE_READ, txTimeout, 0)) {
+                cache.put(1, 'a');
+                b.await();
+                cache.put(2, 'a');
+            }
+
+            return null;
+        });
+
+        IgniteInternalFuture<?> futB = GridTestUtils.runAsync(() -> {
+            try (Transaction tx = ign.transactions().txStart(PESSIMISTIC, REPEATABLE_READ, txTimeout, 0)) {
+                cache.put(2, 'b');
+                b.await();
+                cache.put(1, 'b');
+            }
+
+            return null;
+        });
+
+        IgniteCheckedException e = awaitCompletion(futA, futB);
+
+        assertTrue(e.toString(), e.hasCause(IgniteTxTimeoutCheckedException.class));
+    }
+
+    /** */
+    @Test
+    public void deadlockDetectionEnabled() throws Exception {
+        deadlockDetectionEnabled = true;
+
+        Ignite ign = startGrid();
+
+        IgniteCache<Object, Object> cache = ign.createCache(new CacheConfiguration<>(DEFAULT_CACHE_NAME)
+            .setAtomicityMode(TRANSACTIONAL_SNAPSHOT));
+
+        CyclicBarrier b = new CyclicBarrier(2);
+
+        IgniteInternalFuture<?> futA = GridTestUtils.runAsync(() -> {
+            try (Transaction tx = ign.transactions().txStart(PESSIMISTIC, REPEATABLE_READ)) {
+                cache.put(1, 'a');
+                b.await();
+                cache.put(2, 'a');
+            }
+
+            return null;
+        });
+
+        IgniteInternalFuture<?> futB = GridTestUtils.runAsync(() -> {
+            try (Transaction tx = ign.transactions().txStart(PESSIMISTIC, REPEATABLE_READ)) {
+                cache.put(2, 'b');
+                b.await();
+                cache.put(1, 'b');
+            }
+
+            return null;
+        });
+
+        IgniteCheckedException e = awaitCompletion(futA, futB);
+
+        assertTrue(e.toString(), X.hasCause(e, "Deadlock", IgniteTxRollbackCheckedException.class));
+    }
+
+    /** */
+    private IgniteCheckedException awaitCompletion(IgniteInternalFuture<?> fut1, IgniteInternalFuture<?> fut2) {
+        IgniteCheckedException e = null;
+
+        try {
+            fut1.get(10_000);
+        }
+        catch (IgniteCheckedException e1) {
+            e = e1;
+        }
+
+        try {
+            fut2.get(10_000);
+        }
+        catch (IgniteCheckedException e1) {
+            e = e1;
+        }
+
+        return e;
+    }
+}
diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/MvccDeadlockDetectionTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/MvccDeadlockDetectionTest.java
index fba0390..409e501 100644
--- a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/MvccDeadlockDetectionTest.java
+++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/MvccDeadlockDetectionTest.java
@@ -30,6 +30,7 @@ import org.apache.ignite.IgniteCheckedException;
 import org.apache.ignite.cache.query.SqlFieldsQuery;
 import org.apache.ignite.configuration.CacheConfiguration;
 import org.apache.ignite.configuration.IgniteConfiguration;
+import org.apache.ignite.configuration.TransactionConfiguration;
 import org.apache.ignite.internal.IgniteEx;
 import org.apache.ignite.internal.IgniteInternalFuture;
 import org.apache.ignite.internal.TestRecordingCommunicationSpi;
@@ -43,11 +44,8 @@ import org.apache.ignite.testframework.GridTestUtils.SF;
 import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
 import org.apache.ignite.transactions.Transaction;
 import org.junit.After;
-import org.junit.AfterClass;
-import org.junit.BeforeClass;
 import org.junit.Test;
 
-import static org.apache.ignite.IgniteSystemProperties.IGNITE_TX_DEADLOCK_DETECTION_INITIAL_DELAY;
 import static org.apache.ignite.cache.CacheAtomicityMode.TRANSACTIONAL_SNAPSHOT;
 import static org.apache.ignite.transactions.TransactionConcurrency.PESSIMISTIC;
 import static org.apache.ignite.transactions.TransactionIsolation.REPEATABLE_READ;
@@ -55,34 +53,22 @@ import static org.apache.ignite.transactions.TransactionIsolation.REPEATABLE_REA
 /** */
 public class MvccDeadlockDetectionTest extends GridCommonAbstractTest {
     /** */
-    @BeforeClass
-    public static void setUpClass() {
-        System.setProperty(IGNITE_TX_DEADLOCK_DETECTION_INITIAL_DELAY, "0");
-    }
-
-    /** */
-    @AfterClass
-    public static void tearDownClass() {
-        System.clearProperty(IGNITE_TX_DEADLOCK_DETECTION_INITIAL_DELAY);
-    }
-
-    /** */
     private IgniteEx client;
 
     /** {@inheritDoc} */
     @Override protected IgniteConfiguration getConfiguration(String igniteInstanceName) throws Exception {
-        IgniteConfiguration cfg = super.getConfiguration(igniteInstanceName);
-
-        cfg.setCommunicationSpi(new TestRecordingCommunicationSpi());
-
-        return cfg;
+        return super.getConfiguration(igniteInstanceName)
+            .setCommunicationSpi(new TestRecordingCommunicationSpi())
+            .setTransactionConfiguration(new TransactionConfiguration().setDeadlockTimeout(1));
     }
 
     /** */
     private void setUpGrids(int n, boolean indexed) throws Exception {
         Ignite ign = startGridsMultiThreaded(n);
+
         CacheConfiguration<Object, Object> ccfg = new CacheConfiguration<>(DEFAULT_CACHE_NAME)
             .setAtomicityMode(TRANSACTIONAL_SNAPSHOT);
+
         if (indexed)
             ccfg.setIndexedTypes(Integer.class, Integer.class);
 
diff --git a/modules/indexing/src/test/java/org/apache/ignite/testsuites/IgniteCacheMvccSqlTestSuite.java b/modules/indexing/src/test/java/org/apache/ignite/testsuites/IgniteCacheMvccSqlTestSuite.java
index d1ed2f3..130e754 100644
--- a/modules/indexing/src/test/java/org/apache/ignite/testsuites/IgniteCacheMvccSqlTestSuite.java
+++ b/modules/indexing/src/test/java/org/apache/ignite/testsuites/IgniteCacheMvccSqlTestSuite.java
@@ -67,6 +67,7 @@ import org.apache.ignite.internal.processors.cache.mvcc.CacheMvccSqlUpdateCounte
 import org.apache.ignite.internal.processors.cache.mvcc.CacheMvccStreamingInsertTest;
 import org.apache.ignite.internal.processors.cache.mvcc.CacheMvccTxNodeMappingTest;
 import org.apache.ignite.internal.processors.cache.mvcc.CacheMvccTxRecoveryTest;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccDeadlockDetectionConfigTest;
 import org.apache.ignite.internal.processors.cache.mvcc.MvccDeadlockDetectionTest;
 import org.apache.ignite.internal.processors.cache.mvcc.MvccRepeatableReadBulkOpsTest;
 import org.apache.ignite.internal.processors.cache.mvcc.MvccRepeatableReadOperationsTest;
@@ -94,6 +95,7 @@ import static org.apache.ignite.cache.CacheAtomicityMode.TRANSACTIONAL_SNAPSHOT;
 
     CacheMvccTxNodeMappingTest.class,
 
+    MvccDeadlockDetectionConfigTest.class,
     MvccDeadlockDetectionTest.class,
 
     // SQL vs CacheAPI consistency.
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/IgniteConfiguration.cs b/modules/platforms/dotnet/Apache.Ignite.Core/IgniteConfiguration.cs
index f293267..523ca65 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/IgniteConfiguration.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/IgniteConfiguration.cs
@@ -515,6 +515,7 @@ namespace Apache.Ignite.Core
                 writer.WriteLong((long) TransactionConfiguration.DefaultTimeout.TotalMilliseconds);
                 writer.WriteInt((int) TransactionConfiguration.PessimisticTransactionLogLinger.TotalMilliseconds);
                 writer.WriteLong((long) TransactionConfiguration.DefaultDefaultTimeoutOnPartitionMapExchange.TotalMilliseconds);
+                writer.WriteLong((long) TransactionConfiguration.DeadlockTimeout.TotalMilliseconds);
             }
             else
                 writer.WriteBoolean(false);
@@ -841,7 +842,8 @@ namespace Apache.Ignite.Core
                     DefaultTransactionIsolation = (TransactionIsolation) r.ReadInt(),
                     DefaultTimeout = TimeSpan.FromMilliseconds(r.ReadLong()),
                     PessimisticTransactionLogLinger = TimeSpan.FromMilliseconds(r.ReadInt()),
-                    DefaultTimeoutOnPartitionMapExchange = TimeSpan.FromMilliseconds(r.ReadLong())
+                    DefaultTimeoutOnPartitionMapExchange = TimeSpan.FromMilliseconds(r.ReadLong()),
+                    DeadlockTimeout = TimeSpan.FromMilliseconds(r.ReadLong())
                 };
             }
 
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/IgniteConfigurationSection.xsd b/modules/platforms/dotnet/Apache.Ignite.Core/IgniteConfigurationSection.xsd
index b023c3b..d1d7037 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/IgniteConfigurationSection.xsd
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/IgniteConfigurationSection.xsd
@@ -18,11 +18,10 @@
 -->
 
 <xs:schema id="IgniteConfigurationSection"
-    targetNamespace="http://ignite.apache.org/schema/dotnet/IgniteConfigurationSection"
-    elementFormDefault="qualified"
-    xmlns="http://ignite.apache.org/schema/dotnet/IgniteConfigurationSection"
-    xmlns:mstns="http://ignite.apache.org/schema/dotnet/IgniteConfigurationSection"
-    xmlns:xs="http://www.w3.org/2001/XMLSchema">
+           targetNamespace="http://ignite.apache.org/schema/dotnet/IgniteConfigurationSection"
+           elementFormDefault="qualified"
+           xmlns="http://ignite.apache.org/schema/dotnet/IgniteConfigurationSection"
+           xmlns:xs="http://www.w3.org/2001/XMLSchema">
 
     <xs:simpleType name="cacheMode" final="restriction">
         <xs:restriction base="xs:string">
@@ -1278,6 +1277,13 @@
                                 </xs:documentation>
                             </xs:annotation>
                         </xs:attribute>
+                        <xs:attribute name="deadlockTimeout" type="xs:string">
+                            <xs:annotation>
+                                <xs:documentation>
+                                    Timeout before starting deadlock detection. TimeSpan.Zero for disabling deadlock detection.
+                                </xs:documentation>
+                            </xs:annotation>
+                        </xs:attribute>
                     </xs:complexType>
                 </xs:element>
                 <xs:element name="logger" minOccurs="0">
@@ -1410,7 +1416,7 @@
                             </xs:annotation>
                         </xs:attribute>
                     </xs:complexType>
-                </xs:element>                
+                </xs:element>
                 <xs:element name="sqlConnectorConfiguration" minOccurs="0">
                     <xs:annotation>
                         <xs:documentation>SQL connector configuration (JDBC and ODBC).</xs:documentation>
@@ -1781,7 +1787,7 @@
                                                         <xs:documentation>Size of the checkpointing page buffer.</xs:documentation>
                                                     </xs:annotation>
                                                 </xs:attribute>
-                                            </xs:complexType>                                            
+                                            </xs:complexType>
                                         </xs:element>
                                     </xs:sequence>
                                 </xs:complexType>
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Transactions/TransactionConfiguration.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Transactions/TransactionConfiguration.cs
index e7f62f7..7220211 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/Transactions/TransactionConfiguration.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Transactions/TransactionConfiguration.cs
@@ -43,6 +43,9 @@ namespace Apache.Ignite.Core.Transactions
         /// <summary> The default value for <see cref="DefaultTimeoutOnPartitionMapExchange"/></summary>
         public static readonly TimeSpan DefaultDefaultTimeoutOnPartitionMapExchange = TimeSpan.Zero;
 
+        /// <summary> The default value for <see cref="DeadlockTimeout"/></summary>
+        public static readonly TimeSpan DefaultDeadlockTimeout = TimeSpan.FromSeconds(10);
+
         /// <summary>
         /// Gets or sets the cache transaction concurrency to use when one is not explicitly specified.
         /// </summary>
@@ -63,7 +66,7 @@ namespace Apache.Ignite.Core.Transactions
         public TimeSpan DefaultTimeout { get; set; }
 
         /// <summary>
-        /// Gets or sets the size of pessimistic transactions log stored on node in order to recover 
+        /// Gets or sets the size of pessimistic transactions log stored on node in order to recover
         /// transaction commit if originating node has left grid before it has sent all messages to transaction nodes.
         /// <code>0</code> for unlimited.
         /// </summary>
@@ -84,6 +87,13 @@ namespace Apache.Ignite.Core.Transactions
         public TimeSpan DefaultTimeoutOnPartitionMapExchange { get; set; }
 
         /// <summary>
+        /// Gets or sets timeout before starting deadlock detection.
+        /// <see cref="TimeSpan.Zero"/> for disabling deadlock detection.
+        /// </summary>
+        [DefaultValue(typeof(TimeSpan), "00:00:10")]
+        public TimeSpan DeadlockTimeout { get; set; }
+
+        /// <summary>
         /// Initializes a new instance of the <see cref="TransactionConfiguration" /> class.
         /// </summary>
         public TransactionConfiguration()
@@ -94,6 +104,7 @@ namespace Apache.Ignite.Core.Transactions
             PessimisticTransactionLogSize = DefaultPessimisticTransactionLogSize;
             PessimisticTransactionLogLinger = DefaultPessimisticTransactionLogLinger;
             DefaultTimeoutOnPartitionMapExchange = DefaultDefaultTimeoutOnPartitionMapExchange;
+            DeadlockTimeout = DefaultDeadlockTimeout;
         }
     }
 }