package org.infinispan.statetransfer; import static org.testng.AssertJUnit.assertEquals; import java.util.Arrays; import javax.transaction.Transaction; import org.infinispan.Cache; import org.infinispan.configuration.cache.CacheMode; import org.infinispan.configuration.cache.ConfigurationBuilder; import org.infinispan.test.MultipleCacheManagersTest; import org.infinispan.test.TestingUtil; import org.infinispan.topology.LocalTopologyManager; import org.infinispan.transaction.LockingMode; import org.infinispan.transaction.impl.TransactionTable; import org.infinispan.util.ReplicatedControlledConsistentHashFactory; import org.infinispan.util.logging.Log; import org.infinispan.util.logging.LogFactory; import org.testng.annotations.Test; @Test(groups = "functional", testName = "statetransfer.OrphanTransactionsCleanupTest") public class OrphanTransactionsCleanupTest extends MultipleCacheManagersTest { private static final Log log = LogFactory.getLog(OrphanTransactionsCleanupTest.class); protected ConfigurationBuilder configurationBuilder; public OrphanTransactionsCleanupTest() { cleanup = CleanupPhase.AFTER_METHOD; } @Override protected void createCacheManagers() throws Throwable { configurationBuilder = getDefaultClusteredCacheConfig(CacheMode.REPL_SYNC, true); configurationBuilder.transaction().lockingMode(LockingMode.PESSIMISTIC); // Make the coordinator the primary owner of the only segment configurationBuilder.clustering().hash().numSegments(1).consistentHashFactory(new ReplicatedControlledConsistentHashFactory(0)); configurationBuilder.clustering().stateTransfer().awaitInitialTransfer(false); addClusterEnabledCacheManager(configurationBuilder); addClusterEnabledCacheManager(configurationBuilder); waitForClusterToForm(); } public void testJoinerTransactionSurvives() throws Exception { Cache<Object, Object> c0 = manager(0).getCache(); Cache<Object, Object> c1 = manager(1).getCache(); final TransactionTable tt0 = TestingUtil.extractComponent(c0, TransactionTable.class); // Disable rebalancing so that the joiner is not included in the CH LocalTopologyManager ltm0 = TestingUtil.extractGlobalComponent(manager(0), LocalTopologyManager.class); ltm0.setRebalancingEnabled(false); // Add a new node addClusterEnabledCacheManager(configurationBuilder); Cache<Object, Object> c2 = manager(2).getCache(); // Start a transaction from c2, but don't commit yet tm(2).begin(); c2.put("key1", "value1"); assertEquals(1, tt0.getRemoteGlobalTransaction().size()); Transaction tx2 = tm(2).suspend(); // Start another transaction from c1, also without committing it tm(1).begin(); c1.put("key2", "value2"); assertEquals(2, tt0.getRemoteGlobalTransaction().size()); Transaction tx1 = tm(1).suspend(); // Kill node 1 to trigger the orphan transaction cleanup manager(1).stop(); TestingUtil.blockUntilViewsReceived(60000, false, c0, c2); // Cache 2 should not be in the CH yet TestingUtil.waitForNoRebalance(c0); assertEquals(Arrays.asList(address(0)), c0.getAdvancedCache().getDistributionManager().getWriteConsistentHash().getMembers()); eventuallyEquals(1, () -> tt0.getRemoteTransactions().size()); // Committing the tx on c2 should succeed tm(2).resume(tx2); tm(2).commit(); } }