/** * */ package edu.brown.hstore; import java.util.HashSet; import java.util.Set; import java.util.concurrent.TimeUnit; import org.junit.Test; import org.voltdb.StoredProcedureInvocationHints; import org.voltdb.VoltSystemProcedure; import org.voltdb.VoltTable; import org.voltdb.VoltType; import org.voltdb.catalog.Procedure; import org.voltdb.catalog.Site; import org.voltdb.catalog.Table; import org.voltdb.client.Client; import org.voltdb.client.ClientResponse; import org.voltdb.jni.ExecutionEngine; import org.voltdb.regressionsuites.TestSmallBankSuite; import org.voltdb.regressionsuites.specexecprocs.BlockingSendPayment; import org.voltdb.sysprocs.AdHoc; import org.voltdb.utils.VoltTableUtil; import edu.brown.BaseTestCase; import edu.brown.HStoreSiteTestUtil; import edu.brown.HStoreSiteTestUtil.LatchableProcedureCallback; import edu.brown.benchmark.smallbank.SmallBankConstants; import edu.brown.benchmark.smallbank.SmallBankProjectBuilder; import edu.brown.hstore.Hstoreservice.Status; import edu.brown.hstore.conf.HStoreConf; import edu.brown.hstore.txns.LocalTransaction; import edu.brown.utils.CollectionUtil; /** * Read/Write Set Tracking * @author pavlo */ public class TestReadWriteTracking extends BaseTestCase { private static final int NUM_PARTITONS = 1; private static final int BASE_PARTITION = 0; public static final VoltTable.ColumnInfo[] RESULT_COLS = { new VoltTable.ColumnInfo("TABLE_NAME", VoltType.STRING), new VoltTable.ColumnInfo("TUPLE_ID", VoltType.INTEGER), }; private HStoreSite hstore_site; private HStoreConf hstore_conf; private Client client; private PartitionExecutor executor; private ExecutionEngine ee; private Procedure blockingProc; private final SmallBankProjectBuilder builder = new SmallBankProjectBuilder() { { this.addAllDefaults(); this.addProcedure(BlockingSendPayment.class); } }; @Override protected void setUp() throws Exception { super.setUp(builder); this.addPartitions(NUM_PARTITONS); this.blockingProc = this.getProcedure(BlockingSendPayment.class); this.hstore_conf = HStoreConf.singleton(); this.hstore_conf.site.exec_readwrite_tracking = true; Site catalog_site = CollectionUtil.first(catalogContext.sites); this.hstore_site = this.createHStoreSite(catalog_site, hstore_conf); this.executor = hstore_site.getPartitionExecutor(0); assertNotNull(this.executor); this.ee = executor.getExecutionEngine(); assertNotNull(this.executor); this.client = createClient(); } @Override protected void tearDown() throws Exception { if (this.client != null) this.client.close(); if (this.hstore_site != null) this.hstore_site.shutdown(); } // -------------------------------------------------------------------------------------------- // UTILITY METHODS // -------------------------------------------------------------------------------------------- private BlockingSendPayment startTxn() throws Exception { // Fire off a distributed a txn that will block. Object dtxnParams[] = new Object[]{ BASE_PARTITION, BASE_PARTITION+1, 1.0 }; StoredProcedureInvocationHints dtxnHints = new StoredProcedureInvocationHints(); dtxnHints.basePartition = BASE_PARTITION; LatchableProcedureCallback dtxnCallback = new LatchableProcedureCallback(1); this.client.callProcedure(dtxnCallback, this.blockingProc.getName(), dtxnHints, dtxnParams); // Block until we know that the txn has started running BlockingSendPayment voltProc = HStoreSiteTestUtil.getCurrentVoltProcedure(this.executor, BlockingSendPayment.class); assertNotNull(voltProc); boolean result = voltProc.NOTIFY_BEFORE.tryAcquire(HStoreSiteTestUtil.NOTIFY_TIMEOUT, TimeUnit.MILLISECONDS); assertTrue(result); // Ok now we're going to release our txn. It will execute a bunch of stuff. voltProc.LOCK_BEFORE.release(); result = voltProc.NOTIFY_AFTER.tryAcquire(HStoreSiteTestUtil.NOTIFY_TIMEOUT, TimeUnit.MILLISECONDS); assertTrue(result); return (voltProc); } private void finishTxn(BlockingSendPayment voltProc) throws Exception { // Check to make sure that the dtxn succeeded voltProc.LOCK_AFTER.release(); // VoltTable result[] = dtxnCallback.latch.await(NOTIFY_TIMEOUT, TimeUnit.MILLISECONDS); // assertTrue("DTXN LATCH"+dtxnCallback.latch, result); // assertEquals(Status.OK, CollectionUtil.first(dtxnCallback.responses).getStatus()); } private void verifySchema(VoltTable vt) { assertEquals(RESULT_COLS.length, vt.getColumnCount()); for (int i = 0; i < vt.getColumnCount(); i++) { assertEquals(Integer.toString(i), RESULT_COLS[i].getName(), vt.getColumnName(i)); assertEquals(Integer.toString(i), RESULT_COLS[i].getType(), vt.getColumnType(i)); } // FOR } // -------------------------------------------------------------------------------------------- // TEST CASES // -------------------------------------------------------------------------------------------- /** * testCaching */ @Test public void testCaching() throws Exception { TestSmallBankSuite.initializeSmallBankDatabase(catalogContext, this.client); BlockingSendPayment voltProc = this.startTxn(); Long txnId = voltProc.getTransactionId(); assertNotNull(txnId); LocalTransaction ts = hstore_site.getTransaction(txnId); assertNotNull(ts); Set<String> expectedTables = new HashSet<String>(); for (Table tbl : catalogContext.database.getTables()) { if (ts.isTableRead(BASE_PARTITION, tbl)) { expectedTables.add(tbl.getName()); } } // FOR // We should always get back the same handle each time. try { VoltTable result0 = this.ee.trackingReadSet(txnId); VoltTable result1 = this.ee.trackingReadSet(txnId); assert(result0 == result1); } finally { this.finishTxn(voltProc); } } /** * testReadSetsAdHoc */ @Test public void testReadSetsAdHoc() throws Exception { TestSmallBankSuite.initializeSmallBankDatabase(catalogContext, this.client); String sql = String.format("SELECT * FROM %s WHERE custid = 1", SmallBankConstants.TABLENAME_ACCOUNTS); String procName = VoltSystemProcedure.procCallName(AdHoc.class); ClientResponse cresponse = client.callProcedure(procName, sql); assert(cresponse.getStatus() == Status.OK) : cresponse.toString(); // XXX: We currently have no way of checking the read/write set // for adhoc queries, so I just have this for checking manually. } /** * testReadSets */ @Test public void testReadSets() throws Exception { TestSmallBankSuite.initializeSmallBankDatabase(catalogContext, this.client); BlockingSendPayment voltProc = this.startTxn(); Long txnId = voltProc.getTransactionId(); assertNotNull(txnId); LocalTransaction ts = hstore_site.getTransaction(txnId); assertNotNull(ts); Set<String> expectedTables = new HashSet<String>(); for (Table tbl : catalogContext.database.getTables()) { if (ts.isTableRead(BASE_PARTITION, tbl)) { expectedTables.add(tbl.getName()); } } // FOR // Let's take a peek at its ReadSet VoltTable result = this.ee.trackingReadSet(txnId); assertNotNull(result); this.verifySchema(result); Set<String> foundTables = new HashSet<String>(); while (result.advanceRow()) { String tableName = result.getString(0); int tupleId = (int)result.getLong(1); foundTables.add(tableName); assert(tupleId >= 0); } // WHILE this.finishTxn(voltProc); System.err.println("READ SET:\n" + VoltTableUtil.format(result)); assertEquals(expectedTables, foundTables); } /** * testWriteSets */ @Test public void testWriteSets() throws Exception { TestSmallBankSuite.initializeSmallBankDatabase(catalogContext, this.client); BlockingSendPayment voltProc = this.startTxn(); Long txnId = voltProc.getTransactionId(); assertNotNull(txnId); LocalTransaction ts = hstore_site.getTransaction(txnId); assertNotNull(ts); Set<String> expectedTables = new HashSet<String>(); for (Table tbl : catalogContext.database.getTables()) { if (ts.isTableWritten(BASE_PARTITION, tbl)) { expectedTables.add(tbl.getName()); } } // FOR // Let's take a peek at its WriteSet VoltTable result = this.ee.trackingWriteSet(txnId); assertNotNull(result); this.verifySchema(result); Set<String> foundTables = new HashSet<String>(); while (result.advanceRow()) { String tableName = result.getString(0); int tupleId = (int)result.getLong(1); foundTables.add(tableName); assert(tupleId >= 0); } // WHILE this.finishTxn(voltProc); System.err.println("WRITE SET:\n" + VoltTableUtil.format(result)); assertEquals(expectedTables, foundTables); } /** * testEnableTracking */ @Test public void testEnableTracking() throws Exception { Long txnId = 12345l; this.ee.trackingEnable(txnId); } /** * testFinishTracking */ @Test public void testFinishTracking() throws Exception { Long txnId = 12345l; this.ee.trackingEnable(txnId); this.ee.trackingFinish(txnId); } }