package org.infinispan.statetransfer; import static java.util.concurrent.TimeUnit.SECONDS; import static org.infinispan.test.TestingUtil.blockUntilViewsReceived; import static org.infinispan.test.TestingUtil.extractGlobalComponent; import static org.infinispan.test.TestingUtil.extractGlobalComponentRegistry; import static org.infinispan.test.TestingUtil.replaceComponent; import static org.infinispan.test.TestingUtil.waitForNoRebalance; import static org.testng.AssertJUnit.assertEquals; import java.util.concurrent.Callable; import java.util.concurrent.Future; import org.infinispan.Cache; import org.infinispan.commands.ReplicableCommand; import org.infinispan.configuration.cache.CacheMode; import org.infinispan.configuration.cache.Configuration; import org.infinispan.configuration.cache.ConfigurationBuilder; import org.infinispan.configuration.global.GlobalConfigurationBuilder; import org.infinispan.lifecycle.ComponentStatus; import org.infinispan.manager.DefaultCacheManager; import org.infinispan.manager.EmbeddedCacheManager; import org.infinispan.remoting.inboundhandler.DeliverOrder; import org.infinispan.remoting.inboundhandler.InboundInvocationHandler; import org.infinispan.remoting.inboundhandler.Reply; import org.infinispan.remoting.transport.Address; import org.infinispan.test.MultipleCacheManagersTest; import org.infinispan.test.fwk.CheckPoint; import org.infinispan.test.fwk.TestCacheManagerFactory; import org.infinispan.test.fwk.TestResourceTracker; import org.infinispan.test.fwk.TransportFlags; import org.infinispan.topology.CacheTopologyControlCommand; import org.infinispan.util.logging.Log; import org.infinispan.util.logging.LogFactory; import org.infinispan.xsite.XSiteReplicateCommand; import org.testng.annotations.Test; /** * Tests concurrent startup of replicated and distributed caches * * @author Dan Berindei * @since 7.2 */ @Test(testName = "statetransfer.ConcurrentStartTest", groups = "functional") public class ConcurrentStartTest extends MultipleCacheManagersTest { public static final String REPL_CACHE_NAME = "repl"; public static final String DIST_CACHE_NAME = "dist"; @Override protected void createCacheManagers() throws Throwable { // The test method will create the cache managers } @Test(timeOut = 60000) public void testConcurrentStart() throws Exception { TestResourceTracker.testThreadStarted(this); final CheckPoint checkPoint = new CheckPoint(); EmbeddedCacheManager cm1 = createCacheManager(0); EmbeddedCacheManager cm2 = createCacheManager(1); // Install the blocking invocation handlers assertEquals(ComponentStatus.INSTANTIATED, extractGlobalComponentRegistry(cm1).getStatus()); replaceInboundInvocationHandler(cm1, checkPoint, 0); assertEquals(ComponentStatus.INSTANTIATED, extractGlobalComponentRegistry(cm2).getStatus()); replaceInboundInvocationHandler(cm2, checkPoint, 1); log.debugf("Cache managers created. Starting the caches"); Future<Object> repl1Future = fork(new CacheStartCallable(cm1, "repl")); Future<Object> repl2Future = fork(new CacheStartCallable(cm2, "repl")); Future<Object> dist1Future = fork(new CacheStartCallable(cm1, "dist")); Future<Object> dist2Future = fork(new CacheStartCallable(cm2, "dist")); // The joiner always sends a POLICY_GET_STATUS command to the coordinator. // The coordinator may or may not send a GET_STATUS command to the other node, // depending on whether the second node joined the cluster quickly enough. // Wait for at least one of the POLICY_GET_STATUS/GET_STATUS commands to block checkPoint.peek(2, SECONDS, "blocked_0", "blocked_1"); // Now allow both to continue. checkPoint.trigger("unblocked_0", CheckPoint.INFINITE); checkPoint.trigger("unblocked_1", CheckPoint.INFINITE); repl1Future.get(10, SECONDS); repl2Future.get(10, SECONDS); dist1Future.get(10, SECONDS); dist2Future.get(10, SECONDS); Cache<String, String> c1r = cm1.getCache("repl"); Cache<String, String> c1d = cm1.getCache("dist"); Cache<String, String> c2r = cm2.getCache("repl"); Cache<String, String> c2d = cm2.getCache("dist"); blockUntilViewsReceived(10000, cm1, cm2); waitForNoRebalance(c1r, c2r); waitForNoRebalance(c1d, c2d); c1r.put("key", "value"); assertEquals("value", c2r.get("key")); c1d.put("key", "value"); assertEquals("value", c2d.get("key")); } private EmbeddedCacheManager createCacheManager(int index) { GlobalConfigurationBuilder gcb = new GlobalConfigurationBuilder(); gcb.globalJmxStatistics().disable().allowDuplicateDomains(true); gcb.transport().defaultTransport(); TestCacheManagerFactory.amendGlobalConfiguration(gcb, new TransportFlags().withPortRange(index)); EmbeddedCacheManager cm = new DefaultCacheManager(gcb.build(), false); registerCacheManager(cm); Configuration replCfg = new ConfigurationBuilder().clustering().cacheMode(CacheMode.REPL_SYNC).build(); cm.defineConfiguration(REPL_CACHE_NAME, replCfg); Configuration distCfg = new ConfigurationBuilder().clustering().cacheMode(CacheMode.DIST_SYNC).build(); cm.defineConfiguration(DIST_CACHE_NAME, distCfg); return cm; } private void replaceInboundInvocationHandler(EmbeddedCacheManager cm, CheckPoint checkPoint, int index) { InboundInvocationHandler handler = extractGlobalComponent(cm, InboundInvocationHandler.class); BlockingInboundInvocationHandler ourHandler = new BlockingInboundInvocationHandler(handler, checkPoint, index); replaceComponent(cm, InboundInvocationHandler.class, ourHandler, true); } private static class CacheStartCallable implements Callable<Object> { private final EmbeddedCacheManager cm; private final String cacheName; public CacheStartCallable(EmbeddedCacheManager cm, String cacheName) { this.cm = cm; this.cacheName = cacheName; } @Override public Object call() throws Exception { cm.getCache(cacheName); return null; } } private static class BlockingInboundInvocationHandler implements InboundInvocationHandler { private Log log = LogFactory.getLog(ConcurrentStartTest.class); private final CheckPoint checkPoint; private final InboundInvocationHandler delegate; private final int index; public BlockingInboundInvocationHandler(InboundInvocationHandler delegate, CheckPoint checkPoint, int index) { this.delegate = delegate; this.checkPoint = checkPoint; this.index = index; } @Override public void handleFromCluster(Address origin, ReplicableCommand command, Reply reply, DeliverOrder order) { if (command instanceof CacheTopologyControlCommand) { try { checkPoint.trigger("blocked_" + index); checkPoint.awaitStrict("unblocked_" + index, 10, SECONDS); } catch (Exception e) { log.warnf(e, "Error while blocking before command %s", command); } } delegate.handleFromCluster(origin, command, reply, order); } @Override public void handleFromRemoteSite(String origin, XSiteReplicateCommand command, Reply reply, DeliverOrder order) { delegate.handleFromRemoteSite(origin, command, reply, order); } } }