package org.infinispan.server.test.cs.jdbc.multinode;
import static org.infinispan.server.test.util.ITestUtils.createMemcachedClient;
import static org.infinispan.server.test.util.ITestUtils.eventually;
import static org.infinispan.server.test.util.ITestUtils.startContainer;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import org.infinispan.arquillian.core.RunningServer;
import org.infinispan.arquillian.core.WithRunningServer;
import org.infinispan.server.test.category.CacheStore;
import org.infinispan.server.test.cs.jdbc.AbstractJdbcStoreMultinodeIT;
import org.infinispan.server.test.util.ITestUtils.Condition;
import org.junit.Test;
import org.junit.experimental.categories.Category;
/**
* Tests fetch-state and singleton attributes of a string-based jdbc cache store.
*
* @author <a href="mailto:mgencur@redhat.com">Martin Gencur</a>
*/
@Category(CacheStore.class)
public class StringBasedStoreMultinodeIT extends AbstractJdbcStoreMultinodeIT {
private final String CONFIG_FETCH_STATE_1 = "testsuite/jdbc-string-multinode-fetch-state1.xml";
private final String CONFIG_FETCH_STATE_2 = "testsuite/jdbc-string-multinode-fetch-state2.xml";
private final String CONFIG_SINGLETON_1 = "testsuite/jdbc-string-multinode-singleton1.xml";
private final String CONFIG_SINGLETON_2 = "testsuite/jdbc-string-multinode-singleton2.xml";
private static final String MANAGER_NAME = "clustered";
private final String CACHE_NAME = "memcachedCache";
private final String TABLE_NAME_PREFIX1 = "STRING_MULTINODEx";
private final String TABLE_NAME_PREFIX2 = "STRING_MULTINODEy";
/**
* When a node joins a cluster, it should fetch state from other caches in the cluster and also
* its underlying cache store should contain the same state immediately. This is ensured by fetch-state
* attribute.
*/
@Test
@WithRunningServer({@RunningServer(name = CONTAINER1, config = CONFIG_FETCH_STATE_1)})
public void testFetchState() throws Exception {
try {
mc1 = createMemcachedClient(server1);
assertCleanCacheAndStore1();
mc1.set("k1", "v1");
mc1.set("k2", "v2");
mc1.set("k3", "v3");
assertNotNull("k1", dbServer1.stringTable.getValueByKey("k1"));
startContainer(controller, CONTAINER2, CONFIG_FETCH_STATE_2);
mc2 = createMemcachedClient(server2);
assertEquals(3, server2.getCacheManager(MANAGER_NAME).getCache(CACHE_NAME).getNumberOfEntries());
assertEquals(1, dbServer2.stringTable.getAllKeys().size());
String evictedKey = dbServer2.stringTable.getAllKeys().get(0);
assertEquals("v" + evictedKey.charAt(1), mc2.get(evictedKey));
assertNull(dbServer2.stringTable.getValueByKey(evictedKey));
assertCleanCacheAndStore2();
} finally {
controller.stop(CONTAINER2);
}
}
/**
* The singleton attribute ensures that only a coordinator in a cluster works with
* its underlying cache store. So even if a key is stored to a cache on server2
* only server1 should store it into its underlying cache store as server1 is the coordinator.
*/
@Test
@WithRunningServer({@RunningServer(name = CONTAINER1, config = CONFIG_SINGLETON_1)})
public void testSingleton() throws Exception {
try {
mc1 = createMemcachedClient(server1);
assertCleanCacheAndStore1();
mc1.set("k1", "v1");
mc1.set("k2", "v2");
assertEquals(2, dbServer1.stringTable.getAllRows().size());
assertNotNull(dbServer1.stringTable.getValueByKey("k1"));
assertNotNull(dbServer1.stringTable.getValueByKey("k2"));
startContainer(controller, CONTAINER2, CONFIG_SINGLETON_2);
assertEquals(2, server2.getCacheManager(MANAGER_NAME).getCache(CACHE_NAME).getNumberOfEntries());
//the cache store should NOT fetch state from the others as there is a singleton defined
assertTrue(dbServer2.stringTable.getAllRows().isEmpty());
mc2 = createMemcachedClient(server2);
mc2.set("k3", "v3");
assertEquals(3, server1.getCacheManager(MANAGER_NAME).getCache(CACHE_NAME).getNumberOfEntries());
assertEquals(3, server2.getCacheManager(MANAGER_NAME).getCache(CACHE_NAME).getNumberOfEntries());
//keys do not go to store2 but to store1
assertTrue(dbServer2.stringTable.getAllRows().isEmpty());
assertEquals(3, dbServer1.stringTable.getAllRows().size());
assertCleanCacheAndStore2();
} finally {
controller.stop(CONTAINER2);
}
}
private void assertCleanCacheAndStore1() throws Exception {
mc1.delete("k1");
mc1.delete("k2");
assertEquals(0, server1.getCacheManager(MANAGER_NAME).getCache(CACHE_NAME).getNumberOfEntries());
if (dbServer1.stringTable.exists() && !dbServer1.stringTable.getAllRows().isEmpty()) {
dbServer1.stringTable.deleteAllRows();
eventually(new Condition() {
@Override
public boolean isSatisfied() throws Exception {
return dbServer1.stringTable.getAllRows().isEmpty();
}
}, 10000);
}
assertNull(dbServer1.stringTable.getValueByKey("k1"));
assertNull(dbServer1.stringTable.getValueByKey("k2"));
}
private void assertCleanCacheAndStore2() throws Exception {
mc2.delete("k1");
mc2.delete("k2");
mc2.delete("k3");
}
@Override
protected void dBServers() {
dbServer1.connectionUrl = System.getProperty("connection.url");
dbServer1.username = System.getProperty("username");
dbServer1.password = System.getProperty("password");
dbServer1.bucketTableName = null;
dbServer1.stringTableName = TABLE_NAME_PREFIX1 + "_" + CACHE_NAME;
dbServer2.connectionUrl = System.getProperty("connection.url.other");
dbServer2.username = System.getProperty("username.other");
dbServer2.password = System.getProperty("password.other");
dbServer2.bucketTableName = null;
dbServer2.stringTableName = TABLE_NAME_PREFIX2 + "_" + CACHE_NAME;
}
@Override
protected String managerName() {
return MANAGER_NAME;
}
@Override
protected String cacheName() {
return CACHE_NAME;
}
}