package org.infinispan.persistence; import static java.util.concurrent.TimeUnit.MILLISECONDS; import static org.infinispan.test.TestingUtil.withTx; import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.assertFalse; import static org.testng.AssertJUnit.assertNotNull; import static org.testng.AssertJUnit.assertTrue; import java.util.concurrent.Callable; import org.infinispan.Cache; import org.infinispan.configuration.cache.CacheMode; import org.infinispan.configuration.cache.ConfigurationBuilder; import org.infinispan.container.DataContainer; import org.infinispan.container.entries.InternalCacheValue; 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.persistence.spi.PersistenceException; import org.infinispan.test.SingleCacheManagerTest; import org.infinispan.test.TestingUtil; import org.infinispan.test.fwk.TestCacheManagerFactory; import org.infinispan.transaction.TransactionMode; import org.infinispan.util.concurrent.IsolationLevel; import org.testng.annotations.Test; /** * Tests write skew functionality when interacting with a cache loader. * * @author Pedro Ruivo * @author Galder ZamarreƱo * @since 5.3 */ @Test(groups = "functional", testName = "persistence.WriteSkewCacheLoaderFunctionalTest") public class WriteSkewCacheLoaderFunctionalTest extends SingleCacheManagerTest { CacheLoader loader; static final long LIFESPAN = 60000000; // very large lifespan so nothing actually expires @Override protected EmbeddedCacheManager createCacheManager() throws Exception { ConfigurationBuilder builder = defineConfiguration(); EmbeddedCacheManager cm = TestCacheManagerFactory.createClusteredCacheManager(builder); loader = TestingUtil.getFirstLoader(cm.getCache()); return cm; } private ConfigurationBuilder defineConfiguration() { ConfigurationBuilder builder = new ConfigurationBuilder(); builder.transaction().transactionMode(TransactionMode.TRANSACTIONAL) .locking().isolationLevel(IsolationLevel.REPEATABLE_READ) .clustering().cacheMode(CacheMode.REPL_SYNC) .persistence().addStore(DummyInMemoryStoreConfigurationBuilder.class) .storeName(this.getClass().getName()).preload(true); return builder; } private void assertInCacheAndStore(Cache cache, CacheLoader loader, Object key, Object value) throws PersistenceException { assertInCacheAndStore(cache, loader, key, value, -1); } private void assertInCacheAndStore(Cache cache, CacheLoader store, Object key, Object value, long lifespanMillis) throws PersistenceException { InternalCacheValue icv = cache.getAdvancedCache().getDataContainer().get(key).toInternalCacheValue(); assertStoredEntry(icv.getValue(), value, icv.getLifespan(), lifespanMillis, "Cache", key); assertNotNull("For :" + icv, icv.getMetadata().version()); MarshalledEntry load = store.load(key); assertStoredEntry(load.getValue(), value, load.getMetadata().lifespan(), lifespanMillis, "Store", key); assertNotNull("For :" + load, load.getMetadata().version()); } private void assertStoredEntry(Object value, Object expectedValue, long lifespanMillis, long expectedLifespan, String src, Object key) { assertNotNull(src + " entry for key " + key + " should NOT be null", value); assertEquals(src + " should contain value " + expectedValue + " under key " + key + " but was " + value, expectedValue, value); assertEquals(src + " expected lifespan for key " + key + " to be " + expectedLifespan + " but was " + lifespanMillis, expectedLifespan, lifespanMillis); } private <T> void assertNotInCacheAndStore(Cache cache, CacheLoader store, T... keys) throws PersistenceException { for (Object key : keys) { assertFalse("Cache should not contain key " + key, cache.getAdvancedCache().getDataContainer().containsKey(key)); assertFalse("Store should not contain key " + key, store.contains(key)); } } public void testPreloadingInTransactionalCache() throws Exception { assertTrue(cache.getCacheConfiguration().persistence().preload()); assertNotInCacheAndStore(cache, loader, "k1", "k2", "k3", "k4"); cache.put("k1", "v1"); cache.put("k2", "v2", LIFESPAN, MILLISECONDS); cache.put("k3", "v3"); cache.put("k4", "v4", LIFESPAN, MILLISECONDS); for (int i = 1; i < 5; i++) { if (i % 2 == 1) assertInCacheAndStore(cache, loader, "k" + i, "v" + i); else assertInCacheAndStore(cache, loader, "k" + i, "v" + i, LIFESPAN); } DataContainer c = cache.getAdvancedCache().getDataContainer(); assertEquals(4, c.size()); cache.stop(); assertEquals(0, c.size()); cache.start(); assertTrue(cache.getCacheConfiguration().persistence().preload()); c = cache.getAdvancedCache().getDataContainer(); assertEquals(4, c.size()); // Re-retrieve since the old reference might not be usable loader = TestingUtil.getFirstLoader(cache); for (int i = 1; i < 5; i++) { if (i % 2 == 1) assertInCacheAndStore(cache, loader, "k" + i, "v" + i); else assertInCacheAndStore(cache, loader, "k" + i, "v" + i, LIFESPAN); } withTx(cache.getAdvancedCache().getTransactionManager(), (Callable<Void>) () -> { assertEquals("v1", cache.get("k1")); cache.put("k1", "new-v1"); return null; }); } }