package edu.brown.costmodel;
import java.io.File;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.junit.Before;
import org.junit.Test;
import org.voltdb.VoltProcedure;
import org.voltdb.benchmark.tpcc.procedures.neworder;
import org.voltdb.catalog.CatalogType;
import org.voltdb.catalog.Procedure;
import org.voltdb.catalog.Statement;
import edu.brown.BaseTestCase;
import edu.brown.costmodel.MarkovCostModel.Penalty;
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.MarkovVertex;
import edu.brown.markov.MarkovVertex.Type;
import edu.brown.markov.containers.MarkovGraphsContainerUtil;
import edu.brown.markov.containers.MarkovGraphsContainer;
import edu.brown.utils.CollectionUtil;
import edu.brown.utils.PartitionSet;
import edu.brown.utils.ProjectType;
import edu.brown.workload.AbstractTraceElement;
import edu.brown.workload.TransactionTrace;
import edu.brown.workload.Workload;
import edu.brown.workload.filters.BasePartitionTxnFilter;
import edu.brown.workload.filters.Filter;
import edu.brown.workload.filters.MultiPartitionTxnFilter;
import edu.brown.workload.filters.ProcParameterValueFilter;
import edu.brown.workload.filters.ProcedureLimitFilter;
import edu.brown.workload.filters.ProcedureNameFilter;
public class TestMarkovCostModel extends BaseTestCase {
private static final Class<? extends VoltProcedure> TARGET_PROCEDURE = neworder.class;
private static final int WORKLOAD_XACT_LIMIT = 500;
private static final int BASE_PARTITION = 1;
private static final int NUM_PARTITIONS = 5;
private static final EstimationThresholds thresholds = new EstimationThresholds();
private static Workload workload;
private static MarkovGraphsContainer markovs;
private static Procedure catalog_proc;
private static MarkovEstimator t_estimator;
private MarkovCostModel costmodel;
private MarkovGraph markov;
private TransactionTrace txn_trace;
private MarkovEstimatorState txn_state;
private MarkovEstimate initialEst;
private List<MarkovVertex> estimated_path;
private List<MarkovVertex> actual_path;
@Before
public void setUp() throws Exception {
super.setUp(ProjectType.TPCC);
this.addPartitions(NUM_PARTITIONS);
if (isFirstSetup()) {
catalog_proc = this.getProcedure(TARGET_PROCEDURE);
File file = this.getWorkloadFile(ProjectType.TPCC);
workload = new Workload(catalogContext.catalog);
// Check out this beauty:
// (1) Filter by procedure name
// (2) Filter on partitions that start on our BASE_PARTITION
// (3) Filter to only include multi-partition txns
// (4) Another limit to stop after allowing ### txns
// Where is your god now???
Filter filter = new ProcedureNameFilter(false)
.include(TARGET_PROCEDURE.getSimpleName())
.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 MultiPartitionTxnFilter(p_estimator, false))
.attach(new ProcedureLimitFilter(WORKLOAD_XACT_LIMIT));
workload.load(file, catalogContext.database, filter);
// Make a copy that doesn't have the first TransactionTrace
Workload clone = new Workload(catalogContext.catalog, new Filter() {
private boolean first = true;
@Override
protected FilterResult filter(AbstractTraceElement<? extends CatalogType> element) {
if (element instanceof TransactionTrace && first) {
this.first = false;
return (FilterResult.SKIP);
}
return FilterResult.ALLOW;
}
@Override
public String debugImpl() { return null; }
@Override
protected void resetImpl() { }
}, workload);
TransactionTrace txn0 = CollectionUtil.first(workload.getTransactions());
assertNotNull(txn0);
TransactionTrace txn1 = CollectionUtil.first(clone.getTransactions());
assertNotNull(txn1);
assert(txn0.getTransactionId() != txn1.getTransactionId());
// assertEquals(WORKLOAD_XACT_LIMIT, workload.getTransactionCount());
// for (TransactionTrace xact : workload.getTransactions()) {
// System.err.println(xact + ": " + p_estimator.getAllPartitions(xact));
// }
// Generate MarkovGraphs per base partition
// file = this.getMarkovFile(ProjectType.TPCC);
// markovs = MarkovUtil.load(catalogContext.database, file.getAbsolutePath());
markovs = MarkovGraphsContainerUtil.createBasePartitionMarkovGraphsContainer(catalogContext, clone, p_estimator);
assertNotNull(markovs);
// And then populate the MarkovCostModel
t_estimator = new MarkovEstimator(catalogContext, p_estimator, markovs);
}
this.costmodel = new MarkovCostModel(catalogContext, p_estimator, t_estimator, thresholds);
// Take a TransactionTrace and throw it at the estimator to get our path info
this.txn_trace = CollectionUtil.first(workload.getTransactions());
assertNotNull(this.txn_trace);
this.txn_state = t_estimator.processTransactionTrace(txn_trace);
assertNotNull(this.txn_state);
this.markov = markovs.get(BASE_PARTITION, catalog_proc);
assertNotNull(this.markov);
this.initialEst = this.txn_state.getInitialEstimate();
assertNotNull(this.initialEst);
this.estimated_path = this.initialEst.getMarkovPath();
assertNotNull(this.estimated_path);
assert(this.estimated_path.isEmpty() == false);
this.actual_path = this.txn_state.getActualPath();
assertNotNull(this.actual_path);
assert(this.actual_path.isEmpty() == false);
}
/**
* testComparePathsFast
*/
@Test
public void testComparePathsFast() throws Exception {
List<MarkovVertex> clone = new ArrayList<MarkovVertex>(this.actual_path);
// At the very least the exact clone should be equal
assert(costmodel.comparePathsFast(clone, this.actual_path));
// Test to make sure that it catches when one path aborts while the other commits
this.actual_path.add(markov.getCommitVertex());
clone.add(markov.getCommitVertex());
assert(costmodel.comparePathsFast(clone, this.actual_path));
clone.set(clone.size()-1, markov.getAbortVertex());
assertFalse(costmodel.comparePathsFast(clone, this.actual_path));
this.actual_path.remove(this.actual_path.size()-1);
// Now check to make sure that it catches when the read/write differ
clone.get(1).getPartitions().add(Integer.MAX_VALUE);
assertEquals(false, costmodel.comparePathsFast(clone, this.actual_path));
clone.get(1).getPartitions().remove(Integer.MAX_VALUE);
}
/**
* testComparePathsFull_Penalty1
*/
@Test
public void testComparePathsFull_Penalty1() throws Exception {
// Grab the actual path in the State and cut it off after the first
// invocation of neworder.getCustomer().
Procedure catalog_proc = this.getProcedure(TARGET_PROCEDURE);
Statement catalog_stmt = this.getStatement(catalog_proc, "getCustomer");
List<MarkovVertex> actual = this.txn_state.getActualPath();
List<MarkovVertex> orig = new ArrayList<MarkovVertex>(actual);
actual.clear();
for (MarkovVertex mv : orig) {
actual.add(mv);
if (mv.getCatalogItemName().equalsIgnoreCase(catalog_stmt.getName())) {
break;
}
} // FOR
assertNotSame(orig.size(), actual.size());
MarkovVertex abort_v = markov.getAbortVertex();
assertEquals(Type.ABORT, abort_v.getType());
actual.add(abort_v);
// We have to call comparePathsFast first to setup some sets
// We don't care what the outcome is here...
this.costmodel.comparePathsFast(this.estimated_path, actual);
// System.err.println(StringUtil.join("\n", actual));
double cost = costmodel.comparePathsFull(this.txn_state);
List<Penalty> penalties = costmodel.getLastPenalties();
assertNotNull(penalties);
System.err.println(String.format("COST=%.03f PENALTIES=%s", cost, penalties));
// assert(penalties.contains(Penalty.UNUSED_READ_PARTITION_MULTI) ||
// penalties.contains(Penalty.UNUSED_WRITE_PARTITION_MULTI));
}
/**
* testComparePathsFull_Penalty2
*/
@Test
public void testComparePathsFull_Penalty2() throws Exception {
// We have to call comparePathsFast first to setup some sets
// We don't care what the outcome is here...
this.costmodel.comparePathsFast(this.estimated_path, this.txn_state.getActualPath());
// Remove all of the estimated read partitions except for one
PartitionSet e_read_partitions = this.costmodel.getLastEstimatedReadPartitions();
assertNotNull(e_read_partitions);
Set<Integer> retain = (Set<Integer>)CollectionUtil.addAll(new HashSet<Integer>(), CollectionUtil.first(e_read_partitions));
e_read_partitions.retainAll(retain);
// Then add all of our partitions to the actual read partitions
PartitionSet a_read_partitions = this.costmodel.getLastActualReadPartitions();
a_read_partitions.addAll(catalogContext.getAllPartitionIds());
double cost = this.costmodel.comparePathsFull(this.txn_state);
List<Penalty> penalties = this.costmodel.getLastPenalties();
assertNotNull(penalties);
System.err.println(String.format("COST=%.03f PENALTIES=%s", cost, penalties));
assert(penalties.contains(Penalty.MISSED_READ_PARTITION));
}
/**
* testCompareIncompletePath
*/
@Test
public void testCompareIncompletePath() throws Exception {
// Then make sure that our cost model can handle paths where the estimated path isn't complete
List<MarkovVertex> tester = new ArrayList<MarkovVertex>(this.actual_path);
tester.removeAll(this.actual_path.subList(this.actual_path.size() - 5, this.actual_path.size()));
/* FIXME
double cost = costmodel.comparePathsFast(tester, this.actual_path);
*/
// assert(cost > 0);
}
}