You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by dg...@apache.org on 2019/09/19 08:02:14 UTC

[ignite] branch master updated: IGNITE-12119 Fixed peer Class Loading has no retries - Fixes #6821.

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

dgovorukhin 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 27434c5  IGNITE-12119 Fixed peer Class Loading has no retries - Fixes #6821.
27434c5 is described below

commit 27434c5c6d909f3779875b0085ff6d99a7cd8847
Author: Ivan Bessonov <be...@gmail.com>
AuthorDate: Thu Sep 19 11:01:59 2019 +0300

    IGNITE-12119 Fixed peer Class Loading has no retries - Fixes #6821.
    
    Signed-off-by: Dmitriy Govorukhin <dm...@gmail.com>
---
 .../managers/deployment/GridDeployment.java        |   7 +
 .../deployment/GridDeploymentClassLoader.java      |  35 +++-
 .../deployment/GridDeploymentCommunication.java    |  15 +-
 .../deployment/GridDeploymentPerVersionStore.java  |  14 +-
 .../GridPeerDeploymentRetryModifiedTest.java       | 176 +++++++++++++++++++++
 .../internal/GridPeerDeploymentRetryTest.java      | 158 ++++++++++++++++++
 .../ignite/testsuites/IgniteBasicTestSuite.java    |   5 +
 .../ignite/tests/p2p/CacheDeploymentTestTask4.java |  87 ++++++++++
 8 files changed, 489 insertions(+), 8 deletions(-)

diff --git a/modules/core/src/main/java/org/apache/ignite/internal/managers/deployment/GridDeployment.java b/modules/core/src/main/java/org/apache/ignite/internal/managers/deployment/GridDeployment.java
index c47649d..a24cd7c 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/managers/deployment/GridDeployment.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/managers/deployment/GridDeployment.java
@@ -30,8 +30,10 @@ import java.util.Set;
 import java.util.UUID;
 import java.util.concurrent.Callable;
 import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.TimeoutException;
 import java.util.concurrent.atomic.AtomicStampedReference;
 import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.IgniteException;
 import org.apache.ignite.compute.ComputeTask;
 import org.apache.ignite.configuration.DeploymentMode;
 import org.apache.ignite.internal.processors.task.GridInternal;
@@ -42,6 +44,7 @@ import org.apache.ignite.internal.util.lang.GridPeerDeployAware;
 import org.apache.ignite.internal.util.lang.GridTuple;
 import org.apache.ignite.internal.util.tostring.GridToStringExclude;
 import org.apache.ignite.internal.util.typedef.F;
+import org.apache.ignite.internal.util.typedef.X;
 import org.apache.ignite.internal.util.typedef.internal.S;
 import org.apache.ignite.internal.util.typedef.internal.U;
 import org.apache.ignite.lang.IgniteBiTuple;
@@ -498,6 +501,10 @@ public class GridDeployment extends GridMetadataAwareAdapter implements GridDepl
                     }
                 }
             }
+            catch (IgniteException e) {
+                if (!X.hasCause(e, TimeoutException.class))
+                    throw e;
+            }
         }
 
         return cls;
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/managers/deployment/GridDeploymentClassLoader.java b/modules/core/src/main/java/org/apache/ignite/internal/managers/deployment/GridDeploymentClassLoader.java
index 640b0c7..346c4d2 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/managers/deployment/GridDeploymentClassLoader.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/managers/deployment/GridDeploymentClassLoader.java
@@ -28,7 +28,9 @@ import java.util.Map;
 import java.util.UUID;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.TimeoutException;
 import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.IgniteException;
 import org.apache.ignite.IgniteLogger;
 import org.apache.ignite.cluster.ClusterNode;
 import org.apache.ignite.configuration.DeploymentMode;
@@ -37,6 +39,7 @@ import org.apache.ignite.internal.util.GridBoundedLinkedHashSet;
 import org.apache.ignite.internal.util.GridByteArrayList;
 import org.apache.ignite.internal.util.tostring.GridToStringExclude;
 import org.apache.ignite.internal.util.tostring.GridToStringInclude;
+import org.apache.ignite.internal.util.typedef.X;
 import org.apache.ignite.internal.util.typedef.internal.S;
 import org.apache.ignite.internal.util.typedef.internal.U;
 import org.apache.ignite.lang.IgniteUuid;
@@ -445,6 +448,9 @@ class GridDeploymentClassLoader extends ClassLoader implements GridDeploymentInf
         // Catch Throwable to secure against any errors resulted from
         // corrupted class definitions or other user errors.
         catch (Exception e) {
+            if (X.hasCause(e, TimeoutException.class))
+                throw e;
+
             throw new ClassNotFoundException("Failed to load class due to unexpected error: " + name, e);
         }
 
@@ -581,6 +587,8 @@ class GridDeploymentClassLoader extends ClassLoader implements GridDeploymentInf
 
         IgniteCheckedException err = null;
 
+        TimeoutException te = null;
+
         for (UUID nodeId : nodeListCp) {
             if (nodeId.equals(ctx.discovery().localNode().id()))
                 // Skip local node as it is already used as parent class loader.
@@ -598,7 +606,14 @@ class GridDeploymentClassLoader extends ClassLoader implements GridDeploymentInf
             }
 
             try {
-                GridDeploymentResponse res = comm.sendResourceRequest(path, ldrId, node, endTime);
+                GridDeploymentResponse res = null;
+
+                try {
+                    res = comm.sendResourceRequest(path, ldrId, node, endTime);
+                }
+                catch (TimeoutException e) {
+                    te = e;
+                }
 
                 if (res == null) {
                     String msg = "Failed to send class-loading request to node (is node alive?) [node=" +
@@ -657,12 +672,28 @@ class GridDeploymentClassLoader extends ClassLoader implements GridDeploymentInf
             }
         }
 
+        if (te != null) {
+            err.addSuppressed(te);
+
+            throw new IgniteException(err);
+        }
+
         throw new ClassNotFoundException("Failed to peer load class [class=" + name + ", nodeClsLdrs=" +
             nodeLdrMapCp + ", parentClsLoader=" + getParent() + ']', err);
     }
 
     /** {@inheritDoc} */
     @Nullable @Override public InputStream getResourceAsStream(String name) {
+        try {
+            return getResourceAsStreamEx(name);
+        }
+        catch (TimeoutException ignore) {
+            return null;
+        }
+    }
+
+    /** */
+    @Nullable public InputStream getResourceAsStreamEx(String name) throws TimeoutException {
         assert !Thread.holdsLock(mux);
 
         if (byteMap != null && name.endsWith(".class")) {
@@ -702,7 +733,7 @@ class GridDeploymentClassLoader extends ClassLoader implements GridDeploymentInf
      * @param name Resource name.
      * @return InputStream for resource or {@code null} if resource could not be found.
      */
-    @Nullable private InputStream sendResourceRequest(String name) {
+    @Nullable private InputStream sendResourceRequest(String name) throws TimeoutException {
         assert !Thread.holdsLock(mux);
 
         long endTime = computeEndTime(p2pTimeout);
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/managers/deployment/GridDeploymentCommunication.java b/modules/core/src/main/java/org/apache/ignite/internal/managers/deployment/GridDeploymentCommunication.java
index daa9647..2f76ac3 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/managers/deployment/GridDeploymentCommunication.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/managers/deployment/GridDeploymentCommunication.java
@@ -22,6 +22,7 @@ import java.io.InputStream;
 import java.util.Collection;
 import java.util.HashSet;
 import java.util.UUID;
+import java.util.concurrent.TimeoutException;
 import org.apache.ignite.IgniteCheckedException;
 import org.apache.ignite.IgniteLogger;
 import org.apache.ignite.cluster.ClusterNode;
@@ -354,7 +355,7 @@ class GridDeploymentCommunication {
      * @throws IgniteCheckedException Thrown if there is no connection with remote node.
      */
     GridDeploymentResponse sendResourceRequest(final String rsrcName, IgniteUuid clsLdrId,
-        final ClusterNode dstNode, long threshold) throws IgniteCheckedException {
+        final ClusterNode dstNode, long threshold) throws IgniteCheckedException, TimeoutException {
         assert rsrcName != null;
         assert dstNode != null;
         assert clsLdrId != null;
@@ -471,13 +472,21 @@ class GridDeploymentCommunication {
 
                         timeout = threshold - U.currentTimeMillis();
                     }
+
+                    if (timeout <= 0)
+                        throw new TimeoutException();
                 }
                 catch (InterruptedException e) {
                     // Interrupt again to get it in the users code.
                     Thread.currentThread().interrupt();
 
-                    throw new IgniteCheckedException("Got interrupted while waiting for response from node: " +
-                        dstNode.id(), e);
+                    TimeoutException te = new TimeoutException(
+                        "Got interrupted while waiting for response from node: " + dstNode.id()
+                    );
+
+                    te.initCause(e);
+
+                    throw te;
                 }
             }
 
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/managers/deployment/GridDeploymentPerVersionStore.java b/modules/core/src/main/java/org/apache/ignite/internal/managers/deployment/GridDeploymentPerVersionStore.java
index d537d39..abd17a3 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/managers/deployment/GridDeploymentPerVersionStore.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/managers/deployment/GridDeploymentPerVersionStore.java
@@ -29,6 +29,7 @@ import java.util.Map;
 import java.util.UUID;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.TimeoutException;
 import org.apache.ignite.IgniteCheckedException;
 import org.apache.ignite.configuration.DeploymentMode;
 import org.apache.ignite.events.DeploymentEvent;
@@ -701,7 +702,7 @@ public class GridDeploymentPerVersionStore extends GridDeploymentStoreAdapter {
                 return false;
 
             // Temporary class loader.
-            ClassLoader temp = new GridDeploymentClassLoader(
+            GridDeploymentClassLoader temp = new GridDeploymentClassLoader(
                 IgniteUuid.fromUuid(ctx.localNodeId()),
                 meta.userVersion(),
                 meta.deploymentMode(),
@@ -724,7 +725,14 @@ public class GridDeploymentPerVersionStore extends GridDeploymentStoreAdapter {
             InputStream rsrcIn = null;
 
             try {
-                rsrcIn = temp.getResourceAsStream(path);
+                boolean timeout = false;
+
+                try {
+                    rsrcIn = temp.getResourceAsStreamEx(path);
+                }
+                catch (TimeoutException e) {
+                    timeout = true;
+                }
 
                 boolean found = rsrcIn != null;
 
@@ -744,7 +752,7 @@ public class GridDeploymentPerVersionStore extends GridDeploymentStoreAdapter {
 
                         return false;
                     }
-                    else
+                    else if (!timeout)
                         // Cache result if classloader is still alive.
                         ldrRsrcCache.put(clsName, found);
                 }
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/GridPeerDeploymentRetryModifiedTest.java b/modules/core/src/test/java/org/apache/ignite/internal/GridPeerDeploymentRetryModifiedTest.java
new file mode 100644
index 0000000..e8535b4
--- /dev/null
+++ b/modules/core/src/test/java/org/apache/ignite/internal/GridPeerDeploymentRetryModifiedTest.java
@@ -0,0 +1,176 @@
+/*
+ * 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;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+import org.apache.ignite.Ignite;
+import org.apache.ignite.IgniteException;
+import org.apache.ignite.cluster.ClusterNode;
+import org.apache.ignite.compute.ComputeTask;
+import org.apache.ignite.configuration.CacheConfiguration;
+import org.apache.ignite.configuration.DeploymentMode;
+import org.apache.ignite.configuration.IgniteConfiguration;
+import org.apache.ignite.internal.managers.deployment.GridDeploymentResponse;
+import org.apache.ignite.internal.util.typedef.T2;
+import org.apache.ignite.testframework.GridTestExternalClassLoader;
+import org.apache.ignite.testframework.config.GridTestProperties;
+import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
+import org.junit.Test;
+
+/**
+ * Tests affinity and affinity mapper P2P loading.
+ */
+public class GridPeerDeploymentRetryModifiedTest extends GridCommonAbstractTest {
+    /** */
+    private static final String EXT_TASK_CLASS_NAME = "org.apache.ignite.tests.p2p.CacheDeploymentTestTask4";
+
+    /** URL of classes. */
+    private static final URL[] URLS;
+
+    /** Current deployment mode. Used in {@link #getConfiguration(String)}. */
+    private DeploymentMode depMode;
+
+    /**
+     * Initialize URLs.
+     */
+    static {
+        try {
+            URLS = new URL[] {new URL(GridTestProperties.getProperty("p2p.uri.cls"))};
+        }
+        catch (MalformedURLException e) {
+            throw new RuntimeException("Define property p2p.uri.cls", e);
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override protected IgniteConfiguration getConfiguration(String igniteInstanceName) throws Exception {
+        IgniteConfiguration cfg = super.getConfiguration(igniteInstanceName);
+
+        cfg.setCommunicationSpi(new TestRecordingCommunicationSpi());
+        cfg.setDeploymentMode(depMode);
+
+        cfg.setCacheConfiguration(new CacheConfiguration().setName(DEFAULT_CACHE_NAME));
+
+        return cfg;
+    }
+
+    /**
+     *
+     */
+    public GridPeerDeploymentRetryModifiedTest() {
+        super(false);
+    }
+
+    /**
+     * Test {@link DeploymentMode#PRIVATE} mode.
+     *
+     * @throws Exception if error occur.
+     */
+    @Test
+    public void testPrivateMode() throws Exception {
+        depMode = DeploymentMode.PRIVATE;
+
+        deploymentTest();
+    }
+
+    /**
+     * Test {@link DeploymentMode#ISOLATED} mode.
+     *
+     * @throws Exception if error occur.
+     */
+    @Test
+    public void testIsolatedMode() throws Exception {
+        depMode = DeploymentMode.ISOLATED;
+
+        deploymentTest();
+    }
+
+    /**
+     * Test {@link DeploymentMode#CONTINUOUS} mode.
+     *
+     * @throws Exception if error occur.
+     */
+    @Test
+    public void testContinuousMode() throws Exception {
+        depMode = DeploymentMode.CONTINUOUS;
+
+        deploymentTest();
+    }
+
+    /**
+     * Test {@link DeploymentMode#SHARED} mode.
+     *
+     * @throws Exception if error occur.
+     */
+    @Test
+    public void testSharedMode() throws Exception {
+        depMode = DeploymentMode.SHARED;
+
+        deploymentTest();
+    }
+
+    /** @throws Exception If failed. */
+    private void deploymentTest() throws Exception {
+        Ignite g1 = startGrid(1);
+        IgniteEx g2 = startGrid(2);
+
+        try {
+            GridTestExternalClassLoader ldr = new GridTestExternalClassLoader(URLS);
+
+            ClusterNode node = g1.cluster().node(g2.cluster().localNode().id());
+
+            g1.compute(g1.cluster().forRemotes()).execute(
+                (ComputeTask<Object, T2>)ldr.loadClass(EXT_TASK_CLASS_NAME).newInstance(),
+                new T2<>(node, null)
+            );
+
+            TestRecordingCommunicationSpi rec1 =
+                (TestRecordingCommunicationSpi)g1.configuration().getCommunicationSpi();
+
+            rec1.blockMessages((n, message) -> message instanceof GridDeploymentResponse);
+
+            ComputeTask<Object, T2> task = (ComputeTask<Object, T2>)ldr.loadClass(EXT_TASK_CLASS_NAME).newInstance();
+
+            try {
+                g1.compute(g1.cluster().forRemotes()).withTimeout(2000).execute(task, new T2<>(node, "foo"));
+
+                fail("Exception should be thrown");
+            }
+            catch (IgniteException ignore) {
+                // Expected exception.
+                //ignore.printStackTrace();
+            }
+
+            rec1.stopBlock(false, null, true, true);
+
+            try {
+                g1.compute(g1.cluster().forRemotes()).execute(task, new T2<>(node, "bar"));
+            }
+            catch (Exception e) {
+                throw new RuntimeException(e);
+            }
+
+            assertFalse(g2.cache(DEFAULT_CACHE_NAME).containsKey("foo"));
+            assertTrue(g2.cache(DEFAULT_CACHE_NAME).containsKey("bar"));
+        }
+        finally {
+            stopAllGrids(true);
+        }
+    }
+}
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/GridPeerDeploymentRetryTest.java b/modules/core/src/test/java/org/apache/ignite/internal/GridPeerDeploymentRetryTest.java
new file mode 100644
index 0000000..df80b11
--- /dev/null
+++ b/modules/core/src/test/java/org/apache/ignite/internal/GridPeerDeploymentRetryTest.java
@@ -0,0 +1,158 @@
+/*
+ * 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;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+import org.apache.ignite.Ignite;
+import org.apache.ignite.IgniteException;
+import org.apache.ignite.cluster.ClusterNode;
+import org.apache.ignite.compute.ComputeTask;
+import org.apache.ignite.configuration.DeploymentMode;
+import org.apache.ignite.configuration.IgniteConfiguration;
+import org.apache.ignite.internal.managers.deployment.GridDeploymentResponse;
+import org.apache.ignite.testframework.GridTestExternalClassLoader;
+import org.apache.ignite.testframework.config.GridTestProperties;
+import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
+import org.junit.Test;
+
+/**
+ * Tests affinity and affinity mapper P2P loading.
+ */
+public class GridPeerDeploymentRetryTest extends GridCommonAbstractTest {
+    /** */
+    private static final String EXT_TASK_CLASS_NAME = "org.apache.ignite.tests.p2p.CacheDeploymentTestTask2";
+
+    /** URL of classes. */
+    private static final URL[] URLS;
+
+    /** Current deployment mode. Used in {@link #getConfiguration(String)}. */
+    private DeploymentMode depMode;
+
+    /**
+     * Initialize URLs.
+     */
+    static {
+        try {
+            URLS = new URL[] {new URL(GridTestProperties.getProperty("p2p.uri.cls"))};
+        }
+        catch (MalformedURLException e) {
+            throw new RuntimeException("Define property p2p.uri.cls", e);
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override protected IgniteConfiguration getConfiguration(String igniteInstanceName) throws Exception {
+        IgniteConfiguration cfg = super.getConfiguration(igniteInstanceName);
+
+        cfg.setCommunicationSpi(new TestRecordingCommunicationSpi());
+        cfg.setDeploymentMode(depMode);
+
+        return cfg;
+    }
+
+    /**
+     *
+     */
+    public GridPeerDeploymentRetryTest() {
+        super(false);
+    }
+
+    /**
+     * Test {@link DeploymentMode#PRIVATE} mode.
+     *
+     * @throws Exception if error occur.
+     */
+    @Test
+    public void testPrivateMode() throws Exception {
+        depMode = DeploymentMode.PRIVATE;
+
+        deploymentTest();
+    }
+
+    /**
+     * Test {@link DeploymentMode#ISOLATED} mode.
+     *
+     * @throws Exception if error occur.
+     */
+    @Test
+    public void testIsolatedMode() throws Exception {
+        depMode = DeploymentMode.ISOLATED;
+
+        deploymentTest();
+    }
+
+    /**
+     * Test {@link DeploymentMode#CONTINUOUS} mode.
+     *
+     * @throws Exception if error occur.
+     */
+    @Test
+    public void testContinuousMode() throws Exception {
+        depMode = DeploymentMode.CONTINUOUS;
+
+        deploymentTest();
+    }
+
+    /**
+     * Test {@link DeploymentMode#SHARED} mode.
+     *
+     * @throws Exception if error occur.
+     */
+    @Test
+    public void testSharedMode() throws Exception {
+        depMode = DeploymentMode.SHARED;
+
+        deploymentTest();
+    }
+
+    /** @throws Exception If failed. */
+    private void deploymentTest() throws Exception {
+        Ignite g1 = startGrid(1);
+        Ignite g2 = startGrid(2);
+
+        try {
+            GridTestExternalClassLoader ldr = new GridTestExternalClassLoader(URLS);
+
+            TestRecordingCommunicationSpi rec1 =
+                (TestRecordingCommunicationSpi)g1.configuration().getCommunicationSpi();
+
+            rec1.blockMessages((node, message) -> message instanceof GridDeploymentResponse);
+
+            ComputeTask<Object, ?> task = (ComputeTask<Object, ?>)ldr.loadClass(EXT_TASK_CLASS_NAME).newInstance();
+
+            ClusterNode node = g1.cluster().node(g2.cluster().localNode().id());
+
+            try {
+                g1.compute(g1.cluster().forRemotes()).execute(task, node);
+
+                fail("Exception should be thrown");
+            }
+            catch (IgniteException ignore) {
+                // Expected exception.
+            }
+
+            rec1.stopBlock(false, null, true, true);
+
+            g1.compute(g1.cluster().forRemotes()).execute(task, node);
+        }
+        finally {
+            stopAllGrids(true);
+        }
+    }
+}
diff --git a/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteBasicTestSuite.java b/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteBasicTestSuite.java
index ac8dd15..0cb88d8 100644
--- a/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteBasicTestSuite.java
+++ b/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteBasicTestSuite.java
@@ -33,6 +33,8 @@ import org.apache.ignite.internal.GridLifecycleAwareSelfTest;
 import org.apache.ignite.internal.GridLifecycleBeanSelfTest;
 import org.apache.ignite.internal.GridMBeansTest;
 import org.apache.ignite.internal.GridNodeMetricsLogSelfTest;
+import org.apache.ignite.internal.GridPeerDeploymentRetryModifiedTest;
+import org.apache.ignite.internal.GridPeerDeploymentRetryTest;
 import org.apache.ignite.internal.GridProjectionForCachesSelfTest;
 import org.apache.ignite.internal.GridReduceSelfTest;
 import org.apache.ignite.internal.GridReleaseTypeSelfTest;
@@ -193,6 +195,9 @@ import org.junit.runners.Suite;
 
     WALRecordTest.class,
 
+    GridPeerDeploymentRetryTest.class,
+    GridPeerDeploymentRetryModifiedTest.class,
+
     // Basic DB data structures.
     BPlusTreeSelfTest.class,
     BPlusTreeFakeReuseSelfTest.class,
diff --git a/modules/extdata/p2p/src/main/java/org/apache/ignite/tests/p2p/CacheDeploymentTestTask4.java b/modules/extdata/p2p/src/main/java/org/apache/ignite/tests/p2p/CacheDeploymentTestTask4.java
new file mode 100644
index 0000000..33b1b88
--- /dev/null
+++ b/modules/extdata/p2p/src/main/java/org/apache/ignite/tests/p2p/CacheDeploymentTestTask4.java
@@ -0,0 +1,87 @@
+/*
+ * 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.tests.p2p;
+
+import java.util.List;
+import java.util.Map;
+import org.apache.ignite.Ignite;
+import org.apache.ignite.cluster.ClusterNode;
+import org.apache.ignite.compute.ComputeJob;
+import org.apache.ignite.compute.ComputeJobAdapter;
+import org.apache.ignite.compute.ComputeJobResult;
+import org.apache.ignite.compute.ComputeTaskAdapter;
+import org.apache.ignite.internal.util.typedef.F;
+import org.apache.ignite.internal.util.typedef.T2;
+import org.apache.ignite.internal.util.typedef.X;
+import org.apache.ignite.resources.IgniteInstanceResource;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Test task for {@code GridCacheDeploymentSelfTest}.
+ */
+public class CacheDeploymentTestTask4 extends ComputeTaskAdapter<T2<ClusterNode, String>, Object> {
+    /** Serial version uid. */
+    private static final long serialVersionUID = 0L;
+
+    /** {@inheritDoc} */
+    @NotNull @Override public Map<? extends ComputeJob, ClusterNode> map(List<ClusterNode> subgrid,
+        @Nullable T2<ClusterNode, String> tup) {
+        final String val = tup.getValue();
+
+        return F.asMap(
+                new ComputeJobAdapter() {
+                    private static final long serialVersionUID = 0L;
+
+                    /** Ignite instance. */
+                    @IgniteInstanceResource
+                    private Ignite ignite;
+
+                    /** {@inheritDoc} */
+                    @Override public Object execute() {
+                        X.println("Executing CacheDeploymentTestTask4 job on node " +
+                                ignite.cluster().localNode().id());
+
+                        try {
+                            if (val != null)
+                                ignite.cache("default").put(val, Val.getVal());
+                        }
+                        catch (Exception e) {
+                            throw new RuntimeException(e);
+                        }
+
+                        return null;
+                    }
+                },
+                tup.get1()
+        );
+    }
+
+    /** */
+    public static class Val {
+        /** */
+        private static Object getVal() {
+            return new CacheDeploymentTestValue();
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Nullable @Override public Object reduce(List<ComputeJobResult> results) {
+        return null;
+    }
+}