package org.infinispan.distribution.groups;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicReference;
import org.infinispan.Cache;
import org.infinispan.commands.remote.GetKeysInGroupCommand;
import org.infinispan.configuration.cache.CacheMode;
import org.infinispan.configuration.cache.ConfigurationBuilder;
import org.infinispan.container.DataContainer;
import org.infinispan.container.entries.InternalCacheEntry;
import org.infinispan.context.Flag;
import org.infinispan.context.InvocationContext;
import org.infinispan.filter.KeyFilter;
import org.infinispan.interceptors.AsyncInterceptorChain;
import org.infinispan.interceptors.base.CommandInterceptor;
import org.infinispan.interceptors.impl.EntryWrappingInterceptor;
import org.infinispan.marshall.core.MarshalledEntry;
import org.infinispan.persistence.dummy.DummyInMemoryStoreConfigurationBuilder;
import org.infinispan.persistence.manager.PersistenceManager;
import org.infinispan.persistence.spi.AdvancedCacheLoader;
import org.infinispan.test.TestingUtil;
import org.infinispan.test.fwk.CheckPoint;
import org.infinispan.util.logging.Log;
import org.testng.AssertJUnit;
import org.testng.annotations.Test;
/**
* It tests the grouping advanced interface.
*
* @author Pedro Ruivo
* @since 7.0
*/
@Test(groups = "functional", testName = "distribution.groups.GetGroupKeysTest")
public class GetGroupKeysTest extends BaseUtilGroupTest {
protected static final String PERSISTENCE_CACHE = "persistence-cache";
protected static final String PERSISTENCE_PASSIVATION_CACHE = "persistence-passivation-cache";
protected final boolean transactional;
@Override
public Object[] factory() {
return new Object[] {
new GetGroupKeysTest(false, TestCacheFactory.PRIMARY_OWNER),
new GetGroupKeysTest(false, TestCacheFactory.BACKUP_OWNER),
new GetGroupKeysTest(false, TestCacheFactory.NON_OWNER),
};
}
public GetGroupKeysTest() {
this(false, null);
}
protected GetGroupKeysTest(boolean transactional, TestCacheFactory factory) {
super(factory);
this.transactional = transactional;
}
public void testGetKeysInGroup() {
final TestCache testCache = createTestCacheAndReset(GROUP, this.caches());
initCache(testCache.primaryOwner);
Map<GroupKey, String> groupKeySet = testCache.testCache.getGroup(GROUP);
Map<GroupKey, String> expectedGroupSet = createMap(0, 10);
AssertJUnit.assertEquals(expectedGroupSet, groupKeySet);
}
public void testGetKeysInGroupWithPersistence() {
final TestCache testCache = createTestCacheAndReset(GROUP, this.caches(PERSISTENCE_CACHE));
initCache(testCache.primaryOwner);
Map<GroupKey, String> groupKeySet = testCache.testCache.getGroup(GROUP);
Map<GroupKey, String> expectedGroupSet = createMap(0, 10);
AssertJUnit.assertEquals(expectedGroupSet, groupKeySet);
}
public void testGetKeysInGroupWithPersistenceAndPassivation() {
final TestCache testCache = createTestCacheAndReset(GROUP, this.caches(PERSISTENCE_PASSIVATION_CACHE));
initCache(testCache.primaryOwner);
Map<GroupKey, String> groupKeySet = testCache.testCache.getGroup(GROUP);
Map<GroupKey, String> expectedGroupSet = createMap(0, 10);
AssertJUnit.assertEquals(expectedGroupSet, groupKeySet);
}
public void testGetKeysInGroupWithPersistenceAndSkipCacheLoader() {
final TestCache testCache = createTestCacheAndReset(GROUP, this.caches(PERSISTENCE_CACHE));
initCache(testCache.primaryOwner);
Map<GroupKey, String> groupKeySet = testCache.testCache.withFlags(Flag.SKIP_CACHE_LOAD).getGroup(GROUP);
//noinspection unchecked
Map<GroupKey, String> expectedGroupSet = new HashMap<>();
//noinspection unchecked
for (InternalCacheEntry<GroupKey, String> entry :
(DataContainer<GroupKey, String>) TestingUtil.extractComponent(extractTargetCache(testCache), DataContainer.class)) {
if (entry.getKey().getGroup().equals(GROUP)) {
expectedGroupSet.put(entry.getKey(), entry.getValue());
}
}
AssertJUnit.assertEquals(expectedGroupSet, groupKeySet);
}
public void testGetKeyInGroupWithConcurrentActivation() throws TimeoutException, InterruptedException, ExecutionException {
final TestCache testCache = createTestCacheAndReset(GROUP, this.caches(PERSISTENCE_PASSIVATION_CACHE));
initCache(testCache.primaryOwner);
final BlockCommandInterceptor interceptor = injectIfAbsent(extractTargetCache(testCache));
interceptor.open = false;
Future<Map<GroupKey, String>> future = fork(new Callable<Map<GroupKey, String>>() {
@Override
public Map<GroupKey, String> call() throws Exception {
return testCache.testCache.getGroup(GROUP);
}
});
interceptor.awaitCommandBlock();
final AtomicReference<GroupKey> keyToActivate = new AtomicReference<>(null);
PersistenceManager persistenceManager = TestingUtil.extractComponent(extractTargetCache(testCache), PersistenceManager.class);
persistenceManager.processOnAllStores(KeyFilter.ACCEPT_ALL_FILTER, new AdvancedCacheLoader.CacheLoaderTask() {
@Override
public void processEntry(MarshalledEntry marshalledEntry, AdvancedCacheLoader.TaskContext taskContext) throws InterruptedException {
keyToActivate.compareAndSet(null, (GroupKey) marshalledEntry.getKey());
taskContext.stop();
}
}, false, false);
AssertJUnit.assertNotNull(extractTargetCache(testCache).get(keyToActivate.get())); //activates the key
interceptor.unblockCommand();
//it should able to pick the remove key
Map<GroupKey, String> groupKeySet = future.get();
Map<GroupKey, String> expectedGroupSet = createMap(0, 10);
AssertJUnit.assertEquals(expectedGroupSet, groupKeySet);
}
public void testRemoveGroupKeys() {
final TestCache testCache = createTestCacheAndReset(GROUP, this.caches());
initCache(testCache.primaryOwner);
Map<GroupKey, String> groupKeySet = testCache.testCache.getGroup(GROUP);
Map<GroupKey, String> expectedGroupSet = createMap(0, 10);
AssertJUnit.assertEquals(expectedGroupSet, groupKeySet);
testCache.testCache.removeGroup(GROUP);
AssertJUnit.assertTrue(testCache.testCache.getGroup(GROUP).isEmpty());
}
public void testRemoveGroupKeysWithPersistence() {
final TestCache testCache = createTestCacheAndReset(GROUP, this.caches(PERSISTENCE_CACHE));
initCache(testCache.primaryOwner);
Map<GroupKey, String> groupKeySet = testCache.testCache.getGroup(GROUP);
Map<GroupKey, String> expectedGroupSet = createMap(0, 10);
AssertJUnit.assertEquals(expectedGroupSet, groupKeySet);
testCache.testCache.removeGroup(GROUP);
AssertJUnit.assertTrue(testCache.testCache.getGroup(GROUP).isEmpty());
}
public void testRemoveGroupKeysWithPersistenceAndPassivation() {
final TestCache testCache = createTestCacheAndReset(GROUP, this.caches(PERSISTENCE_PASSIVATION_CACHE));
initCache(testCache.primaryOwner);
Map<GroupKey, String> groupKeySet = testCache.testCache.getGroup(GROUP);
Map<GroupKey, String> expectedGroupSet = createMap(0, 10);
AssertJUnit.assertEquals(expectedGroupSet, groupKeySet);
testCache.testCache.removeGroup(GROUP);
AssertJUnit.assertTrue(testCache.testCache.getGroup(GROUP).isEmpty());
}
public void testRemoveGroupKeysWithPersistenceAndSkipCacheWriter() {
final TestCache testCache = createTestCacheAndReset(GROUP, this.caches(PERSISTENCE_CACHE));
initCache(testCache.primaryOwner);
Map<GroupKey, String> groupKeySet = testCache.testCache.getGroup(GROUP);
Map<GroupKey, String> expectedGroupSet = createMap(0, 10);
AssertJUnit.assertEquals(expectedGroupSet, groupKeySet);
testCache.testCache.withFlags(Flag.SKIP_CACHE_STORE).removeGroup(GROUP);
final Map<GroupKey, String> expectedGroupSet2 = new ConcurrentHashMap<>();
TestingUtil.extractComponent(extractTargetCache(testCache), PersistenceManager.class).processOnAllStores(
new KeyFilter() {
@Override
public boolean accept(Object key) {
return ((GroupKey) key).getGroup().equals(GROUP);
}
}, new AdvancedCacheLoader.CacheLoaderTask() {
@Override
public void processEntry(MarshalledEntry marshalledEntry, AdvancedCacheLoader.TaskContext taskContext) throws InterruptedException {
expectedGroupSet2.put((GroupKey) marshalledEntry.getKey(), (String) marshalledEntry.getValue());
}
}, true, true);
groupKeySet = testCache.testCache.getGroup(GROUP);
expectedGroupSet = new HashMap<>(expectedGroupSet2);
AssertJUnit.assertEquals(expectedGroupSet, groupKeySet);
}
@Override
protected void createCacheManagers() throws Throwable {
createClusteredCaches(3, amendConfiguration(createConfigurationBuilder(transactional)));
defineConfigurationOnAllManagers(PERSISTENCE_CACHE,
amendConfiguration(createConfigurationBuilderWithPersistence(transactional, false)));
waitForClusterToForm(PERSISTENCE_CACHE);
defineConfigurationOnAllManagers(PERSISTENCE_PASSIVATION_CACHE,
amendConfiguration(createConfigurationBuilderWithPersistence(transactional, true)));
waitForClusterToForm(PERSISTENCE_PASSIVATION_CACHE);
}
protected ConfigurationBuilder amendConfiguration(ConfigurationBuilder builder) {
return builder;
}
@Override
protected final void resetCaches(List<Cache<BaseUtilGroupTest.GroupKey, String>> cacheList) {
for (Cache cache : cacheList) {
AsyncInterceptorChain chain = cache.getAdvancedCache().getAsyncInterceptorChain();
BlockCommandInterceptor interceptor = chain.findInterceptorExtending(BlockCommandInterceptor.class);
if (interceptor != null) {
interceptor.reset();
}
}
}
private static ConfigurationBuilder createConfigurationBuilder(boolean transactional) {
ConfigurationBuilder builder = getDefaultClusteredCacheConfig(CacheMode.DIST_SYNC, transactional);
builder.clustering().stateTransfer().fetchInMemoryState(false);
builder.clustering().hash().groups().enabled(true);
builder.clustering().hash().numOwners(2);
return builder;
}
private static ConfigurationBuilder createConfigurationBuilderWithPersistence(boolean transactional, boolean passivation) {
ConfigurationBuilder builder = createConfigurationBuilder(transactional);
if (passivation) {
builder.eviction().maxEntries(2);
}
builder.persistence().passivation(passivation)
.addStore(DummyInMemoryStoreConfigurationBuilder.class)
.fetchPersistentState(false);
return builder;
}
private BlockCommandInterceptor injectIfAbsent(Cache<?, ?> cache) {
log.debugf("Injecting BlockCommandInterceptor in %s", cache);
AsyncInterceptorChain chain = cache.getAdvancedCache().getAsyncInterceptorChain();
BlockCommandInterceptor interceptor = chain.findInterceptorExtending(BlockCommandInterceptor.class);
if (interceptor == null) {
interceptor = new BlockCommandInterceptor(log);
EntryWrappingInterceptor ewi = chain.findInterceptorExtending(EntryWrappingInterceptor.class);
AssertJUnit.assertTrue(chain.addInterceptorAfter(interceptor, ewi.getClass()));
}
interceptor.reset();
log.debugf("Injected BlockCommandInterceptor in %s. Interceptor=%s", cache, interceptor);
return interceptor;
}
private static class BlockCommandInterceptor extends CommandInterceptor {
private volatile CheckPoint checkPoint;
private volatile boolean open;
private final Log log;
private BlockCommandInterceptor(Log log) {
this.log = log;
checkPoint = new CheckPoint();
}
@Override
public Object visitGetKeysInGroupCommand(InvocationContext ctx, GetKeysInGroupCommand command) throws Throwable {
log.debugf("Visit Get Keys in Group. Open? %s. CheckPoint=%s", open, checkPoint);
if (!open) {
checkPoint.trigger("before");
checkPoint.awaitStrict("after", 30, TimeUnit.SECONDS);
}
return invokeNextInterceptor(ctx, command);
}
public final void awaitCommandBlock() throws TimeoutException, InterruptedException {
checkPoint.awaitStrict("before", 30, TimeUnit.SECONDS);
}
public final void unblockCommand() {
checkPoint.trigger("after");
}
public final void reset() {
open = true;
checkPoint = new CheckPoint();
}
}
}