package edu.brown.hstore.estimators.markov;
import java.io.File;
import java.util.*;
import java.util.concurrent.atomic.AtomicLong;
import org.junit.Test;
import org.voltdb.VoltProcedure;
import org.voltdb.benchmark.tpcc.procedures.neworder;
import org.voltdb.catalog.*;
import org.voltdb.types.ExpressionType;
import edu.brown.BaseTestCase;
import edu.brown.catalog.CatalogUtil;
import edu.brown.hstore.HStoreConstants;
import edu.brown.hstore.conf.HStoreConf;
import edu.brown.hstore.estimators.Estimate;
import edu.brown.hstore.estimators.markov.MarkovEstimate;
import edu.brown.hstore.estimators.markov.MarkovEstimator;
import edu.brown.hstore.estimators.markov.MarkovEstimatorState;
import edu.brown.markov.EstimationThresholds;
import edu.brown.markov.MarkovGraph;
import edu.brown.markov.MarkovUtil.StatementWrapper;
import edu.brown.markov.MarkovVertex;
import edu.brown.markov.containers.MarkovGraphsContainerUtil;
import edu.brown.markov.containers.MarkovGraphsContainer;
import edu.brown.utils.*;
import edu.brown.workload.QueryTrace;
import edu.brown.workload.Workload;
import edu.brown.workload.TransactionTrace;
import edu.brown.workload.filters.BasePartitionTxnFilter;
import edu.brown.workload.filters.Filter;
import edu.brown.workload.filters.NoAbortFilter;
import edu.brown.workload.filters.ProcParameterArraySizeFilter;
import edu.brown.workload.filters.ProcParameterValueFilter;
import edu.brown.workload.filters.ProcedureLimitFilter;
import edu.brown.workload.filters.ProcedureNameFilter;
/**
*
* @author pavlo
*
*/
public class TestMarkovEstimator extends BaseTestCase {
public static final Random rand = new Random();
private static final int WORKLOAD_XACT_LIMIT = 50;
private static final int BASE_PARTITION = 1;
private static final int NUM_PARTITIONS = 5;
private static final Class<? extends VoltProcedure> TARGET_PROCEDURE = neworder.class;
private static AtomicLong XACT_ID = new AtomicLong(1000);
private static Workload workload;
private static MarkovGraphsContainer markovs;
private static TransactionTrace singlep_trace;
private static TransactionTrace multip_trace;
private static final List<MarkovVertex> multip_path = new ArrayList<MarkovVertex>();
private MarkovEstimator t_estimator;
private EstimationThresholds thresholds;
private Procedure catalog_proc;
@Override
protected void setUp() throws Exception {
super.setUp(ProjectType.TPCC);
this.addPartitions(NUM_PARTITIONS);
HStoreConf hstore_conf = HStoreConf.singleton();
hstore_conf.site.markov_path_caching = false;
hstore_conf.site.markov_fast_path = false;
hstore_conf.site.markov_force_traversal = true;
hstore_conf.site.markov_learning_enable = false;
this.catalog_proc = this.getProcedure(TARGET_PROCEDURE);
if (isFirstSetup()) {
Filter filter = new ProcedureNameFilter(false)
.include(TARGET_PROCEDURE.getSimpleName())
.attach(new NoAbortFilter())
.attach(new ProcParameterValueFilter().include(1, new Integer(5))) // D_ID
.attach(new ProcParameterArraySizeFilter(CatalogUtil.getArrayProcParameters(catalog_proc).get(0), 10, ExpressionType.COMPARE_EQUAL))
.attach(new BasePartitionTxnFilter(p_estimator, BASE_PARTITION))
.attach(new ProcedureLimitFilter(WORKLOAD_XACT_LIMIT));
File file = this.getWorkloadFile(ProjectType.TPCC);
workload = new Workload(catalogContext.catalog);
((Workload) workload).load(file, catalogContext.database, filter);
assert(workload.getTransactionCount() > 0);
// Generate MarkovGraphs
markovs = MarkovGraphsContainerUtil.createBasePartitionMarkovGraphsContainer(catalogContext, workload, p_estimator);
assertNotNull(markovs);
// Find a single-partition and multi-partition trace
PartitionSet multip_partitions = new PartitionSet();
multip_partitions.add(BASE_PARTITION);
for (TransactionTrace xact : workload.getTransactions()) {
Object ol_supply_w_ids[] = (Object[])xact.getParam(5);
assert(ol_supply_w_ids != null);
boolean same_partition = true;
for (Object i : ol_supply_w_ids) {
Integer partition = p_estimator.getHasher().hash(Integer.valueOf(i.toString()));
same_partition = same_partition && (partition == BASE_PARTITION);
if (same_partition == false && multip_trace == null) {
multip_partitions.add(partition);
}
} // FOR
if (same_partition && singlep_trace == null) singlep_trace = xact;
if (same_partition == false && multip_trace == null) {
multip_trace = xact;
multip_path.addAll(markovs.get(BASE_PARTITION, this.catalog_proc).processTransaction(xact, p_estimator));
}
if (singlep_trace != null && multip_trace != null) break;
} // FOR
assert(multip_partitions.size() > 1);
}
assertNotNull(multip_trace);
assertFalse(multip_path.isEmpty());
assertNotNull(singlep_trace);
// Setup
this.t_estimator = new MarkovEstimator(catalogContext, p_estimator, markovs);
this.thresholds = new EstimationThresholds();
}
/**
* testUnknownPath
*/
@Test
public void testUnknownPath() throws Exception {
// This is to test our ability to handle a transaction with
// a path that we haven't seen before.
// Find the S_W_ID that's different than the base partition, and change
// all of the elements in the array to that value.
TransactionTrace txn_trace = (TransactionTrace)multip_trace.clone();
int w_id = (Integer)txn_trace.getParam(0);
Short s_w_ids[] = (Short[])txn_trace.getParam(5);
short remote_w_id = -1;
assert(s_w_ids.length > 0);
for (int i = 0; i < s_w_ids.length; i++) {
if (w_id != s_w_ids[i]) {
remote_w_id = s_w_ids[i];
break;
}
} // FOR
assert(remote_w_id >= 0);
assertNotSame(w_id, remote_w_id);
Arrays.fill(s_w_ids, remote_w_id);
// System.err.println("S_W_ID: " + Arrays.toString(s_w_ids));
txn_trace.setParam(5, s_w_ids);
MarkovEstimatorState state = t_estimator.startTransaction(XACT_ID.getAndIncrement(),
this.catalog_proc,
txn_trace.getParams());
assertNotNull(state);
// Even though it won't correctly identify the exact path that we're going to
// take (i.e., what partitions the getStockInfo queries will need), the path
// should be complete (i.e., we should see each Statement in NewOrder executed
// at least once)
MarkovEstimate initialEst = state.getInitialEstimate();
// System.err.println("FIRST ESTIMATE:\n" + initialEst);
List<MarkovVertex> initialPath = initialEst.getMarkovPath();
assertFalse(initialPath.isEmpty());
Set<Statement> seenStmts = new HashSet<Statement>();
for (MarkovVertex v : initialPath) {
Statement stmt = v.getCatalogItem();
if ((stmt instanceof StatementWrapper) == false) {
seenStmts.add(stmt);
}
} // FOR
Procedure proc = txn_trace.getCatalogItem(catalogContext.database);
for (Statement stmt : proc.getStatements()) {
assertTrue(stmt.fullName(), seenStmts.contains(stmt));
}
// System.err.println(proc + "\n" + StringUtil.join("\n", proc.getStatements()));
// System.err.println("=======================================");
// System.err.println("SEEN\n" + StringUtil.join("\n", seenStmts));
assertEquals(proc.getStatements().size(), seenStmts.size());
}
/**
* testMultipleStartTransaction
*/
@Test
public void testMultipleStartTransaction() throws Exception {
Set<MarkovEstimatorState> all_states = new HashSet<MarkovEstimatorState>();
int expected = 20;
MarkovEstimate initialEst = null;
for (int i = 0; i < expected; i++) {
MarkovEstimatorState state = t_estimator.startTransaction(XACT_ID.getAndIncrement(),
this.catalog_proc,
multip_trace.getParams());
assertNotNull(state);
assertFalse(all_states.contains(state));
all_states.add(state);
if (initialEst == null) {
initialEst = state.getInitialEstimate();
// System.err.println("FIRST ESTIMATE:\n" + initialEst);
} else {
MarkovEstimate est = state.getInitialEstimate();
// System.err.printf("ESTIMATE #%02d:\n%s", i, est);
assertEquals(initialEst.getTouchedPartitions(thresholds), est.getTouchedPartitions(thresholds));
}
} // FOR
assertEquals(expected, all_states.size());
}
/**
* testStartTransaction
*/
@Test
public void testStartTransaction() throws Exception {
long txn_id = XACT_ID.getAndIncrement();
MarkovEstimatorState state = t_estimator.startTransaction(txn_id, this.catalog_proc, singlep_trace.getParams());
assertNotNull(state);
assertNotNull(state.getLastEstimate());
MarkovEstimate est = state.getInitialEstimate();
System.err.println(est);
assertNotNull(est);
assertTrue(est.toString(), est.isInitialized());
// assertTrue(est.toString(), est.isSinglePartitionProbabilitySet());
assertTrue(est.toString(), est.isAbortProbabilitySet());
assertTrue(est.toString(), est.isConfidenceCoefficientSet());
// assertTrue(est.toString(), est.getConfidenceCoefficient() >= 0f);
// assertEquals(est.toString(), 1.0f, est.getSinglePartitionProbability());
assertEquals(est.toString(), 1.0f, est.getConfidenceCoefficient());
// System.err.println(est.toString());
MarkovGraph markov = state.getMarkovGraph();
List<MarkovVertex> initial_path = est.getMarkovPath();
assertNotNull(initial_path);
assertFalse(initial_path.isEmpty());
System.err.println("# of Vertices: " + markov.getVertexCount());
System.err.println("# of Edges: " + markov.getEdgeCount());
System.err.println("Confidence: " + String.format("%.4f", est.getConfidenceCoefficient()));
System.err.println("\nINITIAL PATH:\n" + StringUtil.join("\n", initial_path));
// System.err.println(multip_trace.debug(catalog_db));
PartitionSet partitions = new PartitionSet();
p_estimator.getAllPartitions(partitions, singlep_trace);
assertNotNull(partitions);
// assert(partitions.size() > 1) : partitions;
System.err.println("partitions: " + partitions);
// GraphvizExport<Vertex, Edge> gv = MarkovUtil.exportGraphviz(markov, false, null);
// gv.highlightPath(markov.getPath(initial_path), "blue");
// gv.writeToTempFile(this.catalog_proc, 0);
//
// MarkovUtil.exportGraphviz(markov, false, markov.getPath(multip_path)).writeToTempFile(this.catalog_proc, 1);
Collection<Integer> est_partitions = est.getTouchedPartitions(thresholds);
assertNotNull(est_partitions);
assertEquals(partitions.size(), est_partitions.size());
assertEquals(partitions, est_partitions);
assert(est.isSinglePartitioned(this.thresholds));
assertTrue(est.isAbortable(this.thresholds));
for (int partition : catalogContext.getAllPartitionIds()) {
if (partitions.contains(partition)) { // == BASE_PARTITION) {
assertFalse("isFinishedPartition(" + partition + ")", est.isDonePartition(thresholds, partition));
assertTrue("isWritePartition(" + partition + ")", est.isWritePartition(thresholds, partition));
assertTrue("isTargetPartition(" + partition + ")", est.isTargetPartition(thresholds, partition));
} else {
assertTrue("isFinishedPartition(" + partition + ")", est.isDonePartition(thresholds, partition));
assertFalse("isWritePartition(" + partition + ")", est.isWritePartition(thresholds, partition));
assertFalse("isTargetPartition(" + partition + ")", est.isTargetPartition(thresholds, partition));
}
} // FOR
}
/**
* testStartTransactionDtxn
*/
@Test
public void testStartTransactionDtxn() throws Exception {
TransactionTrace txn_trace = multip_trace;
long txn_id = XACT_ID.getAndIncrement();
MarkovEstimatorState state = t_estimator.startTransaction(txn_id, this.catalog_proc, txn_trace.getParams());
assertNotNull(state);
assertNotNull(state.getLastEstimate());
MarkovEstimate initialEst = state.getInitialEstimate();
assertNotNull(initialEst);
assertTrue(initialEst.toString(), initialEst.isInitialized());
// assertTrue(initialEst.toString(), initialEst.isSinglePartitionProbabilitySet());
assertTrue(initialEst.toString(), initialEst.isAbortProbabilitySet());
// assertTrue(initialEst.toString(), initialEst.getSinglePartitionProbability() < 1.0f);
assertTrue(initialEst.toString(), initialEst.isConfidenceCoefficientSet());
assertTrue(initialEst.toString(), initialEst.getConfidenceCoefficient() >= 0f);
assertTrue(initialEst.toString(), initialEst.getConfidenceCoefficient() <= 1f);
assertFalse(initialEst.toString(), initialEst.isSinglePartitioned(this.thresholds));
assertTrue(initialEst.toString(), initialEst.isAbortable(this.thresholds));
MarkovGraph markov = state.getMarkovGraph();
List<MarkovVertex> initial_path = initialEst.getMarkovPath();
assertNotNull(initial_path);
assertFalse(initial_path.isEmpty());
System.err.println("# of Vertices: " + markov.getVertexCount());
System.err.println("# of Edges: " + markov.getEdgeCount());
System.err.println("Confidence: " + String.format("%.4f", initialEst.getConfidenceCoefficient()));
System.err.println("\nINITIAL PATH:\n" + StringUtil.join("\n", initial_path));
PartitionSet partitions = new PartitionSet();
p_estimator.getAllPartitions(partitions, txn_trace);
assertNotNull(partitions);
assert(partitions.size() > 1) : partitions;
Collection<Integer> est_partitions = initialEst.getTouchedPartitions(thresholds);
assertNotNull(est_partitions);
assertEquals(partitions.size(), est_partitions.size());
assertEquals(partitions, est_partitions);
}
/**
* testExecuteQueries
*/
@Test
public void testExecuteQueries() throws Exception {
TransactionTrace txn_trace = multip_trace;
long txn_id = XACT_ID.getAndIncrement();
MarkovEstimatorState state = t_estimator.startTransaction(txn_id, this.catalog_proc, txn_trace.getParams());
assertNotNull(state);
assertNotNull(state.getLastEstimate());
MarkovEstimate initialEst = state.getInitialEstimate();
assertNotNull(initialEst);
assertTrue(initialEst.toString(), initialEst.isInitialized());
// assertTrue(initialEst.toString(), initialEst.isSinglePartitionProbabilitySet());
assertTrue(initialEst.toString(), initialEst.isAbortProbabilitySet());
// assertTrue(initialEst.toString(), initialEst.getSinglePartitionProbability() < 1.0f);
assertTrue(initialEst.toString(), initialEst.isConfidenceCoefficientSet());
assertTrue(initialEst.toString(), initialEst.getConfidenceCoefficient() >= 0f);
assertTrue(initialEst.toString(), initialEst.getConfidenceCoefficient() <= 1f);
// Get the list of partitions that we're going to touch in the beginning
// We should never mark these as finished in subsequent estimates
PartitionSet touched = initialEst.getTouchedPartitions(thresholds);
assertFalse(touched.isEmpty());
assertFalse(touched.contains(HStoreConstants.NULL_PARTITION_ID));
System.err.println("TOUCHED: " + touched);
assertFalse(touched.toString(), touched.size() == 1);
// Execute a bunch of batches
// All should say that the txn is not finished with the partitions until we
// get to the one that contains the updateStock queries, which should be the last.
Statement lastBatchStmt = this.getStatement(this.catalog_proc, "updateStock");
for (int i = 0, cnt = txn_trace.getBatchCount(); i < cnt; i++) {
List<QueryTrace> queries = txn_trace.getBatchQueries(i);
assertFalse(queries.isEmpty());
boolean is_last = (i+1 == cnt);
Statement stmts[] = new Statement[queries.size()];
PartitionSet partitions[] = new PartitionSet[queries.size()];
boolean found = false;
int idx = 0;
for (QueryTrace q : queries) {
stmts[idx] = q.getCatalogItem(catalogContext.database);
assertNotNull(stmts[idx]);
partitions[idx] = new PartitionSet();
p_estimator.getAllPartitions(partitions[idx], q, state.getBasePartition());
assertFalse(partitions[idx].isEmpty());
assertFalse(partitions[idx].contains(HStoreConstants.NULL_PARTITION_ID));
found = found || stmts[idx].equals(lastBatchStmt);
idx++;
} // FOR
if (is_last) assertTrue(StringUtil.join("\n", queries), found);
MarkovEstimate est = t_estimator.executeQueries(state, stmts, partitions);
assertNotNull(est);
for (int partition : catalogContext.getAllPartitionIds()) {
String debug = String.format("Batch %02d / Partition %02d / isLast=%s\n%s",
est.getBatchId(), partition, is_last, est.toString());
assertTrue(debug, est.isDoneProbabilitySet(partition));
assertTrue(debug, est.isWriteProbabilitySet(partition));
// assertTrue(debug, est.isReadOnlyProbabilitySet(partition));
if (touched.contains(partition) && is_last == false) { // || partition == state.getBasePartition())) {
assertFalse(debug, est.isDonePartition(thresholds, partition));
assertTrue(debug, est.isWritePartition(thresholds, partition));
} else {
assertTrue(debug, est.isDonePartition(thresholds, partition));
}
} // FOR
} // FOR
}
/**
* testFastPath
*/
@Test
public void testFastPath() throws Exception {
TransactionTrace txn_trace = multip_trace;
int base_partition = p_estimator.getBasePartition(txn_trace);
List<QueryTrace> queries = txn_trace.getBatchQueries(1);
assertFalse(queries.isEmpty());
Statement stmts[] = new Statement[queries.size()];
PartitionSet partitions[] = new PartitionSet[queries.size()];
int idx = 0;
for (QueryTrace q : queries) {
stmts[idx] = q.getCatalogItem(catalogContext.database);
assertNotNull(stmts[idx]);
partitions[idx] = new PartitionSet();
p_estimator.getAllPartitions(partitions[idx], q, base_partition);
idx++;
} // FOR
MarkovEstimatorState states[] = new MarkovEstimatorState[2];
MarkovEstimate ests[] = new MarkovEstimate[states.length];
for (int i = 0; i < states.length; i++) {
HStoreConf.singleton().site.markov_fast_path = (i != 0);
states[i] = t_estimator.startTransaction(XACT_ID.getAndIncrement(), this.catalog_proc, txn_trace.getParams());
assertNotNull(states[i]);
assertNotNull(states[i].getLastEstimate());
ests[i] = t_estimator.executeQueries(states[i], stmts, partitions);
assertNotNull(ests[i]);
} // FOR
// Now compare that estimates look reasonable the same
for (int i = 0; i < states.length; i++) {
for (int ii = i+1; ii < states.length; ii++) {
assertEquals(states[i].getBasePartition(), states[ii].getBasePartition());
assertEquals(ests[i].getTouchedPartitions(thresholds), ests[ii].getTouchedPartitions(thresholds));
assertEquals(ests[i].getBatchId(), ests[ii].getBatchId());
// assertEquals(ests[i].getSinglePartitionProbability(), ests[ii].getSinglePartitionProbability());
assertEquals(ests[i].getAbortProbability(), ests[ii].getAbortProbability());
for (Integer partition : catalogContext.getAllPartitionIds()) {
String debug = String.format("Batch %02d / Partition %02d\n%s",
ests[i].getBatchId(), partition, ests[i].toString());
assertEquals(debug, ests[i].getTouchedCounter(partition), ests[ii].getTouchedCounter(partition));
assertEquals(debug, ests[i].isDoneProbabilitySet(partition), ests[ii].isDoneProbabilitySet(partition));
assertEquals(debug, ests[i].isWriteProbabilitySet(partition), ests[ii].isWriteProbabilitySet(partition));
// assertEquals(debug, ests[i].isReadOnlyProbabilitySet(partition), ests[ii].isReadOnlyProbabilitySet(partition));
assertEquals(debug, ests[i].isDonePartition(thresholds, partition), ests[ii].isDonePartition(thresholds, partition));
assertEquals(debug, ests[i].isWritePartition(thresholds, partition), ests[ii].isWritePartition(thresholds, partition));
assertEquals(debug, ests[i].isReadOnlyPartition(thresholds, partition), ests[ii].isReadOnlyPartition(thresholds, partition));
} // FOR
} // FOR
}
}
/**
* testProcessTransactionTrace
*/
@Test
public void testProcessTransactionTrace() throws Exception {
TransactionTrace txn_trace = singlep_trace;
assertNotNull(txn_trace);
MarkovEstimatorState s = this.t_estimator.processTransactionTrace(txn_trace);
assertNotNull(s);
MarkovEstimate initialEst = s.getInitialEstimate();
assertNotNull(initialEst);
assertTrue(initialEst.toString(), initialEst.isInitialized());
// assertTrue(initialEst.toString(), initialEst.isSinglePartitionProbabilitySet());
assertTrue(initialEst.toString(), initialEst.isAbortProbabilitySet());
// assertTrue(initialEst.toString(), initialEst.getSinglePartitionProbability() < 1.0f);
assertTrue(initialEst.toString(), initialEst.isConfidenceCoefficientSet());
assertTrue(initialEst.toString(), initialEst.getConfidenceCoefficient() >= 0f);
assertTrue(initialEst.toString(), initialEst.getConfidenceCoefficient() <= 1f);
assertTrue(initialEst.toString(), initialEst.getMarkovPath().isEmpty() == false);
// We should have an MarkovEstimate for each batch
assertEquals(txn_trace.getBatchCount(), s.getEstimateCount());
List<Estimate> estimates = s.getEstimates();
for (int i = 0, cnt = txn_trace.getBatchCount(); i < cnt; i++) {
List<QueryTrace> queries = txn_trace.getBatchQueries(i);
assertFalse(queries.isEmpty());
MarkovEstimate est = (MarkovEstimate)estimates.get(i);
assertNotSame(initialEst, est);
assertNotNull(est);
// assertTrue(est.toString(), est.isSinglePartitionProbabilitySet());
assertTrue(est.toString(), est.isAbortProbabilitySet());
assertTrue(est.toString(), est.isSinglePartitioned(thresholds));
assertTrue(est.toString(), est.isConfidenceCoefficientSet());
assertTrue(est.toString(), est.getConfidenceCoefficient() >= 0f);
assertTrue(est.toString(), est.getConfidenceCoefficient() <= 1f);
assertTrue(est.toString(), est.getMarkovPath().isEmpty() == false);
// The last vertex in each MarkovEstimate should correspond to the last query in each batch
MarkovVertex last_v = est.getVertex();
assertNotNull(last_v);
System.err.println("LAST VERTEX: " + last_v);
assertEquals(CollectionUtil.last(queries).getCatalogItem(catalogContext.database), last_v.getCatalogItem());
} // FOR
}
}