Package edu.brown.hstore.txns

Source Code of edu.brown.hstore.txns.TestTransactionState

/**
*
*/
package edu.brown.hstore.txns;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CountDownLatch;

import org.apache.commons.collections15.set.ListOrderedSet;
import org.apache.log4j.Logger;
import org.junit.Test;

import edu.brown.benchmark.tm1.procedures.UpdateLocation;
import edu.brown.hstore.PartitionExecutor;
import org.voltdb.ParameterSet;
import org.voltdb.SQLStmt;
import org.voltdb.VoltProcedure;
import org.voltdb.VoltTable;
import org.voltdb.VoltType;
import org.voltdb.catalog.Partition;
import org.voltdb.catalog.Procedure;
import org.voltdb.catalog.Site;
import org.voltdb.catalog.Statement;

import edu.brown.BaseTestCase;
import edu.brown.hstore.Hstoreservice.WorkFragment;
import edu.brown.hstore.conf.HStoreConf;
import edu.brown.hstore.txns.AbstractTransaction;
import edu.brown.hstore.txns.DependencyInfo;
import edu.brown.hstore.txns.LocalTransaction;
import edu.brown.statistics.FastIntHistogram;
import edu.brown.utils.PartitionEstimator;
import edu.brown.utils.ProjectType;
import edu.brown.hstore.BatchPlanner;
import edu.brown.hstore.BatchPlanner.BatchPlan;
import edu.brown.hstore.HStore;
import edu.brown.hstore.HStoreConstants;
import edu.brown.hstore.HStoreSite;
import edu.brown.hstore.MockPartitionExecutor;

/**
* @author pavlo
*/
public class TestTransactionState extends BaseTestCase {
    private static final Logger LOG = Logger.getLogger(TestTransactionState.class);

    private static final Long TXN_ID = 1000l;
    private static final long UNDO_TOKEN = 10l;
   
    private static final Class<? extends VoltProcedure> TARGET_PROCEDURE = UpdateLocation.class;
    private static final String TARGET_STATEMENT = "update";
    private static final int NUM_DUPLICATE_STATEMENTS = 3;
   
    private static final int NUM_PARTITIONS = 4;
    private static final int NUM_EXPECTED_DEPENDENCIES = (NUM_PARTITIONS + 1) * NUM_DUPLICATE_STATEMENTS;
    private static final int LOCAL_PARTITION = 0;

    private static final VoltTable.ColumnInfo FAKE_RESULTS_COLUMNS[] = new VoltTable.ColumnInfo[] {
        new VoltTable.ColumnInfo("ID", VoltType.INTEGER),
        new VoltTable.ColumnInfo("VAL", VoltType.STRING),
    };
    private static final VoltTable FAKE_RESULT = new VoltTable(FAKE_RESULTS_COLUMNS);
   
    private HStoreSite hstore_site;
    private PartitionExecutor executor;
    private BatchPlan plan;
    private List<WorkFragment.Builder> ftasks = new ArrayList<WorkFragment.Builder>();
    private DependencyTracker depTracker;
    private DependencyTracker.Debug depTrackerDbg;
   
    private LocalTransaction ts;
    private ListOrderedSet<Integer> dependency_ids = new ListOrderedSet<Integer>();
    private List<Integer> internal_dependency_ids = new ArrayList<Integer>();
    private List<Integer> output_dependency_ids = new ArrayList<Integer>();
    private FastIntHistogram touched_partitions = new FastIntHistogram();
   
    private SQLStmt batch[] = new SQLStmt[NUM_DUPLICATE_STATEMENTS];
    private ParameterSet params[] = new ParameterSet[batch.length];
    private int stmtCounters[] = new int[batch.length];
   
    @Override
    protected void setUp() throws Exception {
        super.setUp(ProjectType.TM1);
        this.addPartitions(NUM_PARTITIONS);
       
        PartitionEstimator p_estimator = new PartitionEstimator(catalogContext);
        this.executor = new MockPartitionExecutor(LOCAL_PARTITION, catalogContext, p_estimator);
        assertNotNull(this.executor);
       
        // Setup a BatchPlanner for ourselves here
        Procedure catalog_proc = this.getProcedure(TARGET_PROCEDURE);
        assertNotNull(catalog_proc);
        Statement catalog_stmt = this.getStatement(catalog_proc, TARGET_STATEMENT);
        assertNotNull(catalog_stmt);

        // Create a SQLStmt batch
        for (int i = 0; i < batch.length; i++) {
            Object raw_args[] = new Object[] {
                new Long(i + 1),    // VLR_LOCATION
                new String("XXX")// SUB_NBR
            };
            this.batch[i] = new SQLStmt(catalog_stmt, catalog_stmt.getMs_fragments());
            this.params[i] = VoltProcedure.getCleanParams(batch[i], raw_args);
            this.stmtCounters[i] = i;
        } // FOR
    
        Partition catalog_part = catalogContext.getPartitionById(LOCAL_PARTITION);
        assertNotNull(catalog_part);
        this.hstore_site = HStore.initialize(catalogContext, ((Site)catalog_part.getParent()).getId(), HStoreConf.singleton());
        this.hstore_site.addPartitionExecutor(LOCAL_PARTITION, executor);
        this.depTracker = hstore_site.getDependencyTracker(LOCAL_PARTITION);
        this.depTrackerDbg = this.depTracker.getDebugContext();
       
        BatchPlanner planner = new BatchPlanner(batch, catalog_proc, p_estimator);
        this.plan = planner.plan(TXN_ID,
                                 LOCAL_PARTITION,
                                 catalogContext.getAllPartitionIds(),
                                 this.touched_partitions,
                                 params);
        this.plan.getWorkFragmentsBuilders(TXN_ID, stmtCounters, ftasks);
        assertFalse(ftasks.isEmpty());
        assertNotNull(ftasks);
       
        this.ts = new LocalTransaction(hstore_site);
        this.ts.testInit(TXN_ID,
                         LOCAL_PARTITION,
                         null,
                         catalogContext.getAllPartitionIds(),
                         this.getProcedure(TARGET_PROCEDURE));
        this.ts.markControlCodeExecuted();
        this.depTracker.addTransaction(ts);
        assertNull(this.ts.getCurrentRoundState(LOCAL_PARTITION));
    }
   
    /**
     * Add all of the FragmentTaskMessages from our BatchPlanner into the TransactionState
     * We will also populate our list of dependency ids
     */
    private void addFragments() {
        for (WorkFragment.Builder ftask : ftasks) {
            assertNotNull(ftask);
            this.depTracker.addWorkFragment(this.ts, ftask, this.params);
            for (int i = 0, cnt = ftask.getFragmentIdCount(); i < cnt; i++) {
                this.dependency_ids.add(ftask.getOutputDepId(i));
                int input_dep_id = ftask.getInputDepId(i);
                if (input_dep_id != HStoreConstants.NULL_DEPENDENCY_ID) {
                    this.internal_dependency_ids.add(input_dep_id);
                }
            } // FOR
        } // FOR
        for (int d_id : this.dependency_ids) {
            if (!this.internal_dependency_ids.contains(d_id)) this.output_dependency_ids.add(d_id);
        } // FOR
        Collections.sort(this.internal_dependency_ids);
        Collections.sort(this.output_dependency_ids);
       
        assertFalse(this.output_dependency_ids.isEmpty());
        assertFalse(this.internal_dependency_ids.isEmpty());
//        System.err.println(ftasks);
//        System.err.println("OUTPUT:   " + this.output_dependency_ids);
//        System.err.println("INTERNAL: " + this.internal_dependency_ids);
    }
   
    /**
     * testInitRound
     */
    @Test
    public void testInitRound() throws Exception {
        this.ts.initFirstRound(UNDO_TOKEN, NUM_DUPLICATE_STATEMENTS);
        assertEquals(AbstractTransaction.RoundState.INITIALIZED, this.ts.getCurrentRoundState(LOCAL_PARTITION));
        assertNotNull(this.ts.getLastUndoToken(LOCAL_PARTITION));
        assertEquals(UNDO_TOKEN, this.ts.getLastUndoToken(LOCAL_PARTITION));
        //System.err.println(this.ts);
    }
   
    /**
     * testStartRound
     */
    @Test
    public void testStartRound() throws Exception {
        this.ts.initFirstRound(UNDO_TOKEN, NUM_DUPLICATE_STATEMENTS);
        assertEquals(AbstractTransaction.RoundState.INITIALIZED, this.ts.getCurrentRoundState(LOCAL_PARTITION));
        this.addFragments();
        this.ts.startRound(LOCAL_PARTITION);
        CountDownLatch latch = this.depTracker.getDependencyLatch(this.ts);
        assertNotNull(latch);
       
//        System.err.println(this.ts.toString());
        assertEquals(NUM_EXPECTED_DEPENDENCIES, latch.getCount());
        assertEquals(NUM_DUPLICATE_STATEMENTS, this.depTrackerDbg.getOutputOrder(this.ts).size());
       
        // Although there will be a single blocked FragmentTaskMessage, it will contain
        // the same number of PlanFragments as we have duplicate Statements
        System.err.println(this.depTrackerDbg.getBlockedWorkFragments(this.ts));
        assertEquals(NUM_DUPLICATE_STATEMENTS, this.depTrackerDbg.getBlockedWorkFragments(this.ts).size());
       
        // We now need to make sure that our output order is correct
        // We should be getting back the same number of results as how
        // many Statements that we queued up
        for (int i = 0; i < NUM_DUPLICATE_STATEMENTS; i++) {
            Integer dependency_id = this.depTrackerDbg.getOutputOrder(this.ts).get(i);
            assertNotNull(dependency_id);
            assert(this.output_dependency_ids.contains(dependency_id));
            assertNotNull(this.depTrackerDbg.getDependencyInfo(this.ts, dependency_id));
        } // FOR
        //System.err.println(this.ts);
    }
   
    /**
     * testAddFragmentTaskMessage
     */
    @Test
    public void testAddFragmentTaskMessage() throws Exception {
        this.ts.initFirstRound(UNDO_TOKEN, NUM_DUPLICATE_STATEMENTS);
        this.addFragments();
       
        assertEquals(NUM_EXPECTED_DEPENDENCIES, this.depTrackerDbg.getDependencyCount(this.ts));
        assertEquals(NUM_DUPLICATE_STATEMENTS, this.ts.getCurrentBatchSize());
       
        // For each Statement that we have queued, make sure that they have the proper
        // partitions setup for the dependencies that we expect to show up
        for (int i = 0; i < NUM_DUPLICATE_STATEMENTS; i++) {
            Map<Integer, DependencyInfo> stmt_dinfos = this.depTrackerDbg.getStatementDependencies(this.ts, i);
            assertNotNull(stmt_dinfos);
            assertFalse(stmt_dinfos.isEmpty());
           
            for (Integer d_id : stmt_dinfos.keySet()) {
                DependencyInfo dinfo = stmt_dinfos.get(d_id);
                assertNotNull(dinfo);
               
                if (this.internal_dependency_ids.contains(d_id)) {
                    // This fragment should have been broadcast to all partitions
                    assertEquals(NUM_PARTITIONS, dinfo.getExpectedPartitions().size());
                    // But never out to VoltProcedure
                    assertFalse(this.output_dependency_ids.contains(d_id));
                    // And we should have a task blocked waiting for this dependency
                    assertFalse(dinfo.getBlockedWorkFragments().isEmpty());
                } else {
                    assertEquals(1, dinfo.getExpectedPartitions().size());
                    assertEquals(LOCAL_PARTITION, dinfo.getExpectedPartitions().get());
                }
            } // FOR
           
        } // FOR
        //System.err.println(this.ts);
    }
   
    /**
     * testAddResult
     */
    @Test
    public void testAddResult() throws Exception {
        this.ts.initFirstRound(UNDO_TOKEN, NUM_DUPLICATE_STATEMENTS);
        this.addFragments();
        this.ts.startRound(LOCAL_PARTITION);
        assertEquals(AbstractTransaction.RoundState.STARTED, this.ts.getCurrentRoundState(LOCAL_PARTITION));
       
        // We want to add results for just one of the duplicated statements and make sure that
        // we only unblock one of them. First we need to find an internal dependency that has blocked tasks
        Integer internal_d_id = null;
        DependencyInfo internal_dinfo = null;
        for (Integer d_id : this.internal_dependency_ids) {
            internal_d_id = d_id;
            internal_dinfo = this.depTrackerDbg.getDependencyInfo(this.ts, d_id);
            if (internal_dinfo != null) break;
        } // FOR
        assertNotNull(internal_d_id);
        assertNotNull(internal_dinfo);
       
        // Now shove a fake result for each partition at the TransactionState object. This should
        // cause the blocked FragmentTaskMessage to get moved into the "ready" queue
        for (int partition = 0; partition < NUM_PARTITIONS; partition++) {
            assertFalse(internal_dinfo.hasTasksReady());
            // If this is the first partition, then add one for each of the duplicate Statements
            // We want to make sure that they don't unblock
            for (int stmt_index = 0; stmt_index < NUM_DUPLICATE_STATEMENTS; stmt_index++) {
                if (partition != LOCAL_PARTITION && stmt_index > 0) break;
                int dependency_id = this.internal_dependency_ids.get(stmt_index);
                LOG.debug("Adding result for [partition=" + partition + ", dependency_id=" + dependency_id + ", stmt_index=" + stmt_index + "]");
                this.depTracker.addResult(this.ts, partition, dependency_id, FAKE_RESULT);
            } // FOR
        } // FOR
        if (NUM_PARTITIONS != internal_dinfo.getResults().size()) {
            LOG.info(this.ts);
            LOG.info("----------------------------------");
            LOG.info(internal_dinfo);
        }
        assertEquals(internal_dinfo.getResults().toString(), NUM_PARTITIONS, internal_dinfo.getResults().size());
        assert(internal_dinfo.hasTasksReleased());
       
        // Make sure that all other Statements didn't accidently unblock their FragmentTaskMessages...
        for (int stmt_index = 1; stmt_index < NUM_DUPLICATE_STATEMENTS; stmt_index++) {
            int dep_id = this.internal_dependency_ids.get(stmt_index);
            DependencyInfo other = this.depTrackerDbg.getDependencyInfo(this.ts, dep_id);
            assertNotNull(other);
            assertFalse(other.hasTasksReady());
            assertFalse(other.hasTasksReleased());
            assertEquals(1, other.getResults().size());
        } // FOR
        // System.err.println(this.ts);
    }
   
    /**
     * testAddResultsBeforeStart
     */
    @Test
    public void testAddResultsBeforeStart() throws Exception {
        this.ts.initFirstRound(UNDO_TOKEN, NUM_DUPLICATE_STATEMENTS);
        this.addFragments();
        assertEquals(AbstractTransaction.RoundState.INITIALIZED, this.ts.getCurrentRoundState(LOCAL_PARTITION));
       
        // We need to test to make sure that we don't get a CountDownLatch with the wrong count
        // if we start the round *after* a bunch of results have arrived.
        // Add a bunch of fake results
        Long marker = 1000l;
        List<Long> markers = new ArrayList<Long>();
        for (int dependency_id : this.dependency_ids) {
            for (int partition = 0; partition < NUM_PARTITIONS; partition++) {
                // If this dependency is meant to go back to the VoltProcedure, then
                // we want to add a row so that we can figure out whether we are getting
                // the results back in the right order
                if (!this.internal_dependency_ids.contains(dependency_id)) {
                    // Skip anything that isn't our local partition
                    if (partition != LOCAL_PARTITION) continue;
                    VoltTable copy = new VoltTable(FAKE_RESULTS_COLUMNS);
                    copy.addRow(marker, "XXXX");
                    this.depTracker.addResult(this.ts, partition, dependency_id, copy);
                    markers.add(marker++);
                // Otherwise just stuff in our fake result
                } else {
                    this.depTracker.addResult(this.ts, partition, dependency_id, FAKE_RESULT);
                }
            } // FOR (partition)
        } // FOR (dependency ids)
        assertEquals(NUM_DUPLICATE_STATEMENTS, markers.size());

        this.ts.startRound(LOCAL_PARTITION);
        CountDownLatch latch = this.depTracker.getDependencyLatch(this.ts);
        assertNotNull(latch);
        assertEquals(0, latch.getCount());
        assertEquals(AbstractTransaction.RoundState.STARTED, this.ts.getCurrentRoundState(LOCAL_PARTITION));
    }
   
    /**
     * testGetResults
     */
    @Test
    public void testGetResults() throws Exception {
        this.ts.initFirstRound(UNDO_TOKEN, NUM_DUPLICATE_STATEMENTS);
        this.addFragments();
        this.ts.startRound(LOCAL_PARTITION);

        // Add a bunch of fake results
        Long marker = 1000l;
        List<Long> markers = new ArrayList<Long>();
        List<Integer> sorted_dependency_ids = new ArrayList<Integer>(this.dependency_ids.asList());
        Collections.sort(sorted_dependency_ids);
       
        for (int dependency_id : sorted_dependency_ids) {
            for (int partition = 0; partition < NUM_PARTITIONS; partition++) {
                // If this dependency is meant to go back to the VoltProcedure, then
                // we want to add a row so that we can figure out whether we are getting
                // the results back in the right order
                if (!this.internal_dependency_ids.contains(dependency_id)) {
                    // Skip anything that isn't our local partition
                    if (partition != LOCAL_PARTITION) continue;
                    VoltTable copy = new VoltTable(FAKE_RESULTS_COLUMNS);
                    copy.addRow(marker, "XXXX");
                    this.depTracker.addResult(this.ts, partition, dependency_id, copy);
                    markers.add(marker++);
                // Otherwise just stuff in our fake result
                } else {
                    this.depTracker.addResult(this.ts, partition, dependency_id, FAKE_RESULT);
                    LOG.debug("Adding result for [partition=" + partition + ", dependency_id=" + dependency_id + "]");
                }
            } // FOR (partition)
        } // FOR (dependency ids)
        assertEquals(NUM_DUPLICATE_STATEMENTS, markers.size());

        VoltTable results[] = this.depTracker.getResults(this.ts);
        assertNotNull(results);
        assertEquals(NUM_DUPLICATE_STATEMENTS, results.length);

//        System.err.println("MARKERS: " + markers);
//        System.err.println(StringUtil.join("---------\n", results));
        for (int i = 0; i < results.length; i++) {
            marker = markers.get(i);
            assertNotNull(marker);
            results[i].resetRowPosition();
            assert(results[i].advanceRow());
            assertEquals(marker.longValue(), results[i].getLong(0));
            // System.err.println(results[i]);
            // System.err.println(StringUtil.DOUBLE_LINE);
        } // FOR
        // System.err.println(this.ts);
    }
}
TOP

Related Classes of edu.brown.hstore.txns.TestTransactionState

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.