package org.multiverse.stms.gamma.integration.isolation;
import org.junit.Before;
import org.junit.Test;
import org.multiverse.TestThread;
import org.multiverse.api.Txn;
import org.multiverse.api.TxnExecutor;
import org.multiverse.api.LockMode;
import org.multiverse.api.callables.TxnBooleanCallable;
import org.multiverse.stms.gamma.GammaStm;
import org.multiverse.stms.gamma.transactionalobjects.GammaTxnLong;
import static java.lang.System.currentTimeMillis;
import static org.junit.Assert.assertEquals;
import static org.multiverse.TestUtils.joinAll;
import static org.multiverse.TestUtils.randomOneOf;
import static org.multiverse.api.GlobalStmInstance.getGlobalStmInstance;
import static org.multiverse.api.TxnThreadLocal.clearThreadLocalTxn;
import static org.multiverse.stms.gamma.BenchmarkUtils.transactionsPerSecondAsString;
import static org.multiverse.stms.gamma.BenchmarkUtils.transactionsPerSecondPerThreadAsString;
/**
* A Stress test that checks if the system is able to deal with mostly reading transactions and doesn't cause
* any isolation problems.
*
* @author Peter Veentjer.
*/
public class ReadBiasedIsolationStressTest {
private GammaStm stm = (GammaStm) getGlobalStmInstance();
private int chanceOfUpdate = new GammaTxnLong(stm).getReadBiasedThreshold() * 5;
private int threadCount = 4;
@Before
public void setUp() {
clearThreadLocalTxn();
}
//todo: testing with and without arriveenabled functionality
@Test
public void none_and_dirtyCheckEnabled() {
test(LockMode.None, true);
}
@Test
public void none_read_and_dirtyCheckDisabled() {
test(LockMode.None, false);
}
@Test
public void read_and_dirtyCheckEnabled() {
test(LockMode.Read, true);
}
@Test
public void read_and_dirtyCheckDisabled() {
test(LockMode.Read, false);
}
@Test
public void write_and_dirtyCheckEnabled() {
test(LockMode.Write, true);
}
@Test
public void write_and_dirtyCheckDisabled() {
test(LockMode.Write, false);
}
@Test
public void commit_and_dirtyCheckEnabled() {
test(LockMode.Exclusive, true);
}
@Test
public void commit_and_dirtyCheckDisabled() {
test(LockMode.Exclusive, false);
}
public void test(LockMode lockMode, boolean dirtyCheckEnabled) {
StressThread[] threads = new StressThread[threadCount];
GammaTxnLong ref = new GammaTxnLong(stm);
long transactionsPerThread = 100 * 1000 * 1000;
for (int k = 0; k < threads.length; k++) {
threads[k] = new StressThread(k, ref, transactionsPerThread, lockMode, dirtyCheckEnabled);
}
for (StressThread thread : threads) {
thread.start();
}
joinAll(threads);
long totalDurationMs = 0;
long sum = 0;
for (StressThread thread : threads) {
totalDurationMs += thread.durationMs;
sum += thread.incrementCount;
}
System.out.println("--------------------------------------------------------");
System.out.printf("Threadcount: %s\n", threadCount);
System.out.printf("Performance: %s transactions/second/thread\n",
transactionsPerSecondPerThreadAsString(transactionsPerThread, totalDurationMs, threadCount));
System.out.printf("Performance: %s transactions/second\n",
transactionsPerSecondAsString(transactionsPerThread, totalDurationMs, threadCount));
assertEquals(sum, ref.atomicGet());
System.out.println("ref.orec: " + ref.___toOrecString());
}
class StressThread extends TestThread {
private final boolean dirtyCheckEnabled;
private final GammaTxnLong ref;
private final long count;
private long durationMs;
private LockMode lockMode;
private long incrementCount = 0;
public StressThread(int id, GammaTxnLong ref, long count, LockMode lockMode, boolean dirtyCheckEnabled) {
super("StressThread-" + id);
this.ref = ref;
this.count = count;
this.lockMode = lockMode;
this.dirtyCheckEnabled = dirtyCheckEnabled;
}
@Override
public void doRun() {
TxnExecutor executor = stm.newTxnFactoryBuilder()
.setDirtyCheckEnabled(dirtyCheckEnabled)
.newTxnExecutor();
TxnBooleanCallable callable = new TxnBooleanCallable() {
@Override
public boolean call(Txn tx) throws Exception {
ref.getLock().acquire(tx, lockMode);
if (randomOneOf(chanceOfUpdate)) {
ref.incrementAndGet(tx, 1);
return true;
} else {
ref.get(tx);
return false;
}
}
};
long startMs = currentTimeMillis();
for (long k = 0; k < count; k++) {
if (executor.execute(callable)) {
incrementCount++;
}
if (k % 10000000 == 0) {
System.out.printf("%s is at %s\n", getName(), k);
}
}
durationMs = currentTimeMillis() - startMs;
System.out.printf("finished %s after %s ms\n", getName(), durationMs);
}
}
}