package org.infinispan.stats; import static org.infinispan.distribution.DistributionTestHelper.addressOf; import static org.infinispan.test.TestingUtil.k; import java.lang.reflect.Method; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import javax.transaction.NotSupportedException; import javax.transaction.SystemException; import javax.transaction.Transaction; import org.infinispan.Cache; import org.infinispan.configuration.cache.CacheMode; import org.infinispan.configuration.cache.ConfigurationBuilder; import org.infinispan.distribution.DistributionManager; import org.infinispan.interceptors.impl.TxInterceptor; import org.infinispan.stats.topK.CacheUsageInterceptor; import org.infinispan.test.TestingUtil; import org.infinispan.test.fwk.CleanupAfterTest; import org.infinispan.util.concurrent.IsolationLevel; import org.testng.Assert; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; /** * @author Pedro Ruivo * @since 6.0 */ @Test(groups = "functional") @CleanupAfterTest public abstract class BaseClusterTopKeyTest extends AbstractTopKeyTest { private final CacheMode cacheMode; private final int clusterSize; protected BaseClusterTopKeyTest(CacheMode cacheMode, int clusterSize) { this.cacheMode = cacheMode; this.clusterSize = clusterSize; } @BeforeMethod(alwaysRun = true) public void resetBeforeTest() { caches().forEach(cache -> getTopKey(cache).resetStatistics()); } public void testPut(Method method) { final String key1 = k(method, 1); final String key2 = k(method, 2); cache(0).put(key1, "value1"); cache(0).put(key2, "value2"); assertNoLocks(key1); assertNoLocks(key2); cache(1).put(key1, "value3"); cache(1).put(key2, "value4"); assertTopKeyAccesses(cache(0), key1, 1, false); assertTopKeyAccesses(cache(0), key2, 1, false); assertTopKeyAccesses(cache(0), key1, 0, true); assertTopKeyAccesses(cache(0), key2, 0, true); assertTopKeyAccesses(cache(1), key1, 1, false); assertTopKeyAccesses(cache(1), key2, 1, false); assertTopKeyAccesses(cache(1), key1, 0, true); assertTopKeyAccesses(cache(1), key2, 0, true); if (isPrimaryOwner(cache(0), key1)) { assertLockInformation(cache(0), key1, 2, 0, 0); assertLockInformation(cache(1), key1, 0, 0, 0); } else { assertLockInformation(cache(0), key1, 0, 0, 0); assertLockInformation(cache(1), key1, 2, 0, 0); } if (isPrimaryOwner(cache(0), key2)) { assertLockInformation(cache(0), key2, 2, 0, 0); assertLockInformation(cache(1), key2, 0, 0, 0); } else { assertLockInformation(cache(0), key2, 0, 0, 0); assertLockInformation(cache(1), key2, 2, 0, 0); } assertWriteSkew(cache(0), key1, 0); assertWriteSkew(cache(0), key2, 0); assertWriteSkew(cache(1), key1, 0); assertWriteSkew(cache(1), key2, 0); } public void testGet(Method method) { final String key1 = k(method, 1); final String key2 = k(method, 2); cache(0).get(key1); cache(0).get(key2); cache(1).get(key1); cache(1).get(key2); assertTopKeyAccesses(cache(0), key1, 0, false); assertTopKeyAccesses(cache(0), key2, 0, false); assertTopKeyAccesses(cache(0), key1, 1, true); assertTopKeyAccesses(cache(0), key2, 1, true); assertTopKeyAccesses(cache(1), key1, 0, false); assertTopKeyAccesses(cache(1), key2, 0, false); assertTopKeyAccesses(cache(1), key1, 1, true); assertTopKeyAccesses(cache(1), key2, 1, true); assertLockInformation(cache(0), key1, 0, 0, 0); assertLockInformation(cache(0), key2, 0, 0, 0); assertLockInformation(cache(1), key1, 0, 0, 0); assertLockInformation(cache(1), key2, 0, 0, 0); assertWriteSkew(cache(0), key1, 0); assertWriteSkew(cache(0), key2, 0); assertWriteSkew(cache(1), key1, 0); assertWriteSkew(cache(1), key2, 0); } public void testLockFailed(Method method) throws InterruptedException, TimeoutException, ExecutionException { final String key = k(method, 0); Cache<Object, Object> primary; Cache<Object, Object> nonPrimary; if (isPrimaryOwner(cache(0), key)) { primary = cache(0); nonPrimary = cache(1); } else { primary = cache(1); nonPrimary = cache(0); } PrepareCommandBlocker blocker = addPrepareBlockerIfAbsent(primary); blocker.reset(); Future<Void> f = fork(() -> { nonPrimary.put(key, "value"); return null; }); blocker.awaitUntilPrepareBlocked(); //at this point, the key is locked... try { primary.put(key, "value"); Assert.fail("The key should be locked!"); } catch (Throwable t) { //expected } blocker.unblock(); f.get(30, TimeUnit.SECONDS); assertTopKeyAccesses(cache(0), key, 1, false); assertTopKeyAccesses(cache(0), key, 0, true); assertTopKeyAccesses(cache(1), key, 1, false); assertTopKeyAccesses(cache(1), key, 0, true); assertLockInformation(primary, key, 2, 1, 1); assertLockInformation(nonPrimary, key, 0, 0, 0); assertWriteSkew(cache(0), key, 0); assertWriteSkew(cache(1), key, 0); } public void testWriteSkew(Method method) throws InterruptedException, SystemException, NotSupportedException { final String key = k(method, 0); Cache<Object, Object> primary; Cache<Object, Object> nonPrimary; if (isPrimaryOwner(cache(0), key)) { primary = cache(0); nonPrimary = cache(1); } else { primary = cache(1); nonPrimary = cache(0); } tm(primary).begin(); primary.put(key, "value"); Transaction transaction = tm(primary).suspend(); primary.put(key, "value"); try { tm(primary).resume(transaction); tm(primary).commit(); Assert.fail("The write skew should be detected"); } catch (Exception t) { //expected } assertTopKeyAccesses(primary, key, 2, false); assertTopKeyAccesses(primary, key, 0, true); assertTopKeyAccesses(nonPrimary, key, 0, false); assertTopKeyAccesses(nonPrimary, key, 0, true); assertLockInformation(primary, key, 2, 0, 0); assertLockInformation(nonPrimary, key, 0, 0, 0); assertWriteSkew(primary, key, 1); assertWriteSkew(nonPrimary, key, 0); } @Override protected void createCacheManagers() throws Throwable { for (int i = 0; i < clusterSize; ++i) { ConfigurationBuilder builder = getDefaultClusteredCacheConfig(cacheMode, true); builder.customInterceptors().addInterceptor() .before(TxInterceptor.class) .interceptor(new CacheUsageInterceptor()); builder.locking().isolationLevel(IsolationLevel.REPEATABLE_READ) .lockAcquisitionTimeout(TestingUtil.shortTimeoutMillis()); addClusterEnabledCacheManager(builder); } waitForClusterToForm(); } protected boolean isPrimaryOwner(Cache<?, ?> cache, Object key) { DistributionManager dm = cache.getAdvancedCache().getDistributionManager(); return dm.getPrimaryLocation(key).equals(addressOf(cache)); } private void assertNoLocks(String key) { for (Cache<?, ?> cache : caches()) { assertEventuallyNotLocked(cache, key); } } }