Package edu.brown.hstore.txns

Source Code of edu.brown.hstore.txns.AbstractTransaction$Debug

/***************************************************************************
*   Copyright (C) 2011 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.atomic.AtomicBoolean;

import org.apache.log4j.Logger;
import org.voltdb.ParameterSet;
import org.voltdb.VoltTable;
import org.voltdb.catalog.Procedure;
import org.voltdb.catalog.Table;
import org.voltdb.exceptions.SerializableException;
import org.voltdb.types.SpeculationType;
import org.voltdb.utils.NotImplementedException;

import com.google.protobuf.ByteString;
import com.google.protobuf.RpcCallback;

import edu.brown.hstore.HStoreConstants;
import edu.brown.hstore.HStoreSite;
import edu.brown.hstore.Hstoreservice.Status;
import edu.brown.hstore.Hstoreservice.UnevictDataResponse;
import edu.brown.hstore.Hstoreservice.WorkFragment;
import edu.brown.hstore.callbacks.PartitionCountingCallback;
import edu.brown.hstore.estimators.Estimate;
import edu.brown.hstore.estimators.EstimatorState;
import edu.brown.hstore.internal.FinishTxnMessage;
import edu.brown.hstore.internal.SetDistributedTxnMessage;
import edu.brown.hstore.internal.WorkFragmentMessage;
import edu.brown.interfaces.DebugContext;
import edu.brown.logging.LoggerUtil;
import edu.brown.logging.LoggerUtil.LoggerBoolean;
import edu.brown.pools.Poolable;
import edu.brown.utils.PartitionSet;
import edu.brown.utils.StringUtil;

/**
* Base Transaction State
* @author pavlo
*/
public abstract class AbstractTransaction implements Poolable, Comparable<AbstractTransaction> {
    private static final Logger LOG = Logger.getLogger(AbstractTransaction.class);
    private static final LoggerBoolean debug = new LoggerBoolean();
    static {
        LoggerUtil.attachObserver(LOG, debug);
    }
   
    /**
     * Internal state for the transaction
     */
    public enum RoundState {
        INITIALIZED,
        STARTED,
        FINISHED;
    }
   
    protected final HStoreSite hstore_site;
   
    // ----------------------------------------------------------------------------
    // GLOBAL DATA MEMBERS
    // ----------------------------------------------------------------------------
   
    /**
     * Catalog object of the Procedure that this transaction is currently executing
     */
    private Procedure catalog_proc;
    private boolean sysproc;
    private boolean readonly;
    private boolean allow_early_prepare = true;
   
    protected Long txn_id = null;
    protected Long last_txn_id = null; // FOR DEBUGGING
    protected long client_handle;
    protected int base_partition;
    protected Status status;
    protected SerializableException pending_error;

    /**
     * StoredProcedureInvocation Input Parameters
     * These are the parameters that are sent from the client
     */
    protected ParameterSet parameters;
   
    /**
     * Internal flag that is set to true once to tell whomever
     * that this transaction handle can be deleted.
     */
    private AtomicBoolean deletable = new AtomicBoolean(false);
   
    // ----------------------------------------------------------------------------
    // ATTACHED DATA STRUCTURES
    // ----------------------------------------------------------------------------

    /**
     * Attached inputs for the current execution round.
     * This cannot be in the ExecutionState because we may have attached inputs for
     * transactions that aren't running yet.
     */
    private Map<Integer, List<VoltTable>> attached_inputs;
   
    /**
     * Attached ParameterSets for the current execution round
     * This is so that we don't have to marshal them over to different partitions on the same HStoreSite 
     */
    private ParameterSet attached_parameterSets[];
   
    /**
     * Internal state information for txns that request prefetch queries
     * This is only needed for distributed transactions
     */
    protected PrefetchState prefetch;
   
    // ----------------------------------------------------------------------------
    // INTERNAL MESSAGE WRAPPERS
    // ----------------------------------------------------------------------------
   
    private SetDistributedTxnMessage setdtxn_task;
   
    private FinishTxnMessage finish_task;
   
    private WorkFragmentMessage work_task[];
   
    // ----------------------------------------------------------------------------
    // GLOBAL PREDICTIONS FLAGS
    // ----------------------------------------------------------------------------
   
    /**
     * Whether this transaction will only touch one partition
     */
    protected boolean predict_singlePartition = false;
   
    /**
     * Whether this txn can abort
     */
    protected boolean predict_abortable = true;
   
    /**
     * Whether we predict that this txn will be read-only
     */
    protected boolean predict_readOnly = false;
   
    /**
     * EstimationState Handle
     */
    private EstimatorState predict_tState;
   
    /**
     * The set of partitions that we expected this partition to touch.
     */
    protected PartitionSet predict_touchedPartitions;
   
    // ----------------------------------------------------------------------------
    // PER PARTITION EXECUTION FLAGS
    // ----------------------------------------------------------------------------
   
    private final boolean released[];
    private final boolean prepared[];
    private final boolean finished[];
   
    protected final RoundState round_state[];
    protected final int round_ctr[];
    /**
     * The number of times that this transaction has been restarted
     */
    protected int restart_ctr = 0;
   

    /**
     * The first undo token used at each local partition
     * When we abort a txn we will need to give the EE this value
     */
    private final long exec_firstUndoToken[];
    /**
     * The last undo token used at each local partition
     * When we commit a txn we will need to give the EE this value
     */
    private final long exec_lastUndoToken[];
    /**
     * This is set to true if the transaction did some work without an undo
     * buffer at a local partition. This is used to just check that we aren't
     * trying to rollback changes without having used the undo log.
     */
    private final boolean exec_noUndoBuffer[];
   
    /**
     * Whether this transaction has been read-only so far on a local partition
     */
    protected final boolean exec_readOnly[];
   
    /**
     * Whether this Transaction has queued work that may need to be removed
     * from this partition
     */
    protected final boolean exec_queueWork[];
   
    /**
     * Whether this Transaction has submitted work to the EE that may need to be
     * rolled back on a local partition
     */
    protected final boolean exec_eeWork[];
   
    /**
     * BitSets for whether this txn has read from a particular table on each
     * local partition.
     * PartitionId -> TableId
     */
    protected final boolean readTables[][];
   
    /**
     * BitSets for whether this txn has executed a modifying query against a particular table
     * on each partition. Note that it does not actually record whether any rows were changed,
     * but just we executed a query that targeted that table.
     * PartitionId -> TableId
     */
    protected final boolean writeTables[][];
    /**
     * The table that this txn needs to merge the results for in the EE
     * before it starts executing
     */
    protected Table anticache_table = null;
    /**
     * Whether this txn was speculatively executed
     */
    protected SpeculationType exec_specExecType = SpeculationType.NULL;
   
   
    // ----------------------------------------------------------------------------
    // INITIALIZATION
    // ----------------------------------------------------------------------------
   
    /**
     * Constructor
     * @param executor
     */
    public AbstractTransaction(HStoreSite hstore_site) {
        this.hstore_site = hstore_site;
        int numPartitions = hstore_site.getCatalogContext().numberOfPartitions;
       
        this.released = new boolean[numPartitions];
        this.prepared = new boolean[numPartitions];
        this.finished = new boolean[numPartitions];
        this.round_state = new RoundState[numPartitions];
        this.round_ctr = new int[numPartitions];
        this.exec_readOnly = new boolean[numPartitions];
        this.exec_queueWork = new boolean[numPartitions];
        this.exec_eeWork = new boolean[numPartitions];
       
        this.exec_firstUndoToken = new long[numPartitions];
        this.exec_lastUndoToken = new long[numPartitions];
        this.exec_noUndoBuffer = new boolean[numPartitions];
       
        this.readTables = new boolean[numPartitions][];
        this.writeTables = new boolean[numPartitions][];
       
        Arrays.fill(this.exec_firstUndoToken, HStoreConstants.NULL_UNDO_LOGGING_TOKEN);
        Arrays.fill(this.exec_lastUndoToken, HStoreConstants.NULL_UNDO_LOGGING_TOKEN);
        Arrays.fill(this.exec_readOnly, true);
        Arrays.fill(this.exec_queueWork, false);
        Arrays.fill(this.exec_eeWork, false);
    }

    /**
     * Initialize this TransactionState for a new Transaction invocation
     * @param txn_id
     * @param client_handle
     * @param base_partition
     * @param parameters TODO
     * @param sysproc
     * @param predict_singlePartition
     * @param predict_readOnly
     * @param predict_abortable
     * @param exec_local
     * @return
     */
    protected final AbstractTransaction init(Long txn_id,
                                             long client_handle,
                                             int base_partition,
                                             ParameterSet parameters,
                                             Procedure catalog_proc,
                                             PartitionSet predict_touchedPartitions,
                                             boolean predict_readOnly,
                                             boolean predict_abortable,
                                             boolean exec_local) {
        assert(predict_touchedPartitions != null);
        assert(predict_touchedPartitions.isEmpty() == false);
        assert(catalog_proc != null) : "Unexpected null Procedure catalog handle";
       
        this.txn_id = txn_id;
        this.last_txn_id = txn_id;
        this.client_handle = client_handle;
        this.base_partition = base_partition;
        this.parameters = parameters;
       
        this.catalog_proc = catalog_proc;
        this.sysproc = this.catalog_proc.getSystemproc();
        this.readonly = this.catalog_proc.getReadonly();
       
        // Initialize the predicted execution properties for this transaction
        this.predict_touchedPartitions = predict_touchedPartitions;
        this.predict_singlePartition = (this.predict_touchedPartitions.size() == 1);
        this.predict_readOnly = predict_readOnly;
        this.predict_abortable = predict_abortable;
       
        return (this);
    }

    @Override
    public final boolean isInitialized() {
        return (this.txn_id != null && this.catalog_proc != null);
    }
   
    /**
     * <B>Note:</B> This should never be called by anything other than the TransactionInitializer
     * @param txn_id
     */
    public void setTransactionId(Long txn_id) {
        this.txn_id = txn_id;
    }
    /**
     * Returns the speculation type (i.e., stall point) that this txn was executed at.
     * Will be null if this transaction was not speculatively executed
     * @return
     */
    public SpeculationType getSpeculationType() {
        return (this.exec_specExecType);
    }
    /**
     * Should be called once the TransactionState is finished and is
     * being returned to the pool
     */
    @Override
    public void finish() {
        this.predict_singlePartition = false;
        this.predict_abortable = true;
        this.predict_readOnly = false;
        this.predict_tState = null;
       
        this.allow_early_prepare = true;
        this.pending_error = null;
        this.status = null;
        this.parameters = null;
        if (this.attached_inputs != null) this.attached_inputs.clear();
        this.attached_parameterSets = null;

        for (int partition : this.hstore_site.getLocalPartitionIds().values()) {
            this.released[partition] = false;
            this.prepared[partition] = false;
            this.finished[partition] = false;
            this.round_state[partition] = null;
            this.round_ctr[partition] = 0;
            this.exec_readOnly[partition] = true;
            this.exec_queueWork[partition] = false;
            this.exec_eeWork[partition] = false;
            this.exec_firstUndoToken[partition] = HStoreConstants.NULL_UNDO_LOGGING_TOKEN;
            this.exec_lastUndoToken[partition] = HStoreConstants.NULL_UNDO_LOGGING_TOKEN;
            this.exec_noUndoBuffer[partition] = false;
           
            if (this.readTables[partition] != null) Arrays.fill(this.readTables[partition], false);
            if (this.writeTables[partition] != null) Arrays.fill(this.writeTables[partition], false);
        } // FOR

        if (debug.val)
            LOG.debug(String.format("Finished txn #%d and cleaned up internal state [hashCode=%d, finished=%s]",
                      this.txn_id, this.hashCode(), Arrays.toString(this.finished)));
       
        this.deletable.lazySet(false);
        this.catalog_proc = null;
        this.sysproc = false;
        this.readonly = false;
        this.base_partition = HStoreConstants.NULL_PARTITION_ID;
        this.txn_id = null;
    }
    /**
     * Return the number of times that this transaction was restarted
     * @return
     */
    public int getRestartCounter() {
        return (this.restart_ctr);
    }
   
    /**
     * Set the number of times that this transaction has been restarted
     * @param val
     */
    public void setRestartCounter(int val) {
        this.restart_ctr = val;
    }

    // ----------------------------------------------------------------------------
    // DATA STORAGE
    // ----------------------------------------------------------------------------
   
    /**
     * Store data from mapOutput tables to reduceInput table
     * reduceInput table should merge all incoming data from the mapOutput tables.
     */
    public Status storeData(int partition, VoltTable vt) {
        throw new NotImplementedException("Not able to store data for non-MapReduce transactions");
    }
   
    // ----------------------------------------------------------------------------
    // ROUND METHODS
    // ----------------------------------------------------------------------------
   
    /**
     * Must be called once before one can add new WorkFragments for this txn
     * This will always clear out any pending errors
     * @param undoToken
     */
    public void initRound(int partition, long undoToken) {
        assert(this.round_state[partition] == null || this.round_state[partition] == RoundState.FINISHED) :
            String.format("Invalid state %s for ROUND #%s on partition %d for %s [hashCode=%d]",
                          this.round_state[partition], this.round_ctr[partition],
                          partition, this, this.hashCode());
       
        // If we get to this point, then we know that nobody cares about any
        // errors from the previous round, therefore we can just clear it out
        this.pending_error = null;
       
        if (this.exec_lastUndoToken[partition] == HStoreConstants.NULL_UNDO_LOGGING_TOKEN ||
            undoToken != HStoreConstants.DISABLE_UNDO_LOGGING_TOKEN) {
            // LAST UNDO TOKEN
            this.exec_lastUndoToken[partition] = undoToken;
           
            // FIRST UNDO TOKEN
            if (this.exec_firstUndoToken[partition] == HStoreConstants.NULL_UNDO_LOGGING_TOKEN ||
                this.exec_firstUndoToken[partition] == HStoreConstants.DISABLE_UNDO_LOGGING_TOKEN) {
                this.exec_firstUndoToken[partition] = undoToken;
            }
        }
        // NO UNDO LOGGING
        if (undoToken == HStoreConstants.DISABLE_UNDO_LOGGING_TOKEN) {
            this.exec_noUndoBuffer[partition] = true;
        }
        this.round_state[partition] = RoundState.INITIALIZED;
       
        if (debug.val)
            LOG.debug(String.format("%s - Initializing ROUND %d at partition %d [undoToken=%d / first=%d / last=%d]",
                      this, this.round_ctr[partition], partition,
                      undoToken, this.exec_firstUndoToken[partition],
                      this.exec_lastUndoToken[partition]));
    }
   
    /**
     * Called once all of the WorkFragments have been submitted for this txn
     * @return
     */
    public void startRound(int partition) {
        assert(this.round_state[partition] == RoundState.INITIALIZED) :
            String.format("Invalid state %s for ROUND #%s on partition %d for %s [hashCode=%d]",
                          this.round_state[partition], this.round_ctr[partition],
                          partition, this, this.hashCode());
       
        this.round_state[partition] = RoundState.STARTED;
        if (debug.val)
            LOG.debug(String.format("%s - Starting batch ROUND #%d on partition %d",
                      this, this.round_ctr[partition], partition));
    }
   
    /**
     * When a round is over, this must be called so that we can clean up the various
     * dependency tracking information that we have
     */
    public void finishRound(int partition) {
        assert(this.round_state[partition] == RoundState.STARTED) :
            String.format("Invalid batch round state %s for %s at partition %d",
                          this.round_state[partition], this, partition);
       
        if (debug.val)
            LOG.debug(String.format("%s - Finishing batch ROUND #%d on partition %d",
                      this, this.round_ctr[partition], partition));
        this.round_state[partition] = RoundState.FINISHED;
        this.round_ctr[partition]++;
    }
   
    // ----------------------------------------------------------------------------
    // PREDICTIONS
    // ----------------------------------------------------------------------------
   
    /**
     * Return the collection of the partitions that this transaction is expected
     * to need during its execution. The transaction may choose to not use all of
     * these but it is not allowed to use more.
     */
    public final PartitionSet getPredictTouchedPartitions() {
        return (this.predict_touchedPartitions);
    }
    /**
     * Returns true if this Transaction was originally predicted as being able to abort
     */
    public final boolean isPredictAbortable() {
        return (this.predict_abortable);
    }
    /**
     * Returns true if this transaction was originally predicted as read only
     */
    public final boolean isPredictReadOnly() {
        return (this.predict_readOnly);
    }
    /**
     * Returns true if this Transaction was originally predicted to be single-partitioned
     */
    public final boolean isPredictSinglePartition() {
        return (this.predict_singlePartition);
    }
    /**
     * Returns the EstimatorState for this txn
     * @return
     */
    public EstimatorState getEstimatorState() {
        return (this.predict_tState);
    }
    /**
     * Set the EstimatorState for this txn
     * @param state
     */
    public final void setEstimatorState(EstimatorState state) {
        assert(this.predict_tState == null) :
            String.format("Trying to set the %s for %s twice",
                          EstimatorState.class.getSimpleName(), this);
        if (debug.val)
            LOG.debug(String.format("%s - Setting %s for txn",
                      this, state.getClass().getSimpleName()));
        this.predict_tState = state;
    }
    /**
     * Get the last TransactionEstimate produced for this transaction.
     * If there is no estimate, then the return result is null.
     * @return
     */
    public final Estimate getLastEstimate() {
        if (this.predict_tState != null) {
            return (this.predict_tState.getLastEstimate());
        }
        return (null);
    }
    /**
     * Returns true if this transaction was executed speculatively
     */
    public boolean isSpeculative() {
        return (false);
    }
   
    // ----------------------------------------------------------------------------
    // EXECUTION FLAG METHODS
    // ----------------------------------------------------------------------------
   
    /**
     * Mark this transaction as have performed some modification on this partition
     */
    public final void markExecNotReadOnly(int partition) {
        assert(this.sysproc == true || this.readonly == false);
        this.exec_readOnly[partition] = false;
    }

    /**
     * Returns true if this transaction has not executed any modifying work at this partition
     */
    public final boolean isExecReadOnly(int partition) {
        if (this.readonly) return (true);
        return (this.exec_readOnly[partition]);
    }
   
    /**
     * Returns true if this transaction executed without undo buffers at some point
     */
    public final boolean isExecNoUndoBuffer(int partition) {
        return (this.exec_noUndoBuffer[partition]);
    }
    /**
     * Mark that the transaction executed queries without using undo logging
     * at the given partition.
     * @param partition
     */
    public final void markExecNoUndoBuffer(int partition) {
        this.exec_noUndoBuffer[partition] = true;
    }
    /**
     * Returns true if this transaction's control code running at this partition
     */
    public final boolean isExecLocal(int partition) {
        return (this.base_partition == partition);
    }
   
    /**
     * Returns true if this transaction has done something at this partition and therefore
     * the PartitionExecutor needs to be told that they are finished.
     * This could be either executing a query or executing the transaction's control code
     */
    public final boolean needsFinish(int partition) {
        boolean ret = (this.exec_readOnly[partition] == false &&
                        (this.round_state[partition] != null ||
                         this.exec_eeWork[partition] ||
                         this.exec_queueWork[partition])
        );
       
//        if (this.isSpeculative()) {
//            LOG.info(String.format(
//                "%s - Speculative Execution Partition %d => %s\n" +
//                "  Round State:   %s\n" +
//                "  Exec ReadOnly: %s\n" +
//                "  Exec Queue:    %s\n" +
//                "  Exec EE:       %s\n",
//                this, partition, ret,
//                this.round_state[offset],
//                this.exec_readOnly[offset],
//                this.exec_queueWork[offset],
//                this.exec_eeWork[offset]));
//        }
       
        // This transaction needs to be "finished" down in the EE if:
        //  (1) It's not read-only
        //  (2) It has executed at least one batch round
        //  (3) It has invoked work directly down in the EE
        //  (4) It added work that may be waiting in this partition's queue
        return (ret);
    }
   
    /**
     * Returns true if we believe that this transaction can be deleted
     * <B>Note:</B> This will only return true once and only once for each
     * transaction invocation. So don't call this unless you are able to really
     * delete the transaction now. If you just want to know whether the txn has
     * been marked as deletable before, then use checkDeletableFlag()
     * @return
     */
    public boolean isDeletable() {
//        if (this.isInitialized() == false) {
//            return (false);
//        }
        return (this.deletable.compareAndSet(false, true));
    }
   
    /**
     * Returns true if this txn has already been marked for deletion
     * This will not change the internal state of the txn.
     */
    public final boolean checkDeletableFlag() {
        return (this.deletable.get());
    }

   
    // ----------------------------------------------------------------------------
    // GENERAL METHODS
    // ----------------------------------------------------------------------------

    @Override
    public final boolean equals(Object obj) {
        if (obj instanceof AbstractTransaction) {
            AbstractTransaction other = (AbstractTransaction)obj;
            if (this.txn_id == null) {
                return (this.hashCode() != other.hashCode());
            }
            return (this.txn_id.equals(other.txn_id));
        }
        return (false);
    }
   
    @Override
    public final int compareTo(AbstractTransaction o) {
        if (this.txn_id == null) return (1);
        else if (o.txn_id == null) return (-1);
        return this.txn_id.compareTo(o.txn_id);
    }
   
    /**
     * Get this state's transaction id
     */
    public final Long getTransactionId() {
        return this.txn_id;
    }
    /**
     * Get this state's client_handle
     */
    public final long getClientHandle() {
        return this.client_handle;
    }
    /**
     * Get the base PartitionId where this txn's Java code is executing on
     */
    public final int getBasePartition() {
        return this.base_partition;
    }
    /**
     * Return the underlying procedure catalog object
     * @return
     */
    public final Procedure getProcedure() {
        return (this.catalog_proc);
    }
    /**
     * Return the ParameterSet that contains the procedure input
     * parameters for this transaction. These are the original parameters
     * that were sent from the client for this txn.
     * This can be null for distributed transactions on remote partitions
     */
    public ParameterSet getProcedureParameters() {
        return (this.parameters);
    }
    /**
     * Returns true if this transaction is for a system procedure
     */
    public final boolean isSysProc() {
        return (this.sysproc);
    }
   
    public final boolean allowEarlyPrepare() {
        return (this.allow_early_prepare);
    }
   
    public final void setAllowEarlyPrepare(boolean enable) {
        this.allow_early_prepare = enable;
    }
   
    // ----------------------------------------------------------------------------
    // CALLBACK METHODS
    // ----------------------------------------------------------------------------
   
    /**
     * Return this handle's InitCallback
     */
    public abstract <T extends PartitionCountingCallback<? extends AbstractTransaction>> T getInitCallback();
   
    /**
     * Return this handle's PrepareCallback
     */
    public abstract <T extends PartitionCountingCallback<? extends AbstractTransaction>> T getPrepareCallback();
   
    /**
     * Return this handle's FinishCallback
     */
    public abstract <T extends PartitionCountingCallback<? extends AbstractTransaction>> T getFinishCallback();

    // ----------------------------------------------------------------------------
    // ERROR METHODS
    // ----------------------------------------------------------------------------
   
    /**
     * Returns true if this transaction has a pending error
     */
    public final boolean hasPendingError() {
        return (this.pending_error != null);
    }
    /**
     * Return the pending error for this transaction.
     * This does not clear it.
     */
    public final SerializableException getPendingError() {
        return (this.pending_error);
    }
    /**
     * Return the message for the pending error of this transaction.
     */
    public final String getPendingErrorMessage() {
        return (this.pending_error != null ? this.pending_error.getMessage() : null);
    }
   
    /**
     * Set the Exception that is causing this txn to abort. It will need
     * to be processed later on.
     * This is a thread-safe operation. Only the first error will be stored.
     * @param error
     */
    public synchronized void setPendingError(SerializableException error) {
        assert(error != null) : "Trying to set a null error for txn #" + this.txn_id;
        if (this.pending_error == null) {
            if (debug.val)
                LOG.warn(String.format("%s - Got %s error for txn: %s",
                         this, error.getClass().getSimpleName(), error.getMessage()));
            this.pending_error = error;
        }
    }
   
    // ----------------------------------------------------------------------------
    // INTERNAL MESSAGE WRAPPERS
    // ----------------------------------------------------------------------------
   
    public final SetDistributedTxnMessage getSetDistributedTxnMessage() {
        if (this.setdtxn_task == null) {
            this.setdtxn_task = new SetDistributedTxnMessage(this);
        }
        return (this.setdtxn_task);
    }
    public final FinishTxnMessage getFinishTxnMessage(Status status) {
        if (this.finish_task == null) {
            synchronized (this) {
                if (this.finish_task == null) {
                    this.finish_task = new FinishTxnMessage(this, status);           
                }
            } // SYNCH
        }
        this.finish_task.setStatus(status);
        return (this.finish_task);
    }
    public final WorkFragmentMessage getWorkFragmentMessage(WorkFragment fragment) {
        if (this.work_task == null) {
            this.work_task = new WorkFragmentMessage[hstore_site.getCatalogContext().numberOfPartitions];
        }
        int partition = fragment.getPartitionId();
        if (this.work_task[partition] == null) {
            this.work_task[partition] = new WorkFragmentMessage(this, fragment);
        } else {
            this.work_task[partition].setFragment(fragment);
        }
        return (this.work_task[partition]);
    }
   
    /**
     * Set the current Status for this transaction
     * This is not thread-safe.
     * @param status
     */
    public final void setStatus(Status status) {
        this.status = status;
    }
   
    public final Status getStatus() {
        return (this.status);
    }
   
    /**
     * Returns true if this transaction has been aborted.
     */
    public final boolean isAborted() {
        return (this.status != null && this.status != Status.OK);
    }
   
    // ----------------------------------------------------------------------------
    // ANTI-CACHING
    // ----------------------------------------------------------------------------
   
    public boolean hasAntiCacheMergeTable() {
        return (this.anticache_table != null);
    }
   
    public Table getAntiCacheMergeTable() {
        return (this.anticache_table);
    }
   
    public void setAntiCacheMergeTable(Table catalog_tbl) {
        assert(this.anticache_table == null);
        this.anticache_table = catalog_tbl;
    }
   
    // ----------------------------------------------------------------------------
    // TRANSACTION STATE BOOKKEEPING
    // ----------------------------------------------------------------------------
   
    /**
     * Mark this txn as being released from the lock queue at the given partition.
     * This means that this txn was released to that partition and will
     * need to be removed with a FinishTxnMessage
     * @param partition - The partition to mark this txn as "released"
     */
    public final void markReleased(int partition) {
        if (debug.val)
            LOG.debug(String.format("%s - Marking as released on partition %d %s [hashCode=%d]",
                      this, partition, Arrays.toString(this.released), this.hashCode()));
//        assert(this.released[partition] == false) :
//            String.format("Trying to mark %s as released to partition %d twice", this, partition);
        this.released[partition] = true;
    }
    /**
     * Is this TransactionState marked as released at the given partition
     * @return
     */
    public final boolean isMarkedReleased(int partition) {
        return (this.released[partition]);
    }
   
    /**
     * Mark this txn as prepared and return the original value
     * This is a thread-safe operation
     * @param partition - The partition to mark this txn as "prepared"
     */
    public final boolean markPrepared(int partition) {
        if (debug.val)
            LOG.debug(String.format("%s - Marking as prepared on partition %d %s [hashCode=%d, offset=%d]",
                      this, partition, Arrays.toString(this.prepared),
                      this.hashCode(), partition));
        boolean orig = false;
        synchronized (this.prepared) {
            orig = this.prepared[partition];
            this.prepared[partition] = true;
        } // SYNCH
        return (orig == false);
    }
    /**
     * Is this TransactionState marked as prepared at the given partition
     * @return
     */
    public final boolean isMarkedPrepared(int partition) {
        return (this.prepared[partition]);
    }
   
    /**
     * Mark this txn as finished (and thus ready for clean-up)
     */
    public final void markFinished(int partition) {
        if (debug.val)
            LOG.debug(String.format("%s - Marking as finished on partition %d " +
                      "[finished=%s / hashCode=%d / offset=%d]",
                      this, partition, Arrays.toString(this.finished),
                      this.hashCode(), partition));
        this.finished[partition] = true;
    }
    /**
     * Is this TransactionState marked as finished
     * @return
     */
    public final boolean isMarkedFinished(int partition) {
        return (this.finished[partition]);
    }
   
    /**
     * Should be called whenever the txn submits work to the EE
     */
    public final void markQueuedWork(int partition) {
        if (debug.val) LOG.debug(String.format("%s - Marking as having queued work on partition %d [exec_queueWork=%s]",
                                 this, partition, Arrays.toString(this.exec_queueWork)));
        this.exec_queueWork[partition] = true;
    }
   
    /**
     * Returns true if this txn has queued work at the given partition
     * @return
     */
    public final boolean hasQueuedWork(int partition) {
        return (this.exec_queueWork[partition]);
    }
   
    /**
     * Should be called whenever the txn submits work to the EE
     */
    public final void markExecutedWork(int partition) {
        if (debug.val) LOG.debug(String.format("%s - Marking as having submitted to the EE on partition %d [exec_eeWork=%s]",
                                 this, partition, Arrays.toString(this.exec_eeWork)));
        this.exec_eeWork[partition] = true;
    }
   
    /**
     * Returns true if this txn has submitted work to the EE that needs to be rolled back
     * @return
     */
    public final boolean hasExecutedWork(int partition) {
        return (this.exec_eeWork[partition]);
    }
   
    // ----------------------------------------------------------------------------
    // READ/WRITE TABLE TRACKING
    // ----------------------------------------------------------------------------
   
    private final int[] getMarkedTableIds(boolean bitmap[]) {
        int cnt = 0;
        if (bitmap != null) {
            for (int i = 0; i < bitmap.length; i++) {
                if (bitmap[i]) cnt++;
            } // FOR
        }
        int ret[] = new int[cnt];
        if (bitmap != null) {
            cnt = 0;
            for (int i = 0; i < bitmap.length; i++) {
                if (bitmap[i]) {
                    ret[cnt++] = i;
                }
            } // FOR
        }
        return (ret);
    }
   
    /**
     * Return an array of the tableIds that are marked as read by the txn at
     * the given partition id.
     * @param partition
     * @return
     */
    public final int[] getTableIdsMarkedRead(int partition) {
        return (this.getMarkedTableIds(this.readTables[partition]));
    }
    /**
     * Mark that this txn read from the Table at the given partition
     * @param partition
     * @param catalog_tbl
     */
    public final void markTableRead(int partition, Table catalog_tbl) {
        if (this.readTables[partition] == null) {
            this.readTables[partition] = new boolean[hstore_site.getCatalogContext().numberOfTables + 1];
        }
        this.readTables[partition][catalog_tbl.getRelativeIndex()] = true;
    }
    /**
     * Mark that this txn read from the tableIds at the given partition
     * @param partition
     * @param tableIds
     */
    public final void markTableIdsRead(int partition, int...tableIds) {
        if (this.readTables[partition] == null) {
            this.readTables[partition] = new boolean[hstore_site.getCatalogContext().numberOfTables + 1];
        }
        for (int id : tableIds) {
            this.readTables[partition][id] = true;
        } // FOR
    }
    /**
     * Returns true if this txn has executed a query that read from the Table
     * at the given partition.
     * @param partition
     * @param catalog_tbl
     * @return
     */
    public final boolean isTableRead(int partition, Table catalog_tbl) {
        if (this.readTables[partition] != null) {
            return (this.readTables[partition][catalog_tbl.getRelativeIndex()]);
        }
        return (false);
    }
   
    /**
     * Return an array of the tableIds that are marked as written by the txn at
     * the given partition id.
     * @param partition
     * @return
     */
    public final int[] getTableIdsMarkedWritten(int partition) {
        return (this.getMarkedTableIds(this.writeTables[partition]));
    }
    /**
     * Mark that this txn has executed a modifying query for the Table at the given partition.
     * <B>Note:</B> This is just tracking that we executed a query.
     * It does not necessarily mean that the query actually changed anything.
     * @param partition
     * @param catalog_tbl
     */
    public final void markTableWritten(int partition, Table catalog_tbl) {
        if (this.writeTables[partition] == null) {
            this.writeTables[partition] = new boolean[hstore_site.getCatalogContext().numberOfTables + 1];
        }
        this.writeTables[partition][catalog_tbl.getRelativeIndex()] = true;
    }
    /**
     * Mark that this txn has executed a modifying query for the tableIds at the given partition.
     * <B>Note:</B> This is just tracking that we executed a query.
     * It does not necessarily mean that the query actually changed anything.
     * @param partition
     * @param tableIds
     */
    public final void markTableIdsWritten(int partition, int...tableIds) {
        if (this.writeTables[partition] == null) {
            this.writeTables[partition] = new boolean[hstore_site.getCatalogContext().numberOfTables + 1];
        }
        for (int id : tableIds) {
            this.writeTables[partition][id] = true;
        } // FOR
    }
    /**
     * Returns true if this txn has executed a non-readonly query that read from
     * the Table at the given partition.
     * @param partition
     * @param catalog_tbl
     * @return
     */
    public final boolean isTableWritten(int partition, Table catalog_tbl) {
        if (this.writeTables[partition] != null) {
            return (this.writeTables[partition][catalog_tbl.getRelativeIndex()]);
        }
        return (false);
    }
   
    /**
     * Returns true if this txn executed at query that either accessed or modified
     * the Table at the given partition.
     * @param partition
     * @param catalog_tbl
     * @return
     */
    public final boolean isTableReadOrWritten(int partition, Table catalog_tbl) {
        int tableId = catalog_tbl.getRelativeIndex();
        if (this.readTables[partition] != null && this.readTables[partition][tableId]) {
            return (true);
        }
        if (this.writeTables[partition] != null && this.writeTables[partition][tableId]) {
            return (true);
        }
        return (false);
    }
   
    // ----------------------------------------------------------------------------
    // GLOBAL STATE TRACKING
    // ----------------------------------------------------------------------------
   

    /**
     * Get the current Round that this TransactionState is in
     * Used only for testing 
     */
    protected RoundState getCurrentRoundState(int partition) {
        return (this.round_state[partition]);
    }
   
    /**
     * Get the first undo token used for this transaction
     * When we ABORT a txn we will need to give the EE this value
     */
    public long getFirstUndoToken(int partition) {
        return this.exec_firstUndoToken[partition];
    }
    /**
     * Get the last undo token used for this transaction
     * When we COMMIT a txn we will need to give the EE this value
     */
    public long getLastUndoToken(int partition) {
        return this.exec_lastUndoToken[partition];
    }
   
    // ----------------------------------------------------------------------------
    // ATTACHED DATA FOR NORMAL WORK FRAGMENTS
    // ----------------------------------------------------------------------------
   
   
    public final void attachParameterSets(ParameterSet parameterSets[]) {
        this.attached_parameterSets = parameterSets;
    }
   
    public final ParameterSet[] getAttachedParameterSets() {
        assert(this.attached_parameterSets != null) :
            String.format("The attached ParameterSets for %s is null?", this);
        return (this.attached_parameterSets);
    }
   
    public final void attachInputDependency(int input_dep_id, VoltTable vt) {
        if (this.attached_inputs == null) {
            this.attached_inputs = new HashMap<Integer, List<VoltTable>>();
        }
        List<VoltTable> l = this.attached_inputs.get(input_dep_id);
        if (l == null) {
            l = new ArrayList<VoltTable>();
            this.attached_inputs.put(input_dep_id, l);
        }
        l.add(vt);
    }
   
    public final Map<Integer, List<VoltTable>> getAttachedInputDependencies() {
        if (this.attached_inputs == null) {
            this.attached_inputs = new HashMap<Integer, List<VoltTable>>();
        }
        return (this.attached_inputs);
    }
   
    // ----------------------------------------------------------------------------
    // PREFETCH QUERIES
    // ----------------------------------------------------------------------------
   
    /**
     * Grab a PrefetchState object for this LocalTransaction
     * This must be called before you can send prefetch requests on behalf of this txn
     */
    public final void initializePrefetch() {
        if (this.prefetch == null) {
            this.prefetch = new PrefetchState(this.hstore_site);
            this.prefetch.init(this);
        }
    }
   
    public final PrefetchState getPrefetchState() {
        return (this.prefetch);
    }
   
    /**
     * Returns true if this transaction has prefetched queries
     */
    public final boolean hasPrefetchQueries() {
        return (this.prefetch != null);
    }
   
    /**
     * Attach prefetchable WorkFragments for this transaction
     * This should be invoked on the remote side of the initialization request.
     * That is, it is not the transaction's base partition that is storing this information,
     * it's coming from over the network
     * @param fragments
     * @param rawParameters
     */
    public void attachPrefetchQueries(List<WorkFragment> fragments, List<ByteString> rawParameters) {
        assert(this.prefetch.fragments == null) :
            "Trying to attach Prefetch WorkFragments more than once!";
       
        // Simply copy the references so we don't allocate more objects
        this.prefetch.fragments = fragments;
        this.prefetch.paramsRaw = rawParameters;
    }
   
    public boolean hasPrefetchParameters() {
        return (this.prefetch != null && this.prefetch.params != null);
    }
   
    public void attachPrefetchParameters(ParameterSet params[]) {
        assert(this.prefetch.params == null) :
            String.format("Trying to attach Prefetch %s more than once for %s",
                          ParameterSet.class.getSimpleName(), this);
        this.prefetch.params = params;
    }
   
    public final boolean hasPrefetchFragments() {
        return (this.prefetch != null && this.prefetch.fragments != null);
    }
   
    public final List<WorkFragment> getPrefetchFragments() {
        return (this.prefetch.fragments);
    }
   
    /**
     *
     * @return
     */
    public final List<ByteString> getPrefetchRawParameterSets() {
        return (this.prefetch.paramsRaw);
    }

    /**
     * Retrieve the ParameterSets for the prefetch queries.
     * There should be one ParameterSet array per site, which means that this
     * is shared across all partitions.
     * @return
     */
    public final ParameterSet[] getPrefetchParameterSets() {
        assert(this.prefetch.params != null);
        return (this.prefetch.params);
    }
   
    /**
     * Mark this txn as having executed a prefetched query at the given partition
     * @param partition
     */
    public final void markExecPrefetchQuery(int partition) {
        assert(this.prefetch != null);
        this.prefetch.partitions.add(partition);
    }

   
    // ----------------------------------------------------------------------------
    // DEBUG METHODS
    // ----------------------------------------------------------------------------
   
    /**
     * Get the current round counter at the given partition.
     * Note that a round is different than a batch. A "batch" contains multiple queries
     * that the txn wants to execute, of which their PlanFragments are broken
     * up into separate execution "rounds" in the PartitionExecutor.
     */
    protected int getCurrentRound(int partition) {
        return (this.round_ctr[partition]);
    }
   
    @Override
    public final String toString() {
        String str = null;
        if (this.isInitialized()) {
            str = this.toStringImpl();
        } else {
            str = String.format("<Uninitialized-%s>", this.getClass().getSimpleName());
            if (this.last_txn_id != null) str += String.format(" {LAST:%d}", this.last_txn_id);
        }
        // Include hashCode for debugging
        // str += "/" + this.hashCode();
        return (str);
    }
   
    public abstract String toStringImpl();
    public abstract String debug();
   
    @SuppressWarnings("unchecked")
    protected Map<String, Object>[] getDebugMaps() {
        List<Map<String, Object>> maps = new ArrayList<Map<String,Object>>();
        Map<String, Object> m;
       
        m = new LinkedHashMap<String, Object>();
        m.put("Transaction Id", this.txn_id);
        m.put("Procedure", this.catalog_proc);
        m.put("Base Partition", this.base_partition);
        m.put("Hash Code", this.hashCode());
        m.put("Pending Error", this.pending_error);
        m.put("Allow Early Prepare", this.allow_early_prepare);
        maps.add(m);
       
        // Predictions
        m = new LinkedHashMap<String, Object>();
        m.put("Predict Single-Partitioned", this.predict_singlePartition);
        m.put("Predict Touched Partitions", this.getPredictTouchedPartitions());
        m.put("Predict Read Only", this.isPredictReadOnly());
        m.put("Predict Abortable", this.isPredictAbortable());
        maps.add(m);
       
        // Global State
        m = new LinkedHashMap<String, Object>();
        m.put("Marked Released", PartitionSet.toString(this.released));
        m.put("Marked Prepared", PartitionSet.toString(this.prepared));
        m.put("Marked Finished", PartitionSet.toString(this.finished));
        m.put("Marked Deletable", this.checkDeletableFlag());
        maps.add(m);
       
        // Prefetch State
        if (this.prefetch != null) {
            m = new LinkedHashMap<String, Object>();
            m.put("Prefetch Partitions", this.prefetch.partitions);
            m.put("Prefetch Fragments", StringUtil.join("\n", this.prefetch.fragments));
            m.put("Prefetch Parameters", StringUtil.join("\n", this.prefetch.params));
            m.put("Prefetch Raw Parameters", StringUtil.join("\n", this.prefetch.paramsRaw));
            maps.add(m);
        }

        // Partition Execution State
        m = new LinkedHashMap<String, Object>();
        m.put("Current Round State", Arrays.toString(this.round_state));
        m.put("Exec Read-Only", PartitionSet.toString(this.exec_readOnly));
        m.put("First UndoToken", Arrays.toString(this.exec_firstUndoToken));
        m.put("Last UndoToken", Arrays.toString(this.exec_lastUndoToken));
        m.put("No Undo Buffer", PartitionSet.toString(this.exec_noUndoBuffer));
        m.put("# of Rounds", Arrays.toString(this.round_ctr));
        m.put("Executed Work", PartitionSet.toString(this.exec_eeWork));
        maps.add(m);
       
        return ((Map<String, Object>[])maps.toArray(new Map[0]));
    }
   
    public class Debug implements DebugContext {
        /**
         * Clear read/write table sets
         * <B>NOTE:</B> This should only be used for testing
         */
        public final void clearReadWriteSets() {
            for (int i = 0; i < readTables.length; i++) {
                if (readTables[i] != null) Arrays.fill(readTables[i], false);
                if (writeTables[i] != null) Arrays.fill(writeTables[i], false);
            } // FOR
        }
    }

    private Debug cachedDebugContext;
  private RpcCallback<UnevictDataResponse> unevict_callback;
  private long new_transaction_id;
    public Debug getDebugContext() {
        if (this.cachedDebugContext == null) {
            // We don't care if we're thread-safe here...
            this.cachedDebugContext = new Debug();
        }
        return this.cachedDebugContext;
    }

  public void setUnevictCallback(RpcCallback<UnevictDataResponse> done) {
    this.unevict_callback = done;
  }
  public RpcCallback<UnevictDataResponse> getUnevictCallback() {
    return this.unevict_callback;
  }

  public void setNewTransactionId(long newTransactionId) {
    this.new_transaction_id = newTransactionId;
  }
 
  public long getNewTransactionId(){
    return this.new_transaction_id;
  }

}
TOP

Related Classes of edu.brown.hstore.txns.AbstractTransaction$Debug

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.