package org.infinispan.query.distributed;
import static org.testng.AssertJUnit.assertEquals;
import javax.transaction.TransactionManager;
import org.apache.lucene.search.MatchAllDocsQuery;
import org.hibernate.search.spi.SearchIntegrator;
import org.infinispan.Cache;
import org.infinispan.query.CacheQuery;
import org.infinispan.query.Search;
import org.infinispan.query.SearchManager;
import org.infinispan.query.helper.StaticTestingErrorHandler;
import org.infinispan.query.helper.TestableCluster;
import org.infinispan.query.indexmanager.InfinispanIndexManager;
import org.infinispan.query.test.Person;
import org.infinispan.test.AbstractInfinispanTest;
import org.testng.annotations.Test;
/**
* Configures the Hibernate Search backend to use Infinispan custom commands as a backend
* transport, and a consistent hash for Master election for each index.
* The test changes the view several times while indexing and verifying index state.
*
* @author Sanne Grinovero
*/
@Test(groups = "functional", testName = "query.distributed.MultiNodeDistributedTest")
public class MultiNodeDistributedTest extends AbstractInfinispanTest {
protected final TestableCluster<String,Person> cluster = new TestableCluster<>(getConfigurationResourceName());
protected String getConfigurationResourceName() {
return "dynamic-indexing-distribution.xml";
}
protected void storeOn(Cache<String, Person> cache, String key, Person person) throws Exception {
TransactionManager transactionManager = cache.getAdvancedCache().getTransactionManager();
if (transactionsEnabled()) transactionManager.begin();
cache.put(key, person);
if (transactionsEnabled()) transactionManager.commit();
}
public void testIndexingWorkDistribution() throws Exception {
try {
cluster.startNewNode();
cluster.startNewNode();
assertIndexSize(0);
//depending on test run, the index master selection might pick either cache.
//We don't know which cache it picks, but we allow writing & searching on all.
storeOn(cluster.getCache(0), "k1", new Person("K. Firt", "Is not a character from the matrix", 1));
assertIndexSize(1);
storeOn(cluster.getCache(1), "k2", new Person("K. Seycond", "Is a pilot", 1));
assertIndexSize(2);
storeOn(cluster.getCache(0), "k3", new Person("K. Theerd", "Forgot the fundamental laws", 1));
assertIndexSize(3);
storeOn(cluster.getCache(1), "k3", new Person("K. Overide", "Impersonating Mr. Theerd", 1));
assertIndexSize(3);
cluster.startNewNode();
storeOn(cluster.getCache(2), "k4", new Person("K. Forth", "Dynamic Topology!", 1));
assertIndexSize(4);
cluster.startNewNode();
assertIndexSize(4);
killMasterNode();
//After a node kill, a stale lock might not be immediately resolved.
//Solicit resolution by issues at least three writes:
storeOn(cluster.getCache(2), "k5", new Person("K. Vife", "Gets stuck in a buffer", 1));
storeOn(cluster.getCache(2), "k6", new Person("K. Seix", "Fills the buffer", 1));
storeOn(cluster.getCache(2), "k7", new Person("K. Vife", "Failover!", 1));
assertIndexSize(7);
}
finally {
cluster.killAll();
}
}
protected void killMasterNode() {
for (Cache<String,Person> cache : cluster.iterateAllCaches()) {
if (isMasterNode(cache)) {
cluster.killNode(cache);
break;
}
}
}
private boolean isMasterNode(Cache<?,?> cache) {
//Implicitly verifies the components are setup as configured by casting:
SearchManager searchManager = Search.getSearchManager(cache);
SearchIntegrator searchFactory = searchManager.unwrap(SearchIntegrator.class);
InfinispanIndexManager indexManager = (InfinispanIndexManager) searchFactory.getIndexManager("person");
return indexManager.isMasterLocal();
}
protected void assertIndexSize(int expectedIndexSize) {
for (Cache cache : cluster.iterateAllCaches()) {
StaticTestingErrorHandler.assertAllGood(cache);
SearchManager searchManager = Search.getSearchManager(cache);
CacheQuery<Person> query = searchManager.getQuery(new MatchAllDocsQuery(), Person.class);
assertEquals(expectedIndexSize, query.list().size());
}
}
protected boolean transactionsEnabled() {
return false;
}
}