package org.infinispan.persistence;
import static org.testng.AssertJUnit.assertEquals;
import static org.testng.AssertJUnit.assertFalse;
import static org.testng.AssertJUnit.assertNotNull;
import static org.testng.AssertJUnit.assertNull;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.infinispan.commands.write.EvictCommand;
import org.infinispan.configuration.cache.ConfigurationBuilder;
import org.infinispan.container.DataContainer;
import org.infinispan.context.InvocationContext;
import org.infinispan.interceptors.base.CommandInterceptor;
import org.infinispan.interceptors.impl.CacheLoaderInterceptor;
import org.infinispan.manager.EmbeddedCacheManager;
import org.infinispan.marshall.core.MarshalledEntry;
import org.infinispan.persistence.dummy.DummyInMemoryStoreConfigurationBuilder;
import org.infinispan.persistence.spi.CacheLoader;
import org.infinispan.test.SingleCacheManagerTest;
import org.infinispan.test.TestingUtil;
import org.infinispan.test.fwk.TestCacheManagerFactory;
import org.infinispan.transaction.TransactionMode;
import org.testng.annotations.Test;
/**
* Tests an evict command going past the entry wrapping interceptor while the entry is in the store,
* then another thread rushing ahead and activating the entry in memory before the evict command commits.
*
* @author Dan Berindei
*/
@Test(groups = "functional", testName = "persistence.LoadDuringEvictTest")
public class ActivationDuringEvictTest extends SingleCacheManagerTest {
public static final String KEY = "a";
public static final String VALUE = "b";
SlowDownInterceptor sdi;
@Override
protected EmbeddedCacheManager createCacheManager() throws Exception {
sdi = new SlowDownInterceptor();
ConfigurationBuilder config = new ConfigurationBuilder();
config
.persistence()
.passivation(true)
.addStore(DummyInMemoryStoreConfigurationBuilder.class)
.customInterceptors()
.addInterceptor()
.interceptor(sdi).after(CacheLoaderInterceptor.class)
.transaction().transactionMode(TransactionMode.NON_TRANSACTIONAL);
return TestCacheManagerFactory.createCacheManager(config);
}
public void testActivationDuringEvict() throws Exception {
DataContainer dc = cache.getAdvancedCache().getDataContainer();
CacheLoader cl = TestingUtil.getCacheLoader(cache);
cache.put(KEY, VALUE);
assertEquals(VALUE, cache.get(KEY));
MarshalledEntry se = cl.load(KEY);
assertNull(se);
// passivate the entry
cache.evict(KEY);
assertPassivated(dc, cl, KEY, VALUE);
// start blocking evict commands
sdi.enabled = true;
// call the evict()
Future<?> future = fork(() -> {
log.info("before the evict");
cache.evict(KEY);
log.info("after the evict");
});
// wait for the SlowDownInterceptor to intercept the evict
if (!sdi.evictLatch.await(10, TimeUnit.SECONDS))
throw new org.infinispan.util.concurrent.TimeoutException();
log.info("doing the get");
Object value = cache.get(KEY);
// make sure the get call, which would have gone past the cache loader interceptor first, gets the correct value.
assertEquals(VALUE, value);
sdi.getLatch.countDown();
future.get(10, TimeUnit.SECONDS);
// disable the SlowDownInterceptor
sdi.enabled = false;
// and check that the key actually has been evicted
assertPassivated(dc, cl, KEY, VALUE);
}
private void assertPassivated(DataContainer dc, CacheLoader cl, String key, String expected) {
MarshalledEntry se;
assertFalse(dc.containsKey(key));
se = cl.load(key);
assertNotNull(se);
assertEquals(expected, se.getValue());
}
private static class SlowDownInterceptor extends CommandInterceptor implements Cloneable{
private static final long serialVersionUID = 8790944676490291484L;
volatile boolean enabled = false;
CountDownLatch getLatch = new CountDownLatch(1);
CountDownLatch evictLatch = new CountDownLatch(1);
@Override
public Object visitEvictCommand(InvocationContext ctx, EvictCommand command) throws Throwable {
if (enabled) {
evictLatch.countDown();
getLog().trace("Wait for get to finish...");
if (!getLatch.await(10, TimeUnit.SECONDS))
throw new TimeoutException("Didn't see evict!");
}
return invokeNextInterceptor(ctx, command);
}
}
}