You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by sb...@apache.org on 2018/09/27 12:54:33 UTC

[22/50] [abbrv] ignite git commit: IGNITE-9381 Clear class cache on p2p undeploy - Fixes #4784.

IGNITE-9381 Clear class cache on p2p undeploy - Fixes #4784.

Signed-off-by: Alexey Goncharuk <al...@gmail.com>


Project: http://git-wip-us.apache.org/repos/asf/ignite/repo
Commit: http://git-wip-us.apache.org/repos/asf/ignite/commit/d40dcb3a
Tree: http://git-wip-us.apache.org/repos/asf/ignite/tree/d40dcb3a
Diff: http://git-wip-us.apache.org/repos/asf/ignite/diff/d40dcb3a

Branch: refs/heads/ignite-gg-14206
Commit: d40dcb3a8f25a5bb098662fa7270b69eb3cc3ab5
Parents: fe35a64
Author: Sergey Antonov <an...@gmail.com>
Authored: Wed Sep 26 16:55:06 2018 +0300
Committer: Alexey Goncharuk <al...@gmail.com>
Committed: Wed Sep 26 17:19:43 2018 +0300

----------------------------------------------------------------------
 .../GridDeploymentPerVersionStore.java          |   6 +
 .../cache/CacheClassLoaderMarker.java           |  24 ++
 .../cache/GridCacheDeploymentManager.java       |   2 +-
 .../ignite/internal/util/IgniteUtils.java       |  22 +-
 .../ignite/p2p/P2PScanQueryUndeployTest.java    | 250 +++++++++++++++++++
 .../testsuites/IgniteP2PSelfTestSuite.java      |   2 +
 .../ignite/tests/p2p/AlwaysTruePredicate.java   |  30 +++
 7 files changed, 334 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ignite/blob/d40dcb3a/modules/core/src/main/java/org/apache/ignite/internal/managers/deployment/GridDeploymentPerVersionStore.java
----------------------------------------------------------------------
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 14aa49f..56a3f3e 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
@@ -1290,6 +1290,12 @@ public class GridDeploymentPerVersionStore extends GridDeploymentStoreAdapter {
 
                 ctx.cache().onUndeployed(ldr);
 
+               // Clear static class cache.
+                U.clearClassFromClassCache(ctx.cache().context().deploy().globalLoader(), sampleClassName());
+
+                for (String alias : deployedClassMap().keySet())
+                    U.clearClassFromClassCache(ctx.cache().context().deploy().globalLoader(), alias);
+
                 // Clear optimized marshaller's cache.
                 if (ctx.config().getMarshaller() instanceof AbstractMarshaller)
                     ((AbstractMarshaller)ctx.config().getMarshaller()).onUndeploy(ldr);

http://git-wip-us.apache.org/repos/asf/ignite/blob/d40dcb3a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheClassLoaderMarker.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheClassLoaderMarker.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheClassLoaderMarker.java
new file mode 100644
index 0000000..b575ec7
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheClassLoaderMarker.java
@@ -0,0 +1,24 @@
+/*
+ * 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;
+
+/**
+ * It's a marker interface for detecting GridCacheDeploymentManager$CacheClassLoader.
+ */
+public interface CacheClassLoaderMarker {
+    // Marker interface.
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ignite/blob/d40dcb3a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheDeploymentManager.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheDeploymentManager.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheDeploymentManager.java
index b70439a..b34da62 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheDeploymentManager.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheDeploymentManager.java
@@ -760,7 +760,7 @@ public class GridCacheDeploymentManager<K, V> extends GridCacheSharedManagerAdap
     /**
      * Cache class loader.
      */
-    private class CacheClassLoader extends ClassLoader {
+    private class CacheClassLoader extends ClassLoader implements CacheClassLoaderMarker {
         /** */
         private final String[] p2pExclude;
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/d40dcb3a/modules/core/src/main/java/org/apache/ignite/internal/util/IgniteUtils.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/util/IgniteUtils.java b/modules/core/src/main/java/org/apache/ignite/internal/util/IgniteUtils.java
index 6320c9f..c5054ad 100755
--- a/modules/core/src/main/java/org/apache/ignite/internal/util/IgniteUtils.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/util/IgniteUtils.java
@@ -193,6 +193,7 @@ import org.apache.ignite.internal.events.DiscoveryCustomEvent;
 import org.apache.ignite.internal.managers.communication.GridIoManager;
 import org.apache.ignite.internal.managers.deployment.GridDeploymentInfo;
 import org.apache.ignite.internal.mxbean.IgniteStandardMXBean;
+import org.apache.ignite.internal.processors.cache.CacheClassLoaderMarker;
 import org.apache.ignite.internal.processors.cache.GridCacheAttributes;
 import org.apache.ignite.internal.processors.cache.GridCacheContext;
 import org.apache.ignite.internal.processors.cache.GridCacheSharedContext;
@@ -8694,7 +8695,13 @@ public abstract class IgniteUtils {
             if (clsFilter != null && !clsFilter.apply(clsName))
                 throw new RuntimeException("Deserialization of class " + clsName + " is disallowed.");
 
-            Class old = ldrMap.putIfAbsent(clsName, cls = Class.forName(clsName, true, ldr));
+            // Avoid class caching inside Class.forName
+            if (ldr instanceof CacheClassLoaderMarker)
+                cls = ldr.loadClass(clsName);
+            else
+                cls = Class.forName(clsName, true, ldr);
+
+            Class old = ldrMap.putIfAbsent(clsName, cls);
 
             if (old != null)
                 cls = old;
@@ -8704,6 +8711,19 @@ public abstract class IgniteUtils {
     }
 
     /**
+     * Clears class associated with provided class loader from class cache.
+     *
+     * @param ldr Class loader.
+     * @param clsName Class name of clearing class.
+     */
+    public static void clearClassFromClassCache(ClassLoader ldr, String clsName){
+        ConcurrentMap<String, Class> map = classCache.get(ldr);
+
+        if (map!=null)
+            map.remove(clsName);
+    }
+
+    /**
      * Clears class cache for provided loader.
      *
      * @param ldr Class loader.

http://git-wip-us.apache.org/repos/asf/ignite/blob/d40dcb3a/modules/core/src/test/java/org/apache/ignite/p2p/P2PScanQueryUndeployTest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/p2p/P2PScanQueryUndeployTest.java b/modules/core/src/test/java/org/apache/ignite/p2p/P2PScanQueryUndeployTest.java
new file mode 100644
index 0000000..73ce917
--- /dev/null
+++ b/modules/core/src/test/java/org/apache/ignite/p2p/P2PScanQueryUndeployTest.java
@@ -0,0 +1,250 @@
+/*
+ * 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.p2p;
+
+import java.lang.reflect.Field;
+import java.net.URL;
+import java.util.Collections;
+import java.util.Set;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.stream.Collectors;
+import org.apache.ignite.Ignite;
+import org.apache.ignite.IgniteCache;
+import org.apache.ignite.IgniteException;
+import org.apache.ignite.cache.query.ScanQuery;
+import org.apache.ignite.cluster.ClusterNode;
+import org.apache.ignite.configuration.CacheConfiguration;
+import org.apache.ignite.configuration.IgniteConfiguration;
+import org.apache.ignite.internal.managers.communication.GridIoMessage;
+import org.apache.ignite.internal.managers.deployment.GridDeploymentRequest;
+import org.apache.ignite.internal.util.IgniteUtils;
+import org.apache.ignite.internal.util.typedef.internal.U;
+import org.apache.ignite.lang.IgniteBiPredicate;
+import org.apache.ignite.lang.IgniteInClosure;
+import org.apache.ignite.plugin.extensions.communication.Message;
+import org.apache.ignite.spi.IgniteSpiException;
+import org.apache.ignite.spi.communication.tcp.TcpCommunicationSpi;
+import org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi;
+import org.apache.ignite.spi.discovery.tcp.ipfinder.vm.TcpDiscoveryVmIpFinder;
+import org.apache.ignite.testframework.GridTestExternalClassLoader;
+import org.apache.ignite.testframework.config.GridTestProperties;
+import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
+
+/** */
+public class P2PScanQueryUndeployTest extends GridCommonAbstractTest {
+    /** Predicate classname. */
+    private static final String PREDICATE_CLASSNAME = "org.apache.ignite.tests.p2p.AlwaysTruePredicate";
+
+    /** */
+    private static final String TEST_PREDICATE_RESOURCE_NAME = U.classNameToResourceName(PREDICATE_CLASSNAME);
+
+    /** Cache name. */
+    private static final String CACHE_NAME = "test-cache";
+
+    /** Client instance name. */
+    private static final String CLIENT_INSTANCE_NAME = "client";
+
+    /** {@inheritDoc} */
+    @Override protected IgniteConfiguration getConfiguration(String igniteInstanceName) throws Exception {
+        IgniteConfiguration cfg = super.getConfiguration(igniteInstanceName);
+
+        cfg.setPeerClassLoadingEnabled(true);
+
+        cfg.setCacheConfiguration(
+            new CacheConfiguration()
+                .setName(CACHE_NAME)
+                .setBackups(1)
+        );
+
+        cfg.setDiscoverySpi(
+            new TcpDiscoverySpi()
+                .setIpFinder(
+                    new TcpDiscoveryVmIpFinder(true)
+                        .setAddresses(Collections.singletonList("127.0.0.1:47500..47509"))
+                )
+        );
+
+        cfg.setCommunicationSpi(new MessageCountingCommunicationSpi());
+
+        if (igniteInstanceName.equals(CLIENT_INSTANCE_NAME))
+            cfg.setClientMode(true);
+
+        return cfg;
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void beforeTest() throws Exception {
+        super.beforeTest();
+
+        stopAllGrids();
+
+        MessageCountingCommunicationSpi.resetDeploymentRequestCounter();
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void afterTest() throws Exception {
+        super.afterTest();
+
+        stopAllGrids();
+    }
+
+    /**
+     * Checks, that after client's reconnect to cluster will be redeployed from client node.
+     *
+     * @throws Exception if test failed.
+     */
+    public void testAfterClientDisconnect() throws Exception {
+        ClassLoader extClsLdr = new GridTestExternalClassLoader(new URL[] {new URL(GridTestProperties.getProperty("p2p.uri.cls"))});
+
+        assertFalse(classFound(getClass().getClassLoader(), PREDICATE_CLASSNAME));
+
+        Class predCls = extClsLdr.loadClass(PREDICATE_CLASSNAME);
+
+        startGrid(0);
+
+        Ignite client = startGrid(CLIENT_INSTANCE_NAME);
+
+        client.cluster().active(true);
+
+        awaitPartitionMapExchange();
+
+        IgniteCache<Integer, String> cache = client.getOrCreateCache(CACHE_NAME);
+
+        cache.put(1, "foo");
+
+        invokeScanQueryAndStopClient(client, predCls);
+
+        MessageCountingCommunicationSpi.resetDeploymentRequestCounter();
+
+        client = startGrid(CLIENT_INSTANCE_NAME);
+
+        invokeScanQueryAndStopClient(client, predCls);
+    }
+
+    /**
+     * @param client ignite instance.
+     * @param predCls loaded predicate class.
+     * @throws Exception if failed.
+     */
+    private void invokeScanQueryAndStopClient(Ignite client, Class predCls) throws Exception {
+        IgniteCache<Integer, String> cache = client.getOrCreateCache(CACHE_NAME);
+
+        assertEquals("Invalid number of sent grid deployment requests", 0, MessageCountingCommunicationSpi.deploymentRequestCount());
+
+        assertFalse(PREDICATE_CLASSNAME + " mustn't be cached! ", igniteUtilsCachedClasses().contains(PREDICATE_CLASSNAME));
+
+        cache.query(new ScanQuery<>((IgniteBiPredicate<Integer, String>)predCls.newInstance())).getAll();
+
+        // first request is GridDeployment.java 716 and second is GridDeployment.java 501
+        assertEquals("Invalid number of sent grid deployment requests", 2, MessageCountingCommunicationSpi.deploymentRequestCount());
+
+        assertTrue(PREDICATE_CLASSNAME + " must be cached! ", igniteUtilsCachedClasses().contains(PREDICATE_CLASSNAME));
+
+        client.close();
+
+        assertFalse(PREDICATE_CLASSNAME + " mustn't be cached! ", igniteUtilsCachedClasses().contains(PREDICATE_CLASSNAME));
+    }
+
+    /**
+     * @return All class names cached in IgniteUtils.classCache field
+     * @throws Exception if something wrong.
+     */
+    private Set<String> igniteUtilsCachedClasses() throws Exception {
+        Field f = IgniteUtils.class.getDeclaredField("classCache");
+
+        f.setAccessible(true);
+
+        ConcurrentMap<ClassLoader, ConcurrentMap<String, Class>> map =
+            (ConcurrentMap<ClassLoader, ConcurrentMap<String, Class>>)f.get(null);
+
+        return map.values().stream().flatMap(x -> x.keySet().stream()).collect(Collectors.toSet());
+    }
+
+    /**
+     * @param clsLdr classloader.
+     * @param name classname.
+     * @return true if class loaded by classloader, false if class not found.
+     */
+    private boolean classFound(ClassLoader clsLdr, String name) {
+        try {
+            clsLdr.loadClass(name);
+
+            return true;
+        }
+        catch (ClassNotFoundException e) {
+            return false;
+        }
+    }
+
+    /** */
+    private static class MessageCountingCommunicationSpi extends TcpCommunicationSpi {
+        /** */
+        private static final AtomicInteger reqCnt = new AtomicInteger();
+
+        /** {@inheritDoc} */
+        @Override public void sendMessage(ClusterNode node,
+            Message msg,
+            IgniteInClosure<IgniteException> ackClosure
+        ) throws IgniteSpiException {
+            if (msg instanceof GridIoMessage && isDeploymentRequestMessage((GridIoMessage)msg))
+                reqCnt.incrementAndGet();
+
+            super.sendMessage(node, msg, ackClosure);
+        }
+
+        /**
+         * @return Number of deployment requests.
+         */
+        public static int deploymentRequestCount() {
+            return reqCnt.get();
+        }
+
+        /**
+         * Reset request counter.
+         */
+        public static void resetDeploymentRequestCounter() {
+            reqCnt.set(0);
+        }
+
+        /**
+         * Checks if it is a p2p deployment request message with test predicate resource.
+         *
+         * @param msg Message to check.
+         * @return {@code True} if this is a p2p message.
+         */
+        private boolean isDeploymentRequestMessage(GridIoMessage msg) {
+            try {
+                if (msg.message() instanceof GridDeploymentRequest) {
+                    // rsrcName getter have only default access level. So, use reflection for access to field value.
+                    GridDeploymentRequest req = (GridDeploymentRequest)msg.message();
+
+                    Field f = GridDeploymentRequest.class.getDeclaredField("rsrcName");
+
+                    f.setAccessible(true);
+
+                    return f.get(req).equals(TEST_PREDICATE_RESOURCE_NAME);
+                }
+            }
+            catch (Exception e) {
+                throw new RuntimeException(e);
+            }
+
+            return false;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/d40dcb3a/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteP2PSelfTestSuite.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteP2PSelfTestSuite.java b/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteP2PSelfTestSuite.java
index 7218174..76b0aa0 100644
--- a/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteP2PSelfTestSuite.java
+++ b/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteP2PSelfTestSuite.java
@@ -36,6 +36,7 @@ import org.apache.ignite.p2p.GridP2PSameClassLoaderSelfTest;
 import org.apache.ignite.p2p.GridP2PTestTaskExecutionTest;
 import org.apache.ignite.p2p.GridP2PTimeoutSelfTest;
 import org.apache.ignite.p2p.GridP2PUndeploySelfTest;
+import org.apache.ignite.p2p.P2PScanQueryUndeployTest;
 import org.apache.ignite.p2p.P2PStreamingClassLoaderTest;
 import org.apache.ignite.p2p.SharedDeploymentTest;
 import org.apache.ignite.testframework.GridTestUtils;
@@ -78,6 +79,7 @@ public class IgniteP2PSelfTestSuite extends TestSuite {
         suite.addTest(new TestSuite(DeploymentClassLoaderCallableTest.class));
         suite.addTest(new TestSuite(P2PStreamingClassLoaderTest.class));
         suite.addTest(new TestSuite(SharedDeploymentTest.class));
+        suite.addTest(new TestSuite(P2PScanQueryUndeployTest.class));
         GridTestUtils.addTestIfNeeded(suite, GridDeploymentMessageCountSelfTest.class, ignoredTests);
 
         return suite;

http://git-wip-us.apache.org/repos/asf/ignite/blob/d40dcb3a/modules/extdata/p2p/src/main/java/org/apache/ignite/tests/p2p/AlwaysTruePredicate.java
----------------------------------------------------------------------
diff --git a/modules/extdata/p2p/src/main/java/org/apache/ignite/tests/p2p/AlwaysTruePredicate.java b/modules/extdata/p2p/src/main/java/org/apache/ignite/tests/p2p/AlwaysTruePredicate.java
new file mode 100644
index 0000000..3918f71
--- /dev/null
+++ b/modules/extdata/p2p/src/main/java/org/apache/ignite/tests/p2p/AlwaysTruePredicate.java
@@ -0,0 +1,30 @@
+/*
+ * 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 org.apache.ignite.lang.IgniteBiPredicate;
+
+/**
+ *
+ */
+public class AlwaysTruePredicate implements IgniteBiPredicate<Object, Object> {
+    /** */
+    @Override public boolean apply(Object k, Object v) {
+        return new CacheDeploymentAlwaysTruePredicate().apply(k,v);
+    }
+}
\ No newline at end of file