package org.infinispan.query.backend;
import static org.infinispan.query.helper.StaticTestingErrorHandler.assertAllGood;
import static org.infinispan.test.TestingUtil.killCacheManagers;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicLong;
import org.apache.lucene.search.MatchAllDocsQuery;
import org.infinispan.Cache;
import org.infinispan.configuration.cache.CacheMode;
import org.infinispan.configuration.cache.ConfigurationBuilder;
import org.infinispan.configuration.cache.Index;
import org.infinispan.query.CacheQuery;
import org.infinispan.query.Search;
import org.infinispan.query.SearchManager;
import org.infinispan.query.test.Person;
import org.infinispan.test.MultipleCacheManagersTest;
import org.testng.annotations.Test;
/**
* Test to simulate concurrent index writing and merges using Infinispan Directory under
* the InfinispanIndexManager
*
* @author gustavonalle
* @since 7.0
*/
@Test(groups = "profiling", testName = "query.backend.MergeTest")
public class MergeTest extends MultipleCacheManagersTest {
// Low merge factor means more frequent merges
private static final String MERGE_FACTOR = "10";
private static final int OBJECT_COUNT = 100000;
private static final int NUMBER_OF_THREADS = 10;
private static final boolean TX_ENABLED = false;
Cache<Long, Person> cache1, cache2;
/**
* Main method to escape from testng
*/
public static void main(String[] args) throws Throwable {
MergeTest c = new MergeTest();
c.createBeforeClass();
c.createBeforeMethod();
c.testMergesWrites();
}
@Override
protected void createCacheManagers() throws Throwable {
ConfigurationBuilder cacheCfg = getDefaultClusteredCacheConfig(CacheMode.REPL_SYNC, TX_ENABLED);
cacheCfg.clustering().remoteTimeout(120000)
.indexing().index(Index.LOCAL)
.addProperty("default.directory_provider", "infinispan")
.addProperty("default.indexmanager", "org.infinispan.query.indexmanager.InfinispanIndexManager")
.addProperty("error_handler", "org.infinispan.query.helper.StaticTestingErrorHandler")
.addProperty("default.indexwriter.merge_factor", MERGE_FACTOR)
.addProperty("hibernate.search.default.worker.execution", "async")
.addProperty("default.indexwriter.merge_max_size", "1024")
.addProperty("default.indexwriter.ram_buffer_size", "256")
.addProperty("lucene_version", "LUCENE_CURRENT");
List<Cache<Long, Person>> caches = createClusteredCaches(2, cacheCfg);
cache1 = caches.get(0);
cache2 = caches.get(1);
}
public void testMergesWrites() throws Exception {
final long start = System.currentTimeMillis();
final CountDownLatch waitFor = new CountDownLatch(1);
final AtomicLong id = new AtomicLong(1);
ArrayList<Future> futures = new ArrayList<>();
final Random random = new Random();
for (int i = 0; i < NUMBER_OF_THREADS; i++) {
futures.add(fork(new Runnable() {
@Override
public void run() {
try {
waitFor.await();
Thread.sleep(random.nextInt(3000));
for (int j = 0; j < OBJECT_COUNT; j++) {
Cache<Long, Person> cache = (j % 2 == 0) ? cache1 : cache2;
long key = id.incrementAndGet();
cache.put(key, new Person("name" + key, "blurb", 30));
if (j % 100 == 0) {
System.out.println(j + " in " + (System.currentTimeMillis() - start) / 1000 + "s ");
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}));
}
waitFor.countDown();
for (Future f : futures) {
f.get();
}
assertAllGood(cache1, cache2);
System.out.println("Load took: " + (System.currentTimeMillis() - start) / 1000 + " s");
SearchManager searchManager = Search.getSearchManager(cache1);
final CacheQuery<Person> query = searchManager.getQuery(new MatchAllDocsQuery(), Person.class);
final int total = NUMBER_OF_THREADS * OBJECT_COUNT;
eventually(new Condition() {
@Override
public boolean isSatisfied() throws Exception {
return query.list().size() == total;
}
});
System.out.println("Indexing finished: " + query.list().size());
killCacheManagers(cacheManagers);
}
}