/*
* JBoss, Home of Professional Open Source
* Copyright 2011 Red Hat Inc. and/or its affiliates and other
* contributors as indicated by the @author tags. All rights reserved.
* See the copyright.txt in the distribution for a full listing of
* individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.infinispan.lucene.profiling;
import java.io.IOException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.Field.Index;
import org.apache.lucene.document.Field.Store;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.Term;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.TermQuery;
import org.apache.lucene.search.TopDocs;
import org.apache.lucene.store.Directory;
import org.infinispan.Cache;
import org.infinispan.config.Configuration;
import org.infinispan.config.Configuration.CacheMode;
import org.infinispan.lucene.InfinispanDirectory;
import org.infinispan.lucene.testutils.LuceneSettings;
import org.infinispan.manager.EmbeddedCacheManager;
import org.infinispan.test.MultipleCacheManagersTest;
import org.infinispan.test.TestingUtil;
import org.infinispan.test.fwk.TransportFlags;
import org.infinispan.util.logging.Log;
import org.infinispan.util.logging.LogFactory;
import org.jgroups.protocols.DISCARD;
import org.testng.annotations.Test;
/**
* @author Sanne Grinovero <sanne@infinispan.org> (C) 2011 Red Hat Inc.
*/
@SuppressWarnings("deprecation")
@Test(groups = "profiling", testName = "lucene.profiling.DynamicTopologyStressTest", sequential = true)
public class DynamicTopologyStressTest extends MultipleCacheManagersTest {
private static final Log log = LogFactory.getLog(DynamicTopologyStressTest.class);
private static final boolean KEEP_GOING = false;
private static final boolean VISUAL_PROGRESS_FEEDBACK = false;
/** Number of seconds the test will run. Should run for some minutes **/
private static final int SECONDS_TOTAL = 15;
/** How many Documents to write in each second **/
private static final int WRITES_PER_SECOND = 100;
/** Number of Terms written in the index **/
private static final int INITIAL_INDEX_TERMS = 2000;
private static final String INDEX_NAME = "unstableIndex";
private static final String FIELDNAME = "fieldname";
/** Number of reading nodes **/
private static final int READERS = 5;
private final AtomicBoolean failed = new AtomicBoolean(false);
private volatile int lastWrittenTermId = 0;
/** Registry of clustered CacheManagers used as readers **/
private EmbeddedCacheManager[] readers = new EmbeddedCacheManager[READERS];
private DISCARD[] discardPerNode = new DISCARD[READERS];
private EmbeddedCacheManager writingNode;
@Override
protected void createCacheManagers() throws Throwable {
Configuration defaultClusteredConfig = org.infinispan.test.AbstractCacheTest
.getDefaultClusteredConfig(CacheMode.DIST_SYNC, false);
defaultClusteredConfig.setInvocationBatchingEnabled(true);
TransportFlags transportFlags = new TransportFlags().withMerge(true);
writingNode = addClusterEnabledCacheManager(defaultClusteredConfig, transportFlags);
for (int i = 0; i < READERS; i++) {
readers[i] = addClusterEnabledCacheManager(defaultClusteredConfig, transportFlags);
Cache<Object, Object> cache = readers[i].getCache();
discardPerNode[i] = TestingUtil.getDiscardForCache(cache);
TestingUtil.setDelayForCache(cache, 1, 1);
TestingUtil.blockUntilViewReceived(cache, i + 1, 1000);
}
}
@Test
void testDirectoryUnstableCluster() throws IOException {
InfinispanDirectory masterDirectory = new InfinispanDirectory(writingNode.getCache(), INDEX_NAME);
SharedState sharedIndexState = IndexReadingStressTest.fillDirectory(masterDirectory, INITIAL_INDEX_TERMS);
ExecutorService executor = Executors.newFixedThreadPool(READERS + 1);
try {
executor.execute(new ConstantWritingThread(masterDirectory, sharedIndexState));
for (int i=0; i<READERS; i++) {
executor.execute(new ConstantReadingThread(masterDirectory, sharedIndexState));
}
executor.shutdown();
executor.awaitTermination(SECONDS_TOTAL * 2, TimeUnit.SECONDS); //wait for all jobs to finish
} catch (InterruptedException e) {
log.error(e);
assert false : "unexpected interruption";
}
assert failed.get() == false;
}
public class ConstantWritingThread implements Runnable {
private final Directory masterDirectory;
public ConstantWritingThread(Directory masterDirectory,
SharedState sharedIndexState) {
this.masterDirectory = masterDirectory;
}
@Override
public void run() {
while (lastWrittenTermId < (SECONDS_TOTAL * WRITES_PER_SECOND) && failed.get() == false) {
IndexWriter writer = null;
try {
int toWrite = lastWrittenTermId + 1;
writer = LuceneSettings.openWriter(masterDirectory, 3); // 3 or any low setting
Document doc = new Document();
Field field = new Field(FIELDNAME, "HA" + toWrite, Store.YES, Index.NOT_ANALYZED);
doc.add(field);
writer.addDocument(doc);
writer.commit();
lastWrittenTermId = toWrite;
if (VISUAL_PROGRESS_FEEDBACK) System.out.println("Written: " + doc);
Thread.sleep( 1000 / WRITES_PER_SECOND );
} catch (IOException e) {
failed(e);
return;
} catch (InterruptedException e) {
failed(e);
return;
} finally {
try {
if (writer != null) {
writer.close();
}
} catch (IOException e) {
failed(e);
return;
}
}
}
}
}
private void failed(Exception e) {
log.error(e);
if (! KEEP_GOING) {
failed.set(true);
}
}
public class ConstantReadingThread implements Runnable {
private final Directory masterDirectory;
public ConstantReadingThread(Directory masterDirectory, SharedState sharedIndexState) {
this.masterDirectory = masterDirectory;
}
@Override
public void run() {
while (lastWrittenTermId < (SECONDS_TOTAL * WRITES_PER_SECOND) && failed.get() == false) {
IndexReader indexReader = null;
try {
int i = lastWrittenTermId;
indexReader = IndexReader.open(masterDirectory, true);
IndexSearcher indexSearcher = new IndexSearcher(indexReader);
if (i==0) continue; // nothing written yet
String termValue = "HA" + i;
Query query = new TermQuery(new Term( FIELDNAME, termValue));
TopDocs docs = indexSearcher.search(query, null, 1);
if (docs.totalHits != 1) {
failed.set(true);
log.error("String '" + termValue + "' should exist but was not found in index");
}
if (VISUAL_PROGRESS_FEEDBACK) System.out.print(".");
Thread.sleep( 1 );
} catch (IOException e) {
failed(e);
return;
} catch (InterruptedException e) {
failed(e);
return;
} finally {
try {
if (indexReader != null) {
indexReader.close();
}
} catch (IOException e) {
failed(e);
return;
}
}
}
}
}
}