package org.infinispan.expiration.impl;
import static org.testng.AssertJUnit.assertEquals;
import static org.testng.AssertJUnit.assertNull;
import java.util.concurrent.TimeUnit;
import org.infinispan.Cache;
import org.infinispan.configuration.cache.CacheMode;
import org.infinispan.configuration.cache.ConfigurationBuilder;
import org.infinispan.distribution.MagicKey;
import org.infinispan.test.MultipleCacheManagersTest;
import org.infinispan.test.TestingUtil;
import org.infinispan.test.fwk.InCacheMode;
import org.infinispan.util.ControlledTimeService;
import org.infinispan.util.TimeService;
import org.infinispan.util.logging.Log;
import org.infinispan.util.logging.LogFactory;
import org.testng.annotations.Test;
/**
* Tests to make sure that when expiration occurs it occurs across the cluster
*
* @author William Burns
* @since 8.0
*/
@Test(groups = "functional", testName = "expiration.impl.ClusterExpirationFunctionalTest")
@InCacheMode({CacheMode.DIST_SYNC, CacheMode.REPL_SYNC})
public class ClusterExpirationFunctionalTest extends MultipleCacheManagersTest {
protected final Log log = LogFactory.getLog(getClass());
protected ControlledTimeService ts0;
protected ControlledTimeService ts1;
protected ControlledTimeService ts2;
protected Cache<Object, String> cache0;
protected Cache<Object, String> cache1;
protected Cache<Object, String> cache2;
@Override
protected void createCacheManagers() throws Throwable {
ConfigurationBuilder builder = new ConfigurationBuilder();
builder.clustering().cacheMode(cacheMode);
createCluster(builder, 3);
waitForClusterToForm();
injectTimeServices();
cache0 = cache(0);
cache1 = cache(1);
cache2 = cache(2);
}
protected void injectTimeServices() {
long now = System.currentTimeMillis();
ts0 = new ControlledTimeService(now);
TestingUtil.replaceComponent(manager(0), TimeService.class, ts0, true);
ts1 = new ControlledTimeService(now);
TestingUtil.replaceComponent(manager(1), TimeService.class, ts1, true);
ts2 = new ControlledTimeService(now);
TestingUtil.replaceComponent(manager(2), TimeService.class, ts2, true);
}
public void testExpiredOnPrimaryOwner() throws Exception {
testExpiredEntryRetrieval(cache0, cache1, ts0, true);
}
public void testExpiredOnBackupOwner() throws Exception {
testExpiredEntryRetrieval(cache0, cache1, ts1, false);
}
private void testExpiredEntryRetrieval(Cache<Object, String> primaryOwner, Cache<Object, String> backupOwner,
ControlledTimeService timeService, boolean expireOnPrimary) throws Exception {
MagicKey key = new MagicKey(primaryOwner, backupOwner);
primaryOwner.put(key, key.toString(), 10, TimeUnit.MINUTES);
assertEquals(key.toString(), primaryOwner.get(key));
assertEquals(key.toString(), backupOwner.get(key));
// Now we expire on cache0, it should still exist on cache1
timeService.advance(TimeUnit.MINUTES.toMillis(10) + 1);
Cache<?, ?> expiredCache;
Cache<?, ?> otherCache;
if (expireOnPrimary) {
expiredCache = primaryOwner;
otherCache = backupOwner;
} else {
expiredCache = backupOwner;
otherCache = primaryOwner;
}
assertEquals(key.toString(), otherCache.get(key));
// By calling get on an expired key it will remove it all over
Object expiredValue = expiredCache.get(key);
assertNull(expiredValue);
// This should be expired on the other node soon - note expiration is done asynchronously on a get
eventually(() -> !otherCache.containsKey(key), 10, TimeUnit.SECONDS);
}
public void testExpiredOnBoth() {
MagicKey key = new MagicKey(cache0, cache1);
cache0.put(key, key.toString(), 10, TimeUnit.MINUTES);
assertEquals(key.toString(), cache0.get(key));
assertEquals(key.toString(), cache1.get(key));
// Now we expire on cache0, it should still exist on cache1
ts0.advance(TimeUnit.MINUTES.toMillis(10) + 1);
ts1.advance(TimeUnit.MINUTES.toMillis(10) + 1);
// Both should be null
assertNull(cache0.get(key));
assertNull(cache1.get(key));
}
}