/** * */ package edu.brown.hstore.specexec.checkers; import java.util.Collection; import java.util.HashMap; import java.util.Map; import org.junit.Test; import org.voltdb.VoltTable; import org.voltdb.benchmark.tpcc.procedures.neworder; import org.voltdb.catalog.Procedure; import org.voltdb.catalog.Table; import edu.brown.BaseTestCase; import edu.brown.catalog.CatalogUtil; import edu.brown.hstore.HStoreSite; import edu.brown.hstore.MockHStoreSite; import edu.brown.hstore.TestReadWriteTracking; import edu.brown.hstore.conf.HStoreConf; import edu.brown.hstore.txns.AbstractTransaction; import edu.brown.hstore.txns.LocalTransaction; import edu.brown.utils.CollectionUtil; import edu.brown.utils.ProjectType; /** * OCC Conflict Checker * @author pavlo */ public class TestOptimisticConflictChecker extends BaseTestCase { private static final int NUM_PARTITONS = 2; private static final int BASE_PARTITION = 0; private static long NEXT_TXN_ID = 1000; private HStoreSite hstore_site; private OptimisticConflictChecker checker; private Map<AbstractTransaction, VoltTable[]> readWriteSets = new HashMap<AbstractTransaction, VoltTable[]>(); @Override protected void setUp() throws Exception { super.setUp(ProjectType.TPCC); this.addPartitions(NUM_PARTITONS); this.checker = new OptimisticConflictChecker(catalogContext, null) { @Override protected VoltTable[] getReadWriteSets(AbstractTransaction ts) { return readWriteSets.get(ts); } }; this.hstore_site = new MockHStoreSite(0, catalogContext, HStoreConf.singleton()); } // -------------------------------------------------------------------------------------------- // UTILITY METHODS // -------------------------------------------------------------------------------------------- private void updateTracking(AbstractTransaction ts, Table catalog_tbl, boolean isRead, int...tupleIds) { VoltTable vts[] = this.readWriteSets.get(ts); if (vts == null) { vts = new VoltTable[]{ new VoltTable(TestReadWriteTracking.RESULT_COLS), new VoltTable(TestReadWriteTracking.RESULT_COLS) }; this.readWriteSets.put(ts, vts); } VoltTable vt = null; if (isRead) { vt = vts[OptimisticConflictChecker.READ]; ts.markTableRead(BASE_PARTITION, catalog_tbl); } else { vt = vts[OptimisticConflictChecker.WRITE]; ts.markTableWritten(BASE_PARTITION, catalog_tbl); } for (int tupleId : tupleIds) { vt.addRow(catalog_tbl.getName(), tupleId); } // FOR } private void addReads(AbstractTransaction ts, Table tbl, int...tupleIds) { this.updateTracking(ts, tbl, true, tupleIds); } private void addWrites(AbstractTransaction ts, Table tbl, int...tupleIds) { this.updateTracking(ts, tbl, false, tupleIds); } // -------------------------------------------------------------------------------------------- // TEST CASES // -------------------------------------------------------------------------------------------- /** * testReadWriteConflicts */ @Test public void testReadWriteConflicts() throws Exception { Procedure proc = this.getProcedure(neworder.class); Collection<Table> tables = CatalogUtil.getReferencedTables(proc); LocalTransaction ts0 = new LocalTransaction(this.hstore_site); ts0.testInit(NEXT_TXN_ID++, BASE_PARTITION, catalogContext.getPartitionSetSingleton(BASE_PARTITION), proc, new Object[0]); LocalTransaction ts1 = new LocalTransaction(this.hstore_site); ts1.testInit(NEXT_TXN_ID++, BASE_PARTITION, catalogContext.getPartitionSetSingleton(BASE_PARTITION), proc, new Object[0]); // Let both txns read all of the tables referenced in the procedure // The checker should say that there isn't a conflict for (Table tbl : tables) { this.addReads(ts0, tbl, 1111); this.addReads(ts1, tbl, 1111); } assertFalse(this.checker.hasConflictBefore(ts0, ts1, BASE_PARTITION)); assertFalse(this.checker.hasConflictAfter(ts0, ts1, BASE_PARTITION)); // Now if we have the second txn write to the tables, but target different // tupleIds, then it still should say that there isn't a conflict. for (Table tbl : tables) { this.addReads(ts1, tbl, 2222); } assertFalse(this.checker.hasConflictBefore(ts0, ts1, BASE_PARTITION)); assertFalse(this.checker.hasConflictAfter(ts0, ts1, BASE_PARTITION)); // Finally, we'll pick a random table to have the second txn write to. // That should get the checker to mark them as conflicting Table tbl = CollectionUtil.random(tables); assertNotNull(tbl); this.addWrites(ts1, tbl, 1111); assertFalse(this.checker.hasConflictBefore(ts0, ts1, BASE_PARTITION)); assertTrue(this.checker.hasConflictAfter(ts0, ts1, BASE_PARTITION)); } /** * testWriteWriteConflicts */ @Test public void testWriteWriteConflicts() throws Exception { Procedure proc = this.getProcedure(neworder.class); Collection<Table> tables = CatalogUtil.getReferencedTables(proc); LocalTransaction ts0 = new LocalTransaction(this.hstore_site); ts0.testInit(NEXT_TXN_ID++, BASE_PARTITION, catalogContext.getPartitionSetSingleton(BASE_PARTITION), proc, new Object[0]); LocalTransaction ts1 = new LocalTransaction(this.hstore_site); ts1.testInit(NEXT_TXN_ID++, BASE_PARTITION, catalogContext.getPartitionSetSingleton(BASE_PARTITION), proc, new Object[0]); // Let both the txns write to different tuples at the same tables // The checker should say that there isn't a conflict for (Table tbl : tables) { this.addWrites(ts0, tbl, 1111); this.addWrites(ts1, tbl, 2222); } assertFalse(this.checker.hasConflictBefore(ts0, ts1, BASE_PARTITION)); assertFalse(this.checker.hasConflictAfter(ts0, ts1, BASE_PARTITION)); // Finally, we'll pick a random table to have the second txn write to. // That should get the checker to mark them as conflicting Table tbl = CollectionUtil.random(tables); assertNotNull(tbl); this.addWrites(ts1, tbl, 1111); assertFalse(this.checker.hasConflictBefore(ts0, ts1, BASE_PARTITION)); assertTrue(this.checker.hasConflictAfter(ts0, ts1, BASE_PARTITION)); } }