/*
* Copyright 2004-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.compass.core.test.concurrency.multi;
import java.util.Date;
import java.util.concurrent.atomic.AtomicLong;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.compass.annotations.test.AbstractAnnotationsTestCase;
import org.compass.core.Compass;
import org.compass.core.CompassCallbackWithoutResult;
import org.compass.core.CompassException;
import org.compass.core.CompassHits;
import org.compass.core.CompassSession;
import org.compass.core.CompassTemplate;
import org.compass.core.config.CompassConfiguration;
import org.compass.core.lucene.LuceneEnvironment;
import org.compass.core.util.FileHandlerMonitor;
/**
* Base class for simple concurrency tests for Compass. Starts several threads, each reads and writes data.
*
* @author kimchy
*/
public abstract class AbstractMultiLoadTests extends AbstractAnnotationsTestCase {
private final Log logger = LogFactory.getLog(getClass());
private long id = 0;
private final AtomicLong longGenerator = new AtomicLong();
@Override
protected void addExtraConf(CompassConfiguration conf) {
conf.addClass(A.class);
conf.getSettings().setSetting(LuceneEnvironment.Transaction.LOCK_TIMEOUT, "30s");
}
/**
* The number of threads that will be used to execute the test.
*/
protected abstract int getNumberOfThreads();
/**
* The number of Compass instances that will be used. Each thread will pick a compass instace (round robin).
* If set to 1, all threads will use the same Compass instance.
*/
protected abstract int getNumberOfCompassInstances();
/**
* The number of cycles each thread will run.
*/
protected abstract long getNumberOfCycles();
/**
* The write factor. Every how many cycles, a write will be perfomed.
*/
protected abstract int getWriteFactor();
private volatile boolean error = false;
public void testMultiConcurrentThreads() throws Exception {
// no need for the original Compass ...
getCompass().close();
CompassConfiguration conf = buildConf();
CompassTemplate[] templates = new CompassTemplate[getNumberOfCompassInstances()];
for (int i = 0; i < getNumberOfCompassInstances(); i++) {
Compass compass = conf.buildCompass();
templates[i] = new CompassTemplate(compass);
}
FileHandlerMonitor fileHandlerMonitor = FileHandlerMonitor.getFileHandlerMonitor(templates[0].getCompass());
fileHandlerMonitor.verifyNoHandlers();
templates[0].getCompass().getSearchEngineIndexManager().deleteIndex();
templates[0].getCompass().getSearchEngineIndexManager().createIndex();
Thread[] threads = new Thread[getNumberOfThreads()];
for (int i = 0; i < threads.length; i++) {
threads[i] = new Thread(new SimpleLoadTesterRunnable(templates[i % getNumberOfCompassInstances()], i, getNumberOfCycles(), getWriteFactor()), "L" + i);
}
for (Thread thread : threads) {
thread.start();
}
for (Thread thread : threads) {
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
logger.info("VERIFYING INDEX USING EXISTING TEMPLATE");
// now check that everything is in the index
check(templates[0]);
for (int i = 0; i < getNumberOfCompassInstances(); i++) {
templates[i].getCompass().close();
}
fileHandlerMonitor.verifyNoHandlers();
logger.info("VERIFYING INDEX USING NEW COMPASS INSTANCE");
// now build a new one and check again
CompassTemplate template = new CompassTemplate(conf.buildCompass());
fileHandlerMonitor = FileHandlerMonitor.getFileHandlerMonitor(template.getCompass());
fileHandlerMonitor.verifyNoHandlers();
check(template);
template.getCompass().close();
fileHandlerMonitor.verifyNoHandlers();
if (error) {
fail("Multi Concurrent Test Failed, check logs...");
}
}
public class SimpleLoadTesterRunnable implements Runnable {
private long cycles;
private long runId;
private int writeFactor;
private Long lastIdWritten;
private CompassTemplate template;
public SimpleLoadTesterRunnable(CompassTemplate template, long runId, long cycles, int writeFactor) {
this.cycles = cycles;
this.runId = runId;
this.writeFactor = writeFactor;
this.template = template;
}
private String failureStringPrefix(long cycle, Long id) {
return "FAILURE RUN[" + runId + "] CYCLE[" + cycle + "] ID[" + id + "] ";
}
private void check(CompassSession session, long cycle, Long id) {
A a = session.get(A.class, id);
if (a == null) {
error = true;
logger.error(failureStringPrefix(cycle, id) + " A NULL");
}
CompassHits hits = session.find("mdata1:" + id);
if (0 == hits.length()) {
error = true;
logger.error(failureStringPrefix(cycle, id) + " HITS ZERO [" + hits.length() + "]");
}
}
public void run() {
try {
long totalTime = System.currentTimeMillis();
for (long i = 0; i < cycles; i++) {
final long cycle = i;
if (cycle % writeFactor == 0) {
template.execute(new CompassCallbackWithoutResult() {
protected void doInCompassWithoutResult(CompassSession session) throws CompassException {
lastIdWritten = longGenerator.incrementAndGet();
A a = session.get(A.class, lastIdWritten);
if (a != null) {
error = true;
logger.error(failureStringPrefix(cycle, lastIdWritten) + " A NOT NULL [" + a + "]");
}
CompassHits hits = session.find("mdata1:" + lastIdWritten);
if (0 != hits.length()) {
error = true;
logger.error(failureStringPrefix(cycle, lastIdWritten) + " HITS NOT ZERO [" + hits.length() + "]");
}
a = new A();
a.id = lastIdWritten;
a.data1 = "" + lastIdWritten;
a.indexTime = new Date();
session.save(a);
check(session, cycle, lastIdWritten);
}
});
} else {
template.execute(new CompassCallbackWithoutResult() {
protected void doInCompassWithoutResult(CompassSession session) throws CompassException {
check(session, cycle, lastIdWritten);
}
});
}
}
totalTime = System.currentTimeMillis() - totalTime;
logger.info("FINISHED RUN [" + runId + "] TOOK [" + totalTime + "]");
} catch (Exception e) {
error = true;
logger.error("FAILURE RUN [" + runId + "], THREAD ABORTED...", e);
}
}
}
private void check(CompassTemplate template) {
long time = System.currentTimeMillis();
final long limit = id;
template.execute(new CompassCallbackWithoutResult() {
protected void doInCompassWithoutResult(CompassSession session) throws CompassException {
for (long i = 1; i < limit; i++) {
A a = session.get(A.class, i);
if (a == null) {
error = true;
logger.error("FAILURE ID [" + i + "] FINAL CHECK NULL");
}
}
}
});
logger.info("FINISHED CHECK [1-" + limit + "] TOOK [" + (System.currentTimeMillis() - time) + "]");
}
}