package org.infinispan.tx; import static org.testng.AssertJUnit.assertEquals; import java.util.HashMap; import java.util.Map; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import org.infinispan.commands.tx.VersionedCommitCommand; import org.infinispan.configuration.cache.CacheMode; import org.infinispan.configuration.cache.ConfigurationBuilder; import org.infinispan.remoting.transport.Address; import org.infinispan.test.MultipleCacheManagersTest; import org.infinispan.test.TestingUtil; import org.infinispan.test.fwk.CleanupAfterMethod; import org.infinispan.transaction.impl.TransactionTable; import org.infinispan.transaction.lookup.EmbeddedTransactionManagerLookup; import org.infinispan.transaction.tm.EmbeddedTransaction; import org.infinispan.transaction.tm.EmbeddedTransactionManager; import org.infinispan.util.ControlledConsistentHashFactory; import org.infinispan.util.mocks.ControlledCommandFactory; import org.testng.annotations.Test; /** * Test for https://issues.jboss.org/browse/ISPN-2383 */ @CleanupAfterMethod @Test(groups = "functional", testName = "tx.TxCleanupServiceTest") public class TxCleanupServiceTest extends MultipleCacheManagersTest { private static final int TX_COUNT = 1; private ConfigurationBuilder dcc; private ControlledConsistentHashFactory consistentHashFactory; @Override protected void createCacheManagers() throws Throwable { dcc = getDefaultClusteredCacheConfig(CacheMode.DIST_SYNC, true); dcc.transaction().transactionManagerLookup(new EmbeddedTransactionManagerLookup()); consistentHashFactory = new ControlledConsistentHashFactory(1); dcc.clustering().hash().numOwners(1).numSegments(1).consistentHashFactory(consistentHashFactory); createCluster(dcc, 2); waitForClusterToForm(); } public void testTransactionStateNotLost() throws Throwable { final ControlledCommandFactory ccf = ControlledCommandFactory.registerControlledCommandFactory(cache(1), VersionedCommitCommand.class); ccf.gate.close(); final Map<Object, EmbeddedTransaction> keys2Tx = new HashMap<>(TX_COUNT); int viewId = advancedCache(0).getRpcManager().getTransport().getViewId(); log.tracef("ViewId before %s", viewId); //fork it into another thread as this is going to block in commit Future<Object> future = fork(() -> { for (int i = 0; i < TX_COUNT; i++) { Object k = getKeyForCache(1); tm(0).begin(); cache(0).put(k, k); EmbeddedTransaction transaction = ((EmbeddedTransactionManager) tm(0)).getTransaction(); keys2Tx.put(k, transaction); tm(0).commit(); } return null; }); //now wait for all the commits to block eventuallyEquals(TX_COUNT, ccf.blockTypeCommandsReceived::get); log.tracef("Viewid middle %s", viewId); //now add a one new member consistentHashFactory.setOwnerIndexes(2); addClusterEnabledCacheManager(dcc); waitForClusterToForm(); viewId = advancedCache(0).getRpcManager().getTransport().getViewId(); log.tracef("Viewid after before %s", viewId); final Map<Object, EmbeddedTransaction> migratedTx = new HashMap<>(TX_COUNT); for (Object key : keys2Tx.keySet()) { if (keyMapsToNode2(key)) { migratedTx.put(key, keys2Tx.get(key)); } } log.tracef("Number of migrated tx is %s", migratedTx.size()); assertEquals(TX_COUNT, migratedTx.size()); eventuallyEquals(migratedTx.size(), () -> TestingUtil.getTransactionTable(cache(2)).getRemoteTxCount()); log.trace("Releasing the gate"); ccf.gate.open(); future.get(10, TimeUnit.SECONDS); eventuallyEquals(0, () -> TestingUtil.getTransactionTable(cache(2)).getRemoteTxCount()); eventually(() -> { boolean allZero = true; for (int i = 0; i < 3; i++) { TransactionTable tt = TestingUtil.getTransactionTable(cache(i)); // assertEquals("For cache " + i, 0, tt.getLocalTxCount()); // assertEquals("For cache " + i, 0, tt.getRemoteTxCount()); int local = tt.getLocalTxCount(); int remote = tt.getRemoteTxCount(); log.tracef("For cache %d, localTxCount=%s, remoteTxCount=%s", i, local, remote); log.tracef(String.format("For cache %s , localTxCount=%s, remoteTxCount=%s", i, local, remote)); allZero = allZero && (local == 0); allZero = allZero && (remote == 0); } return allZero; }); for (Object key : keys2Tx.keySet()) { assertNotLocked(key); assertEquals(key, cache(0).get(key)); } } private boolean keyMapsToNode2(Object key) { Address owner = owner(key); return owner.equals(address(2)); } private Address owner(Object key) { return advancedCache(0).getDistributionManager().getCacheTopology().getDistribution(key).primary(); } }