package org.infinispan.persistence; import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.assertTrue; import org.infinispan.Cache; import org.infinispan.commons.marshall.StreamingMarshaller; import org.infinispan.configuration.cache.CacheMode; import org.infinispan.configuration.cache.ConfigurationBuilder; import org.infinispan.context.Flag; import org.infinispan.interceptors.AsyncInterceptorChain; import org.infinispan.interceptors.impl.CacheLoaderInterceptor; import org.infinispan.manager.EmbeddedCacheManager; import org.infinispan.marshall.core.MarshalledEntry; import org.infinispan.marshall.core.MarshalledEntryImpl; import org.infinispan.persistence.dummy.DummyInMemoryStoreConfigurationBuilder; import org.infinispan.test.SingleCacheManagerTest; import org.infinispan.test.TestingUtil; import org.infinispan.test.fwk.TestCacheManagerFactory; import org.infinispan.util.concurrent.IsolationLevel; import org.testng.annotations.Test; /** * Tests if the conditional commands correctly fetch the value from cache loader even with the skip cache load/store * flags. * <p/> * The configuration used is a non-tx non-clustered cache without passivation. * * @author Pedro Ruivo * @since 7.0 */ @Test(groups = "functional", testName = "persistence.LocalConditionalCommandTest") public class LocalConditionalCommandTest extends SingleCacheManagerTest { private static final String PRIVATE_STORE_CACHE_NAME = "private-store-cache"; private static final String SHARED_STORE_CACHE_NAME = "shared-store-cache"; private final String key = getClass().getSimpleName() + "-key"; private final String value1 = getClass().getSimpleName() + "-value1"; private final String value2 = getClass().getSimpleName() + "-value2"; private final boolean transactional; private final boolean passivation; public LocalConditionalCommandTest() { this(false, false); } protected LocalConditionalCommandTest(boolean transactional, boolean passivation) { this.transactional = transactional; this.passivation = passivation; } private static ConfigurationBuilder createConfiguration(String storeName, boolean shared, boolean transactional, boolean passivation) { ConfigurationBuilder builder = getDefaultClusteredCacheConfig(CacheMode.LOCAL, transactional); builder.jmxStatistics().enable(); builder.persistence() .passivation(passivation) .addStore(DummyInMemoryStoreConfigurationBuilder.class) .storeName(storeName + (shared ? "-shared" : "-private")) .fetchPersistentState(false) .purgeOnStartup(true) .shared(shared); builder.locking().isolationLevel(IsolationLevel.READ_COMMITTED); return builder; } private static <K, V> void writeToStore(Cache<K, V> cache, K key, V value) { TestingUtil.getFirstWriter(cache).write(marshalledEntry(key, value, cache.getAdvancedCache().getComponentRegistry().getCacheMarshaller())); } private static <K, V> MarshalledEntry<K, V> marshalledEntry(K key, V value, StreamingMarshaller marshaller) { return new MarshalledEntryImpl<>(key, value, null, marshaller); } private static CacheLoaderInterceptor cacheLoaderInterceptor(Cache<?, ?> cache) { AsyncInterceptorChain chain = TestingUtil.extractComponent(cache, AsyncInterceptorChain.class); return chain.findInterceptorExtending( CacheLoaderInterceptor.class); } private void doTest(Cache<String, String> cache, ConditionalOperation operation, Flag flag) { assertEmpty(cache); initStore(cache); final boolean skipLoad = flag == Flag.SKIP_CACHE_LOAD || flag == Flag.SKIP_CACHE_STORE; try { if (flag != null) { operation.execute(cache.getAdvancedCache().withFlags(flag), key, value1, value2); } else { operation.execute(cache, key, value1, value2); } } catch (Exception e) { //some operation are allowed to fail. e.g. putIfAbsent. //we only check the final value log.debug(e); } assertLoadAfterOperation(cache, skipLoad); assertEquals(operation.finalValue(value1, value2, skipLoad), cache.get(key)); } private void assertLoadAfterOperation(Cache<?, ?> cache, boolean skipLoad) { assertEquals("cache load", skipLoad ? 0 : 1, cacheLoaderInterceptor(cache).getCacheLoaderLoads()); } private void assertEmpty(Cache<?, ?> cache) { assertTrue(cache + ".isEmpty()", cache.isEmpty()); } private void initStore(Cache<String, String> cache) { writeToStore(cache, key, value1); assertTrue(TestingUtil.getFirstLoader(cache).contains(key)); cacheLoaderInterceptor(cache).resetStatistics(); } public void testPutIfAbsentWithSkipCacheLoader() { doTest(this.cache(PRIVATE_STORE_CACHE_NAME), ConditionalOperation.PUT_IF_ABSENT, Flag.SKIP_CACHE_LOAD); } public void testPutIfAbsentWithIgnoreReturnValues() { doTest(this.cache(PRIVATE_STORE_CACHE_NAME), ConditionalOperation.PUT_IF_ABSENT, Flag.IGNORE_RETURN_VALUES); } public void testPutIfAbsent() { doTest(this.cache(PRIVATE_STORE_CACHE_NAME), ConditionalOperation.PUT_IF_ABSENT, null); } public void testReplaceWithSkipCacheLoader() { doTest(this.cache(PRIVATE_STORE_CACHE_NAME), ConditionalOperation.REPLACE, Flag.SKIP_CACHE_LOAD); } public void testReplaceWithIgnoreReturnValues() { doTest(this.cache(PRIVATE_STORE_CACHE_NAME), ConditionalOperation.REPLACE, Flag.IGNORE_RETURN_VALUES); } public void testReplace() { doTest(this.cache(PRIVATE_STORE_CACHE_NAME), ConditionalOperation.REPLACE, null); } public void testReplaceIfWithSkipCacheLoader() { doTest(this.cache(PRIVATE_STORE_CACHE_NAME), ConditionalOperation.REPLACE_IF, Flag.SKIP_CACHE_LOAD); } public void testReplaceIfWithIgnoreReturnValues() { doTest(this.cache(PRIVATE_STORE_CACHE_NAME), ConditionalOperation.REPLACE_IF, Flag.IGNORE_RETURN_VALUES); } public void testReplaceIf() { doTest(this.cache(PRIVATE_STORE_CACHE_NAME), ConditionalOperation.REPLACE_IF, null); } public void testRemoveIfWithSkipCacheLoader() { doTest(this.cache(PRIVATE_STORE_CACHE_NAME), ConditionalOperation.REMOVE_IF, Flag.SKIP_CACHE_LOAD); } public void testRemoveIfWithIgnoreReturnValues() { doTest(this.cache(PRIVATE_STORE_CACHE_NAME), ConditionalOperation.REMOVE_IF, Flag.IGNORE_RETURN_VALUES); } public void testRemoveIf() { doTest(this.cache(PRIVATE_STORE_CACHE_NAME), ConditionalOperation.REMOVE_IF, null); } @Override protected EmbeddedCacheManager createCacheManager() throws Exception { EmbeddedCacheManager embeddedCacheManager = TestCacheManagerFactory.createCacheManager(); embeddedCacheManager.defineConfiguration(PRIVATE_STORE_CACHE_NAME, createConfiguration(getClass().getSimpleName(), false, transactional, passivation).build()); embeddedCacheManager.defineConfiguration(SHARED_STORE_CACHE_NAME, createConfiguration(getClass().getSimpleName(), true, transactional, passivation).build()); return embeddedCacheManager; } private enum ConditionalOperation { PUT_IF_ABSENT { @Override public <K, V> void execute(Cache<K, V> cache, K key, V value1, V value2) { cache.putIfAbsent(key, value2); } @Override public <V> V finalValue(V value1, V value2, boolean skipLoad) { return skipLoad ? value2 : value1; } }, REPLACE { @Override public <K, V> void execute(Cache<K, V> cache, K key, V value1, V value2) { cache.replace(key, value2); } @Override public <V> V finalValue(V value1, V value2, boolean skipLoad) { return skipLoad ? value1 : value2; } }, REPLACE_IF { @Override public <K, V> void execute(Cache<K, V> cache, K key, V value1, V value2) { cache.replace(key, value1, value2); } @Override public <V> V finalValue(V value1, V value2, boolean skipLoad) { return skipLoad ? value1 : value2; } }, REMOVE_IF { @Override public <K, V> void execute(Cache<K, V> cache, K key, V value1, V value2) { cache.remove(key, value1); } @Override public <V> V finalValue(V value1, V value2, boolean skipLoad) { return skipLoad ? value1 : null; } }; public abstract <K, V> void execute(Cache<K, V> cache, K key, V value1, V value2); public abstract <V> V finalValue(V value1, V value2, boolean skipLoad); } }