package org.corfudb.runtime.object.transactions;
import org.corfudb.runtime.collections.ISMRMap;
import org.corfudb.runtime.collections.SMRMap;
import org.corfudb.runtime.exceptions.TransactionAbortedException;
import org.junit.Test;
import java.util.ArrayList;
import java.util.Random;
import java.util.concurrent.atomic.AtomicInteger;
/**
* These tests generate workloads with mixed reads/writes on multiple maps.
*
* The tests previously surfaced a concurrency bug in navigating streams
* by concurrent threads. It's good to leave it in.
*
* Created by dmalkhi on 12/13/16.
*/
public class StreamTest extends AbstractTransactionsTest {
@Override
public void TXBegin() { OptimisticTXBegin(); }
/**
* This workload operates over three distinct maps
*/
ISMRMap<String, Integer> map1, map2, map3;
private final int NUM_BATCHES = 3;
private final int BATCH_SZ = 1_0;
private final int numTasks = NUM_BATCHES * BATCH_SZ;
/**
* Set up a repeatable PRNG
*/
private final int SEED = 434343;
Random rand = new Random(SEED);
private final int READ_PERCENT = 80;
private final int MAX_PERCENT = 100;
@Test
public void mixBackpointers() {
final int smallNumber = 5;
/**
* Instantiate three streams with three SMRmap objects
*/
map1 = instantiateCorfuObject(SMRMap.class, "A"); map1.clear();
map2 = instantiateCorfuObject(SMRMap.class, "B"); map2.clear();
map3 = instantiateCorfuObject(SMRMap.class, "foo"); map3.clear();
// generate multi-stream entries
WWTXBegin();
for (int i = 0; i < smallNumber; i++) {
map1.put("m1" + i, i);
map2.put("m2" + i, i);
map3.put("m3" + i, i);
}
TXEnd();
final int concurrency = 4;
for (int j = 0; j < concurrency; j++)
t(j, () -> {
WWTXBegin();
});
t(concurrency, () -> { WWTXBegin(); map2.put("foo", 0); TXEnd(); });
// now, thread 0..concurrency-1 have to go back in
// the stream of map3 to its snapshot-position
for (int j = 0; j < concurrency; j++) {
final int threadNum = j;
t(threadNum, () -> {
try {
map3.put("bar" + threadNum, 0);
TXEnd();
} catch (NullPointerException ne) {
throw new RuntimeException();
}
});
}
}
/**
* This method initiates all the data structures for this program
*/
public void generateMaps() {
/**
* Instantiate three streams with three SMRmap objects
*/
map1 = instantiateCorfuObject(SMRMap.class, "A");
map2 = instantiateCorfuObject(SMRMap.class, "B");
map3 = instantiateCorfuObject(SMRMap.class, "foo");
// populate maps
for (int i = 0; i < NUM_BATCHES; i++) {
WWTXBegin();
for (int j = 0; j < BATCH_SZ; j++) {
map1.put("m1" + (i * BATCH_SZ + j), i);
map2.put("m2" + (i * BATCH_SZ + j), i);
map3.put("m3" + (i * BATCH_SZ + j), i);
}
TXEnd();
}
}
/**
* generate a workload with mixed R/W TX's
* - NUM_BATCHES TX's
* - each TX performs BATCH_SZ operations, mixing R/W according to the specified ratio.
*
* @param readPrecent ratio of reads (to 100)
*/
public void concurrentStreamRWLoad(int ignoredThreadNum, int readPrecent) {
final AtomicInteger aborts = new AtomicInteger(0);
for (int i = 0; i < NUM_BATCHES; i++) {
final int fi = i;
addTestStep(task_num -> {
WWTXBegin();
});
for (int j = 0; j < BATCH_SZ; j++) {
final int fj = j;
addTestStep(task_num -> {
int r1 = rand.nextInt(numTasks);
int r2 = rand.nextInt(numTasks);
int r3 = rand.nextInt(numTasks);
int accumulator = 0;
accumulator += map1.get("m1" + r1);
accumulator += map2.get("m2" + r2);
accumulator += map3.get("m3" + r3);
// perform random put()'s with probability '1 - readPercent/100'
if (rand.nextInt(MAX_PERCENT) >= readPrecent) {
if (rand.nextInt(2) == 1)
map2.put("m2" + rand.nextInt(numTasks), accumulator);
else
map3.put("m3" + rand.nextInt(numTasks), accumulator);
}
});
}
addTestStep(task_num -> {
try {
TXEnd();
} catch (TransactionAbortedException te) {
aborts.getAndIncrement();
}
});
}
addTestStep(task_num ->
calculateAbortRate(aborts.get(), numTasks)
);
}
/**
* This is where activity is started
*/
@Test
public void testConcurrentStreamRW() {
final int NUM_THREADS = 3;
ArrayList<Thread> tList = new ArrayList<>();
// generate the maps and populate with elements
generateMaps();
concurrentStreamRWLoad(-1, READ_PERCENT);
scheduleInterleaved(NUM_THREADS, NUM_THREADS);
}
}