Package edu.brown.hstore.txns

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

/***************************************************************************
*   Copyright (C) 2012 by H-Store Project                                 *
*   Brown University                                                      *
*   Massachusetts Institute of Technology                                 *
*   Yale University                                                       *
*                                                                         *
*   Permission is hereby granted, free of charge, to any person obtaining *
*   a copy of this software and associated documentation files (the       *
*   "Software"), to deal in the Software without restriction, including   *
*   without limitation the rights to use, copy, modify, merge, publish,   *
*   distribute, sublicense, and/or sell copies of the Software, and to    *
*   permit persons to whom the Software is furnished to do so, subject to *
*   the following conditions:                                             *
*                                                                         *
*   The above copyright notice and this permission notice shall be        *
*   included in all copies or substantial portions of the Software.       *
*                                                                         *
*   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       *
*   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    *
*   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*
*   IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR     *
*   OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, *
*   ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR *
*   OTHER DEALINGS IN THE SOFTWARE.                                       *
***************************************************************************/
package edu.brown.hstore.txns;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.locks.ReentrantLock;

import org.apache.log4j.Logger;
import org.voltdb.ClientResponseImpl;
import org.voltdb.ParameterSet;
import org.voltdb.SQLStmt;
import org.voltdb.VoltTable;
import org.voltdb.catalog.Procedure;
import org.voltdb.catalog.Statement;
import org.voltdb.catalog.Table;
import org.voltdb.exceptions.SerializableException;
import org.voltdb.types.SpeculationType;
import org.voltdb.utils.EstTime;

import com.google.protobuf.RpcCallback;

import edu.brown.hstore.HStoreSite;
import edu.brown.hstore.Hstoreservice.WorkFragment;
import edu.brown.hstore.callbacks.LocalFinishCallback;
import edu.brown.hstore.callbacks.LocalInitQueueCallback;
import edu.brown.hstore.callbacks.LocalPrepareCallback;
import edu.brown.hstore.internal.StartTxnMessage;
import edu.brown.logging.LoggerUtil;
import edu.brown.logging.LoggerUtil.LoggerBoolean;
import edu.brown.profilers.TransactionProfiler;
import edu.brown.protorpc.ProtoRpcController;
import edu.brown.statistics.FastIntHistogram;
import edu.brown.statistics.Histogram;
import edu.brown.statistics.ObjectHistogram;
import edu.brown.utils.ClassUtil;
import edu.brown.utils.PartitionSet;
import edu.brown.utils.StringUtil;

/**
*
* @author pavlo
*/
public class LocalTransaction extends AbstractTransaction {
    private static final Logger LOG = Logger.getLogger(LocalTransaction.class);
    private static final LoggerBoolean debug = new LoggerBoolean();
    private static final LoggerBoolean trace = new LoggerBoolean();
    static {
        LoggerUtil.attachObserver(LOG, debug, trace);
    }

    // ----------------------------------------------------------------------------
    // TRANSACTION INVOCATION DATA MEMBERS
    // ----------------------------------------------------------------------------
   
    /**
     * Final RpcCallback to the client
     */
    private RpcCallback<ClientResponseImpl> client_callback;
   
    /**
     * The final ClientResponse for this txn that needs to get sent
     * back to the client.
     */
    private ClientResponseImpl cresponse;
   
    /**
     * A special lock for the critical sections of the LocalTransaction
     * This is only to handle messages coming from the HStoreCoordinator or from other
     * PartitionExecutors that are executing on this txn's behalf
     */
    private final ReentrantLock lock = new ReentrantLock();
   
    /**
     * The DependencyTracker from this txn's base partition.
     * This is just here for caching purposes. We could always just
     * get it from the HStoreSite if we really needed it.
     */
    private DependencyTracker depTracker;
   
    // ----------------------------------------------------------------------------
    // INTERNAL STATE
    // ----------------------------------------------------------------------------
   
    /**
     * Number of SQLStmts in the current batch
     */
    private int batch_size = 0;
   
    /**
     *
     */
    private boolean needs_restart = false; // FIXME
   
    /**
     * Is this transaction part of a large MapReduce transaction 
     */
    private boolean mapreduce = false;
   
    /**
     * If set to true, then this will need to have an entry written
     * to the command log for its invocation
     */
    private boolean log_enabled = false;
   
    /**
     * The timestamp (from EstTime) that our transaction showed up
     * at this HStoreSite
     */
    private long initiateTime;
   
    /**
     * This is where we will store all of the special state information for distributed txns
     */
    private DistributedState dtxnState;
   
   
    /**
     * Special TransactionProfiler handle
     */
    public TransactionProfiler profiler;
   
    // ----------------------------------------------------------------------------
    // CALLBACKS
    // ----------------------------------------------------------------------------

    /**
     * This callback is used to release the transaction once we get
     * the acknowledgments back from all of the partitions that we're going to access.
     */
    protected final LocalInitQueueCallback init_callback;

    // ----------------------------------------------------------------------------
    // INTERNAL MESSAGE WRAPPERS
    // ----------------------------------------------------------------------------
   
    private StartTxnMessage start_msg;
   
    // ----------------------------------------------------------------------------
    // RUN TIME DATA MEMBERS
    // ----------------------------------------------------------------------------
   

    /**
     * What partitions has this txn touched
     * This needs to be a Histogram so that we can figure out what partitions
     * were touched the most if end up needing to redirect it later on
     */
    private final FastIntHistogram exec_touchedPartitions;
   
    /**
     * Whether this transaction's control code was executed on
     * its base partition.
     */
    private boolean exec_controlCode = false;
   
    /**
     * This keeps track of the number of times that we have invoked
     * each query in this transaction.
     */
    private final Histogram<Statement> exec_stmtCounters = new ObjectHistogram<Statement>();
  private Long old_transaction_id;
   
    // ----------------------------------------------------------------------------
    // INITIALIZATION
    // ----------------------------------------------------------------------------
   
    /**
     * Constructor
     * This does not fully initialize this transaction.
     * You must call init() before this can be used
     */
    public LocalTransaction(HStoreSite hstore_site) {
        super(hstore_site);
        this.init_callback = new LocalInitQueueCallback(hstore_site);
        int numPartitions = hstore_site.getCatalogContext().numberOfPartitions;
        this.exec_touchedPartitions = new FastIntHistogram(false, numPartitions);
    }

    /**
     * Main initialization method for LocalTransaction
     * @param txn_id
     * @param clientHandle
     * @param base_partition
     * @param predict_touchedPartitions
     * @param predict_readOnly
     * @param predict_abortable
     * @param catalog_proc
     * @param params
     * @param client_callback
     * @return
     */
    public LocalTransaction init(Long txn_id,
                                 long initiateTime,
                                 long clientHandle,
                                 int base_partition,
                                 PartitionSet predict_touchedPartitions,
                                 boolean predict_readOnly,
                                 boolean predict_abortable,
                                 Procedure catalog_proc,
                                 ParameterSet params,
                                 RpcCallback<ClientResponseImpl> client_callback) {
        super.init(txn_id,
                   clientHandle,
                   base_partition,
                   params,
                   catalog_proc,
                   predict_touchedPartitions,
                   predict_readOnly,
                   predict_abortable,
                   true);
       
        this.initiateTime = initiateTime;
        this.client_callback = client_callback;
        this.init_callback.init(this, this.predict_touchedPartitions);
        this.mapreduce = catalog_proc.getMapreduce();
       
        if (this.predict_singlePartition == false || this.isSysProc() || hstore_site.getCatalogContext().numberOfPartitions == 1) {
            this.depTracker = hstore_site.getDependencyTracker(base_partition);
        }
       
        // Grab a DistributedState that will have all the goodies that we need
        // to execute a distributed transaction
        if (this.predict_singlePartition == false) {
            try {
//                if (hstore_site.getHStoreConf().site.pool_txn_enable) {
//                    this.dtxnState = hstore_site.getObjectPools()
//                                                .getDistributedStatePool(base_partition)
//                                                .borrowObject();
//                } else {
                    this.dtxnState = new DistributedState(hstore_site);
//                }
                this.dtxnState.init(this);
            } catch (Exception ex) {
                throw new RuntimeException("Unexpected error when trying to initialize " + this, ex);
            }
        }
       
        return (this);
    }

    /**
     * Testing Constructor
     * @param txn_id
     * @param base_partition
     * @param params - Procedure Input Parameters
     * @param predict_touchedPartitions
     * @param catalog_proc
     * @return
     */
    public LocalTransaction testInit(Long txn_id,
                                     int base_partition,
                                     ParameterSet params,
                                     PartitionSet predict_touchedPartitions,
                                     Procedure catalog_proc) {
        this.initiateTime = EstTime.currentTimeMillis();
       
        super.init(txn_id,                       // TxnId
                   Integer.MAX_VALUE,            // ClientHandle
                   base_partition,               // BasePartition
                   params,                       // Procedure Parameters
                   catalog_proc,                 // Procedure
                   predict_touchedPartitions,    // Partitions
                   catalog_proc.getReadonly(),   // ReadOnly
                   true,                         // Abortable
                   true                          // ExecLocal
        );
        if (this.predict_singlePartition == false) {
            this.dtxnState = new DistributedState(hstore_site).init(this);
        }
        if (this.predict_singlePartition == false || this.isSysProc() || hstore_site.getCatalogContext().numberOfPartitions == 1) {
            this.depTracker = hstore_site.getDependencyTracker(base_partition);
        }
       
        return (this);
       
    }
   
    /**
     * Testing Constructor with Parameters and Callback
     * @param txn_id
     * @param base_partition
     * @param predict_touchedPartitions
     * @param catalog_proc
     * @param proc_params
     * @return
     */
    public LocalTransaction testInit(Long txn_id,
                                     int base_partition,
                                     PartitionSet predict_touchedPartitions,
                                     Procedure catalog_proc,
                                     Object...proc_params) {
        this.client_callback = new RpcCallback<ClientResponseImpl>() {
            public void run(ClientResponseImpl parameter) {}
        };
        return this.testInit(txn_id,
                             base_partition,
                             new ParameterSet(proc_params),
                             predict_touchedPartitions, catalog_proc);
    }
   
    @Override
    public void finish() {
        if (debug.val)
            LOG.debug(String.format("%s - Invoking finish() cleanup", this));
       
        // Return our DistributedState
//        if (this.dtxnState != null && hstore_site.getHStoreConf().site.pool_txn_enable) {
//            hstore_site.getObjectPools()
//                       .getDistributedStatePool(this.base_partition)
//                       .returnObject(this.dtxnState);
//            this.dtxnState = null;
//        }
       
        super.finish();
       
        this.client_callback = null;
        this.init_callback.finish();
        this.initiateTime = 0;
        this.cresponse = null;
       
        this.exec_controlCode = false;
        this.exec_specExecType = SpeculationType.NULL;
        this.exec_touchedPartitions.clear();
        this.exec_stmtCounters.clear();
        this.predict_touchedPartitions = null;
        this.restart_ctr = 0;

        this.anticache_table = null;
        this.log_enabled = false;
        this.needs_restart = false;
       
        if (this.profiler != null) this.profiler.finish();
    }
   
    // ----------------------------------------------------------------------------
    // SPECIAL SETTER METHODS
    // ----------------------------------------------------------------------------
   
    public final void setClientResponse(ClientResponseImpl cresponse) {
        assert(this.cresponse == null);
        this.cresponse = cresponse;
    }
    public final ClientResponseImpl getClientResponse() {
        return (this.cresponse);
    }
    public final void resetClientResponse() {
        this.cresponse = null;
    }
   
    public final ReentrantLock getTransactionLock() {
        return (this.lock);
    }
    public final boolean hasDependencyTracker() {
        return (this.depTracker != null);
    }
   
    public void removeProcedureParameters() {
        this.parameters = null;
    }
   
    // ----------------------------------------------------------------------------
    // EXECUTION ROUNDS
    // ----------------------------------------------------------------------------
   
    /**
     * Initialize the first round of a new batch. This should only be
     * invoked at the txn's base partition.
     * @param partition
     * @param undoToken
     * @param batchSize
     */
    public void initFirstRound(long undoToken, int batchSize) {
        if (debug.val)
            LOG.debug(String.format("%s - Initializing ROUND #%d on partition %d [undoToken=%d]",
                      this, this.round_ctr[this.base_partition], this.base_partition, undoToken));
       
        this.batch_size = batchSize;
        if (this.depTracker != null) {
            this.depTracker.initRound(this);
        }
        super.initRound(this.base_partition, undoToken);
    }
   
    @Override
    public void initRound(int partition, long undoToken) {
        assert(partition != this.base_partition) :
            String.format("Trying to invoke %s for %s at its base partition. Use initFirstRound()",
                          ClassUtil.getCurrentMethodName(), this);
        if (debug.val)
            LOG.debug(String.format("%s - Initializing ROUND #%d on partition %d [undoToken=%d]",
                      this, this.round_ctr[partition], partition, undoToken));
       
        super.initRound(partition, undoToken);
    }
   
    @Override
    public void startRound(int partition) {
        // SAME SITE, DIFFERENT PARTITION
        if (this.base_partition != partition) {
            super.startRound(partition);
            return;
        }
       
        // SAME SITE, SAME PARTITION
        assert(this.batch_size > 0);
        if (debug.val)
            LOG.debug(String.format("%s - Starting ROUND #%d on partition %d with %d queued Statements",
                      this, this.round_ctr[partition],
                      partition, this.batch_size));
  
        if (this.predict_singlePartition == false) this.lock.lock();
        try {
            // HACK: If there is only one partition, then we need to always
            // update the DependencyTracker. It's a bit complicated to explain why...
            if (this.depTracker != null) {
                this.depTracker.startRound(this);
            } else {
                LOG.warn(String.format("%s - Skipping DependencyTracker.startRound()\n%s", this, this.debug()));
            }
            super.startRound(partition);
        } catch (AssertionError ex) {
            LOG.fatal("Unexpected error for " + this, ex);
            throw ex;
        } finally {
            if (this.predict_singlePartition == false) this.lock.unlock();
        } // SYNCH
    }
   
    @Override
    public void finishRound(int partition) {
        if (debug.val)
            LOG.debug(String.format("%s - Finishing ROUND #%d on partition %d",
                      this, this.round_ctr[partition], partition));
       
        // SAME SITE, DIFFERENT PARTITION
        if (this.base_partition != partition) {
            // This doesn't need to be synchronized because we know that only our
            // thread should be calling this
            super.finishRound(partition);
            return;
        }
       
        // SAME SITE, SAME PARTITION
        if (this.predict_singlePartition == false) this.lock.lock();
        try {
            // HACK: If there is only one partition, then we need to always
            // update the DependencyTracker. It's a bit complicated to explain why...
            if (this.depTracker != null) {
                this.depTracker.finishRound(this);
            } else {
                LOG.warn(String.format("%s - Skipping DependencyTracker.finishRound()", this));
            }
            super.finishRound(partition);
        } finally {
            if (this.predict_singlePartition == false) this.lock.unlock();
        } // SYNCH
    }
   
    /**
     * Quickly finish this round for a single-partition txn. This allows us
     * to change the state to FINISHED without having to go through the
     * INIT and START states first (since we know that we will not be getting results randomly
     * from WorkFragments executed on remote partitions).
     * @param partition The partition to finish this txn on
     */
    public void fastFinishRound(int partition) {
        this.round_state[partition] = RoundState.STARTED;
        super.finishRound(partition);
        if (this.base_partition == partition) {
            if (this.depTracker != null) this.depTracker.finishRound(this);
        }
    }
   
    // ----------------------------------------------------------------------------
    // ERROR HANDLING
    // ----------------------------------------------------------------------------
   
    /**
     * Set the pending error for this transaction. If wakeThread is true, then
     * the transaction will be released from its lock so that the transaction can be
     * aborted without needing to wait for all of the results to return.
     * @param error
     * @param interruptThread
     */
    public void setPendingError(SerializableException error, boolean interruptThread) {
        interruptThread = (this.pending_error == null && interruptThread);
        super.setPendingError(error);
        if (interruptThread == false) return;
        if (this.depTracker != null) this.depTracker.unblock(this);
    }
   
    @Override
    public void setPendingError(SerializableException error) {
        this.setPendingError(error, true);
    }

    // ----------------------------------------------------------------------------
    // CALLBACK METHODS
    // ----------------------------------------------------------------------------
   
    @SuppressWarnings("unchecked")
    @Override
    public LocalInitQueueCallback getInitCallback() {
        return (this.init_callback);
    }
    @SuppressWarnings("unchecked")
    @Override
    public LocalPrepareCallback getPrepareCallback() {
        assert(this.dtxnState != null) :
            "Trying to access DistributedState for non distributed txn " + this + "\n" + this.debug();
        return (this.dtxnState.prepare_callback);
    }
    @SuppressWarnings("unchecked")
    @Override
    public LocalFinishCallback getFinishCallback() {
        assert(this.dtxnState != null) :
            "Trying to access DistributedState for non distributed txn " + this;
        return (this.dtxnState.finish_callback);
    }
   
    /**
     * Return the original callback that will send the final results back to the client
     * @return
     */
    public RpcCallback<ClientResponseImpl> getClientCallback() {
        return (this.client_callback);
    }
   
    // ----------------------------------------------------------------------------
    // INTERNAL MESSAGES
    // ----------------------------------------------------------------------------
   
    public final StartTxnMessage getStartTxnMessage() {
        if (this.start_msg == null) {
            this.start_msg = new StartTxnMessage(this);
        }
        return (this.start_msg);
    }
   
    // ----------------------------------------------------------------------------
    // ACCESS METHODS
    // ----------------------------------------------------------------------------

    /**
     * Mark that we have invoked this txn's control code.
     */
    public final void markControlCodeExecuted() {
        assert(this.exec_controlCode == false);
        this.exec_controlCode = true;
    }
   
    /**
     * Returns true if the control code for this LocalTransaction was actually started
     * in the PartitionExecutor
     */
    public final boolean isMarkedControlCodeExecuted() {
        return (this.exec_controlCode);
    }
   
    /**
     * Reset this txn's control code as not executed.
     */ 
    public final void resetControlCodeExecuted() {
        this.exec_controlCode = false;
    }
   
    /**
     * Mark this transaction as needing to be restarted. This will prevent it from
     * being deleted immediately
     */
    public final void markNeedsRestart() {
        assert(this.needs_restart == false) :
            "Trying to enable " + this + " internal needs_restart flag twice";
        this.needs_restart = true;
    }
   
    /**
     * Unmark this transaction as needing to be restarted. This can be safely
     * invoked multiple times
     */
    public final void unmarkNeedsRestart() {
        this.needs_restart = false;
    }
   
    @Override
    public boolean isDeletable() {
        if (this.init_callback.allCallbacksFinished() == false) {
            if (trace.val)
                LOG.warn(String.format("%s - %s is not finished", this,
                         this.init_callback.getClass().getSimpleName()));
            return (false);
        }
        if (this.dtxnState != null) {
            if (this.dtxnState.prepare_callback.allCallbacksFinished() == false) {
                if (trace.val)
                    LOG.warn(String.format("%s - %s is not finished", this,
                             this.dtxnState.prepare_callback.getClass().getSimpleName()));
                return (false);
            }
            if (this.dtxnState.finish_callback.allCallbacksFinished() == false) {
                if (trace.val)
                    LOG.warn(String.format("%s - %s is not finished", this,
                             this.dtxnState.finish_callback.getClass().getSimpleName()));
                return (false);
            }
        }
        if (this.needs_restart) {
            if (trace.val)
                LOG.warn(String.format("%s - Needs restart, can't delete now", this));
            return (false);
        }
        return (super.isDeletable());
    }


    /**
     * Get the timestamp that this LocalTransaction handle was initiated
     */
    public long getInitiateTime() {
        return (this.initiateTime);
    }
   
    public final int getCurrentBatchSize() {
        return (this.batch_size);
    }

   
    public FastIntHistogram getTouchedPartitions() {
        return (this.exec_touchedPartitions);
    }
   
    /**
     * Returns true if all of the partitions that this txn is predicted
     * to access are all on the same HStoreSite as its base partition
     */
    public boolean isPredictAllLocal() {
        if (this.dtxnState != null) {
            return (this.dtxnState.is_all_local);
        }
        return (true);
    }
   
    /**
     * Returns true if this Transaction has executed only on a single-partition
     * @return
     */
    public boolean isExecSinglePartition() {
        return (this.exec_touchedPartitions.getValueCount() <= 1);
    }
   
    /**
     * Update and return the number of times that we have executed the given
     * Statement in this transaction's lifetime.
     * @param stmt
     * @return
     */
    public int updateStatementCounter(Statement stmt) {
        return ((int)this.exec_stmtCounters.put(stmt));
    }

    // ----------------------------------------------------------------------------
    // DISTRIBUTED TXN EXECUTION
    // ----------------------------------------------------------------------------
   
    /**
     * Returns true if this transaction has partitions that has notified that
     * it is done with them. Note that even though the txn has marked a partition
     * as done doesn't mean that the partition has been notified yet. 
     * @return
     */
    public boolean hasDonePartitions() {
        return (this.dtxnState.exec_donePartitions.isEmpty() == false);
    }
   
    /**
     * Get all of the partitions that have been marked done for this txn.
     * If a partition is in this set, then the DBMS has sent a message to
     * its PartitionExecutor notifying that the txn is finished with them.
     * @return
     */
    public PartitionSet getDonePartitions() {
        if (this.dtxnState != null) {
            return (this.dtxnState.exec_donePartitions);
        }
        return (null);
    }
   
    /**
     * Check whether the calling thread should initiate the finish phase of 2PC.
     * This method will return true if this is the first time that somebody has
     * asked us whether we can finish things up. Multiple invocations of this
     * method will always return false.
     * @return
     */
    public boolean shouldInvokeFinish() {
        assert(this.dtxnState != null);
        return (this.dtxnState.notified_finish.compareAndSet(false, true));
    }
   
    public ProtoRpcController getTransactionInitController(int site_id) {
        return this.dtxnState.getTransactionInitController(site_id);
    }
    public ProtoRpcController getTransactionWorkController(int site_id) {
        return this.dtxnState.getTransactionWorkController(site_id);
    }
    public ProtoRpcController getTransactionPrepareController(int site_id) {
        return this.dtxnState.getTransactionPrepareController(site_id);
    }
    public ProtoRpcController getTransactionFinishController(int site_id) {
        return this.dtxnState.getTransactionFinishController(site_id);
    }
   
    // ----------------------------------------------------------------------------
    // SPECULATIVE EXECUTION
    // ----------------------------------------------------------------------------
   
    /**
     * Set the flag that indicates whether this transaction was executed speculatively
     */
    public void setSpeculative(SpeculationType type) {
        assert(type != SpeculationType.NULL);
        assert(this.exec_specExecType == SpeculationType.NULL) :
            String.format("Trying to mark %s as speculative twice [new=%s / previous=%s]",
                          this, type, this.exec_specExecType);
        if (debug.val)
            LOG.debug(String.format("%s - Set %s = %s",
                      this, type.getClass().getSimpleName(), type));
        this.exec_specExecType = type;
    }

    /**
     * Returns true if this transaction was executed speculatively
     */
    @Override
    public boolean isSpeculative() {
        return (this.exec_specExecType != SpeculationType.NULL);
    }
   
   
    // ----------------------------------------------------------------------------
    // COMMAND LOGGING
    // ----------------------------------------------------------------------------
   
    /**
     * Mark this txn as needing to have a log entry written to disk
     */
    public void markLogEnabled() {
        assert(this.log_enabled == false) :
            "Trying to mark " + this + " as needing to be logged more than once";
        this.log_enabled = true;
    }
   
    /**
     * Returns true if this txn needs to have a command log entry written for it
     * @return
     */
    public boolean isLogEnabled() {
        return (this.log_enabled);
    }
   
    // ----------------------------------------------------------------------------
    // MAP REDUCE
    // ----------------------------------------------------------------------------
   
    /**
     * Returns true if this transaction is part of a MapReduce transaction
     * @return
     */
    public boolean isMapReduce() {
        return (this.mapreduce);
    }
   
    public void markMapReduce() {
        this.mapreduce = true;
    }
   
    // ----------------------------------------------------------------------------
    // DEBUG STUFF
    // ----------------------------------------------------------------------------
   
    public TransactionProfiler getProfiler() {
        return (this.profiler);
    }
    public void setProfiler(TransactionProfiler profiler) {
        this.profiler = profiler;
    }
   
    @Override
    public String toStringImpl() {
        return String.format("%s #%d/%d", this.getProcedure().getName(),
                                          this.txn_id,
                                          this.base_partition);
    }
   
    @Override
    public String debug() {
        List<Map<String, Object>> maps = new ArrayList<Map<String,Object>>();
       
        // Base Class Info
        for (Map<String, Object> m : super.getDebugMaps()) {
            maps.add(m);
        } // FOR
       
        Map<String, Object> m;
       
        // Run Time Stuff
        m = new LinkedHashMap<String, Object>();
        m.put("Status", (this.status != null ? this.status : "null"));
        m.put("Speculative Type", this.getSpeculationType());
        m.put("Exec Java", this.exec_controlCode);
        m.put("Exec Read Only", Arrays.toString(this.exec_readOnly));
        m.put("Exec Touched Partitions", this.exec_touchedPartitions.toString(30));
        m.put("Exec Single-Partitioned", this.isExecSinglePartition());
        m.put("Exec Statement Counters", this.exec_stmtCounters.toString(30));
        m.put("Restart Counter", this.restart_ctr);
        m.put("Needs Restart", this.needs_restart);
        m.put("Needs CommandLog", this.log_enabled);
        m.put("Speculative Execution", this.exec_specExecType);
        m.put("Estimator State", this.getEstimatorState());
        maps.add(m);
       
        // Dependency Information
        DependencyTracker.Debug depTrackerDebug = null;
        if (this.depTracker != null) {
            depTrackerDebug = this.depTracker.getDebugContext();
            m = depTrackerDebug.debugMap(this);
            if (m != null) maps.add(m);
        }

        // Additional Info
        m = new LinkedHashMap<String, Object>();
        m.put("Client Callback", this.client_callback);
        m.put(this.init_callback.getClass().getSimpleName(), this.init_callback);
        if (this.dtxnState != null) {
            m.put(this.dtxnState.prepare_callback.getClass().getSimpleName(), this.dtxnState.prepare_callback);
            m.put(this.dtxnState.finish_callback.getClass().getSimpleName(), this.dtxnState.finish_callback);
        }
        maps.add(m);

        // Profile Times
        if (this.profiler != null) maps.add(this.profiler.debugMap());
       
        StringBuilder sb = new StringBuilder();
        sb.append(StringUtil.formatMaps(maps.toArray(new Map<?, ?>[maps.size()])));
       
        if (this.depTracker != null && depTrackerDebug.hasTransactionState(this)) {
            sb.append(StringUtil.SINGLE_LINE);
            String stmt_debug[] = new String[this.batch_size];
           
            // FIXME
            // VoltProcedure voltProc = state.executor.getVoltProcedure(this.getProcedure().getName());
            // assert(voltProc != null);
            SQLStmt stmts[] = null; // voltProc.voltLastQueriesExecuted();
           
            // This won't work in test cases
//            assert(stmt_debug.length == stmts.length) :
//                String.format("Expected %d SQLStmts but we only got %d", stmt_debug.length, stmts.length);
           
            for (int stmt_index = 0; stmt_index < stmt_debug.length; stmt_index++) {
                Map<Integer, DependencyInfo> s_dependencies = new HashMap<Integer, DependencyInfo>();
                for (DependencyInfo dinfo : depTrackerDebug.getAllDependencies(this)) {
                    if (dinfo.getStatementCounter() == stmt_index) {
                        s_dependencies.put(dinfo.getDependencyId(), dinfo);
                    }
                } // FOR
               
                String inner = "  Statement #" + stmt_index;
                if (stmts != null && stmt_index < stmts.length) {
                    inner += " - " + stmts[stmt_index].getStatement().getName();
                }
                inner += "\n";
//                inner += "  Output Dependency Id: " + (this.state.output_order.contains(stmt_index) ? this.state.output_order.get(stmt_index) : "<NOT STARTED>") + "\n";
               
                inner += "  Dependency Partitions:\n";
                for (Integer dependency_id : s_dependencies.keySet()) {
                    inner += "    [" + dependency_id + "] => " + s_dependencies.get(dependency_id).getExpectedPartitions() + "\n";
                } // FOR
               
                inner += "  Dependency Results:\n";
                for (Integer dependency_id : s_dependencies.keySet()) {
                    inner += "    [" + dependency_id + "] => [";
                    String add = "";
                    for (VoltTable vt : s_dependencies.get(dependency_id).getResults()) {
                        inner += add + (vt == null ? vt : "{" + vt.getRowCount() + " tuples}");
                        add = ",";
                    }
                    inner += "]\n";
                } // FOR
               
                inner += "  Blocked WorkFragments:\n";
                boolean none = true;
                for (Integer dependency_id : s_dependencies.keySet()) {
                    DependencyInfo d = s_dependencies.get(dependency_id);
                    for (WorkFragment.Builder task : d.getBlockedWorkFragments()) {
                        if (task == null) continue;
                        inner += "    [" + dependency_id + "] => [";
                        String add = "";
                        for (int frag_id : task.getFragmentIdList()) {
                            inner += add + frag_id;
                            add = ", ";
                        } // FOR
                        inner += "] ";
                        if (d.hasTasksReady()) {
                            inner += "*READY*";
                        }
                        else if (d.hasTasksReleased()) {
                            inner += "*RELEASED*";
                        }
                        else {
                            inner += String.format("%d / %d", d.getResults().size(), d.getExpectedPartitions().size());
                        }
                        inner += "\n";
                        none = false;
                    }
                } // FOR
                if (none) inner += "    <none>\n";
                stmt_debug[stmt_index] = inner;
            } // (dependencies)
            sb.append(StringUtil.columns(stmt_debug));
        }
       
        return (sb.toString());
    }

  public void setOldTransactionId(Long transactionId) {
    this.old_transaction_id = transactionId;
  }
 
  public Long getOldTransactionId(){
    return this.old_transaction_id;
  }
   
}
TOP

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

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.