package org.infinispan.statetransfer; import static java.util.concurrent.TimeUnit.SECONDS; import static org.infinispan.test.TestingUtil.blockUntilViewsReceived; import static org.infinispan.test.TestingUtil.extractGlobalComponentRegistry; import static org.infinispan.test.TestingUtil.waitForNoRebalance; import static org.testng.AssertJUnit.assertEquals; import java.io.ByteArrayInputStream; import java.util.concurrent.Future; import org.infinispan.Cache; import org.infinispan.configuration.cache.CacheMode; 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.test.MultipleCacheManagersTest; import org.infinispan.test.fwk.CleanupAfterMethod; import org.infinispan.test.fwk.JGroupsConfigBuilder; import org.infinispan.test.fwk.TestResourceTracker; import org.infinispan.test.fwk.TransportFlags; import org.jgroups.JChannel; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; /** * Tests concurrent startup of cache managers when the channel is started externally * and injected with a JGroupsChannelLookup. * * @author Dan Berindei * @since 8.2 */ @Test(testName = "statetransfer.ConcurrentStartChanelLookupTest", groups = "functional") @CleanupAfterMethod public class ConcurrentStartChanelLookupTest extends MultipleCacheManagersTest { public static final String CACHE_NAME = "repl"; @Override protected void createCacheManagers() throws Throwable { // The test method will create the cache managers } @DataProvider(name = "startOrder") public Object[][] startOrder() { return new Object[][]{{0, 1}, {1, 0}}; } @Test(timeOut = 60000, dataProvider = "startOrder") public void testConcurrentStart(int eagerManager, int lazyManager) throws Exception { TestResourceTracker.testThreadStarted(this); String name1 = TestResourceTracker.getNextNodeName(); String name2 = TestResourceTracker.getNextNodeName(); // Create and connect both channels beforehand // We need both nodes in the view when the coordinator's ClusterTopologyManagerImpl starts // in order to reproduce the ISPN-5106 deadlock JChannel ch1 = createChannel(name1, 0); JChannel ch2 = createChannel(name2, 1); // Use a JGroupsChannelLookup to pass the created channels to the transport EmbeddedCacheManager cm1 = createCacheManager(name1, ch1); EmbeddedCacheManager cm2 = createCacheManager(name2, ch2); try { assertEquals(ComponentStatus.INSTANTIATED, extractGlobalComponentRegistry(cm1).getStatus()); assertEquals(ComponentStatus.INSTANTIATED, extractGlobalComponentRegistry(cm2).getStatus()); log.debugf("Channels created. Starting the caches"); Future<Object> repl1Future = fork(() -> manager(eagerManager).getCache(CACHE_NAME)); // If eagerManager == 0, the coordinator broadcasts a GET_STATUS command. // If eagerManager == 1, the non-coordinator sends a POLICY_GET_STATUS command to the coordinator. // We want to start the lazyManager without receiving these commands, then the eagerManager should // retry and succeed. // Bundling and retransmission in NAKACK2 mean we need an extra wait after lazyManager sent its // command, so we don't try to wait for a precise amount of time. Thread.sleep(500); Future<Object> repl2Future = fork(() -> manager(lazyManager).getCache(CACHE_NAME)); repl1Future.get(10, SECONDS); repl2Future.get(10, SECONDS); Cache<String, String> c1r = cm1.getCache(CACHE_NAME); Cache<String, String> c2r = cm2.getCache(CACHE_NAME); blockUntilViewsReceived(10000, cm1, cm2); waitForNoRebalance(c1r, c2r); c1r.put("key", "value"); assertEquals("value", c2r.get("key")); } finally { cm1.stop(); cm2.stop(); ch1.close(); ch2.close(); } } private EmbeddedCacheManager createCacheManager(String name1, JChannel ch1) { GlobalConfigurationBuilder gcb1 = new GlobalConfigurationBuilder(); gcb1.transport().nodeName(ch1.getName()).distributedSyncTimeout(10, SECONDS); gcb1.globalJmxStatistics().allowDuplicateDomains(true); CustomChannelLookup.registerChannel(gcb1, ch1, name1, false); ConfigurationBuilder replCfg = new ConfigurationBuilder(); replCfg.clustering().cacheMode(CacheMode.REPL_SYNC); replCfg.clustering().stateTransfer().timeout(10, SECONDS); EmbeddedCacheManager cm1 = new DefaultCacheManager(gcb1.build(), replCfg.build(), false); registerCacheManager(cm1); cm1.defineConfiguration(CACHE_NAME, replCfg.build()); return cm1; } private JChannel createChannel(String name, int portRange) throws Exception { String configString = JGroupsConfigBuilder .getJGroupsConfig(ConcurrentStartChanelLookupTest.class.getName(), new TransportFlags().withPortRange(portRange)); JChannel channel = new JChannel(new ByteArrayInputStream(configString.getBytes())); channel.setName(name); channel.connect(ConcurrentStartChanelLookupTest.class.getSimpleName()); log.tracef("Channel %s connected: %s", channel, channel.getViewAsString()); return channel; } }