Package edu.brown.hstore

Source Code of edu.brown.hstore.TransactionInitializer

/***************************************************************************
*   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;

import java.nio.ByteBuffer;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Random;

import org.apache.log4j.Logger;
import org.voltdb.CatalogContext;
import org.voltdb.ClientResponseImpl;
import org.voltdb.ParameterSet;
import org.voltdb.StoredProcedureInvocation;
import org.voltdb.TransactionIdManager;
import org.voltdb.catalog.Procedure;
import org.voltdb.messaging.FastDeserializer;

import com.google.protobuf.RpcCallback;

import edu.brown.hstore.conf.HStoreConf;
import edu.brown.hstore.estimators.TransactionEstimator;
import edu.brown.hstore.estimators.Estimate;
import edu.brown.hstore.estimators.EstimatorState;
import edu.brown.hstore.estimators.markov.MarkovEstimatorState;
import edu.brown.hstore.txns.AbstractTransaction;
import edu.brown.hstore.txns.LocalTransaction;
import edu.brown.hstore.txns.MapReduceTransaction;
import edu.brown.hstore.txns.RemoteTransaction;
import edu.brown.hstore.txns.TransactionUtil;
import edu.brown.logging.LoggerUtil;
import edu.brown.logging.LoggerUtil.LoggerBoolean;
import edu.brown.markov.EstimationThresholds;
import edu.brown.profilers.ProfileMeasurement;
import edu.brown.profilers.TransactionProfiler;
import edu.brown.utils.EventObservable;
import edu.brown.utils.PartitionEstimator;
import edu.brown.utils.PartitionSet;
import edu.brown.utils.StringBoxUtil;
import edu.brown.utils.StringUtil;

/**
* This class is responsible for figuring out everything about a txn before it
* starts running. It can figure out what partition to execute the txn's control
* code (i.e., program logic) on. It can also figure out additional properties, such
* as what partitions the txn will need to access, whether it is read-only at a
* partition, and whether it is likely to abort.
* <B>Note:</B> It is thread-safe so it can be used by all of the PartitionExecutors with locking
* @author pavlo
*/
public class TransactionInitializer {
    private static final Logger LOG = Logger.getLogger(TransactionInitializer.class);
    private static final LoggerBoolean debug = new LoggerBoolean();
    private static final LoggerBoolean trace = new LoggerBoolean();
    static {
        LoggerUtil.attachObserver(LOG, debug, trace);
    }
   
    // ----------------------------------------------------------------------------
    // INSTANCE MEMBERS
    // ----------------------------------------------------------------------------

    private final HStoreSite hstore_site;
    private final HStoreConf hstore_conf;
    private final CatalogContext catalogContext;
    private final PartitionEstimator p_estimator;
    private final PartitionSet local_partitions;
    private final TransactionEstimator t_estimators[];
    private final TransactionIdManager txnIdManagers[];
    private final Random rng = new Random();
    private EstimationThresholds thresholds;
   
    /**
     * HACK: This is the internal map used to keep track of TxnId->TxnHandles
     * inside of the HStoreSite.
     */
    private final Map<Long, AbstractTransaction> inflight_txns;
   
    /**
     * This is fired whenever we create a new txn handle is initialized.
     * It is only used for debugging+testing
     */
    private EventObservable<LocalTransaction> newTxnObservable;
   
    // Local Catalog Cache
    private final boolean isMapReduce[];
    private final boolean isSysProc[];
    private final boolean isReadOnly[];
    private final int expectedParams[];
   
    // ----------------------------------------------------------------------------
    // INITIALIZATION
    // ----------------------------------------------------------------------------
   
    public TransactionInitializer(HStoreSite hstore_site) {
        this.hstore_site = hstore_site;
        this.hstore_conf = hstore_site.getHStoreConf();
        this.local_partitions = hstore_site.getLocalPartitionIds();
        this.catalogContext = hstore_site.getCatalogContext();
        this.inflight_txns = hstore_site.getInflightTxns();
       
        this.thresholds = hstore_site.getThresholds();
        this.p_estimator = hstore_site.getPartitionEstimator();
        this.t_estimators = new TransactionEstimator[catalogContext.numberOfPartitions];
       
        int num_procs = this.catalogContext.procedures.size() + 1;
        this.isMapReduce = new boolean[num_procs];
        this.isSysProc = new boolean[num_procs];
        this.isReadOnly = new boolean[num_procs];
        this.expectedParams = new int[num_procs];
        for (Procedure proc : this.catalogContext.procedures) {
            int id = proc.getId();
            this.isMapReduce[id] = proc.getMapreduce();
            this.isSysProc[id] = proc.getSystemproc();
            this.isReadOnly[id] = proc.getReadonly();
            this.expectedParams[id] = proc.getParameters().size();
        } // FOR
       
        this.txnIdManagers = new TransactionIdManager[this.catalogContext.numberOfPartitions];
        for (int partition : this.local_partitions.values()) {
            this.txnIdManagers[partition] = hstore_site.getTransactionIdManager(partition);
        } // FOR
    }
   
    public synchronized EventObservable<LocalTransaction> getNewTxnObservable() {
        if (this.newTxnObservable == null) {
            this.newTxnObservable = new EventObservable<LocalTransaction>();
        }
        return (this.newTxnObservable);
    }

    // ----------------------------------------------------------------------------
    // TRANSACTION PROCESSING METHODS
    // ----------------------------------------------------------------------------

    /**
     * Calculate what partition the txn should be executed on.
     * The provided base_partition argument is the "suggestion" that
     * was embedded in the original StoredProcedureInvocation from the client
     * @param client_handle
     * @param catalog_proc
     * @param procParams
     * @param base_partition
     * @return
     */
    public int calculateBasePartition(long client_handle,
                                      Procedure catalog_proc,
                                      ParameterSet procParams,
                                      int base_partition) {
        final int procId = catalog_proc.getId();
       
        // Simple sanity check to make sure that we're not being told a bad partition
        if (base_partition < 0 || base_partition >= this.local_partitions.size()) {
            base_partition = HStoreConstants.NULL_PARTITION_ID;
        }
       
        // -------------------------------
        // DB2-style Transaction Redirection
        // -------------------------------
        if (base_partition != HStoreConstants.NULL_PARTITION_ID && hstore_conf.site.exec_db2_redirects) {
            if (debug.val)
                LOG.debug(String.format("Using embedded base partition from %s request " +
                                        "[basePartition=%d]",
                                        catalog_proc.getName(), base_partition));
        }
        // -------------------------------
        // System Procedure
        // -------------------------------
        else if (this.isSysProc[procId]) {
            if (catalog_proc.getSinglepartition() &&
                catalog_proc.getEverysite() == false &&
                catalog_proc.getPartitionparameter() >= 0) {
                if (debug.val)
                    LOG.debug(String.format("Using PartitionEstimator for %s request",
                              catalog_proc.getName()));
                try {
                    base_partition = this.p_estimator.getBasePartition(catalog_proc, procParams.toArray(), false);
                } catch (Exception ex) {
                    throw new RuntimeException(ex);
                }
            }
            else {
                // If it's a sysproc, then it doesn't need to go to a specific partition
                // We'll set it to NULL_PARTITION_ID so that we'll pick a random one down below
                if (debug.val)
                    LOG.debug(String.format("Using random local partition for %s request",
                              catalog_proc.getName()));
                base_partition = HStoreConstants.NULL_PARTITION_ID;
            }
        }
        // -------------------------------
        // PartitionEstimator
        // -------------------------------
        else if (hstore_conf.site.exec_force_localexecution == false) {
            // HACK: If they don't have enough parameters, we'll just throw them to
            // a random local partition and then let VoltProcedure give them back the proper error
            if (procParams.size() < this.expectedParams[procId]) {
                if (debug.val)
                    LOG.warn(String.format("Not enough parameters for %s. Not calculating base partition",
                             catalog_proc.getName()));
            } else {
                if (debug.val)
                    LOG.debug(String.format("Using PartitionEstimator for %s request",
                              catalog_proc.getName()));
                try {
                    base_partition = this.p_estimator.getBasePartition(catalog_proc, procParams.toArray(), false);
                } catch (Exception ex) {
                    throw new RuntimeException(ex);
                }
            }
        }
        // If we don't have a partition to send this transaction to, then we will just pick
        // one our partitions at random. This can happen if we're forcing txns to execute locally
        // or if there are no input parameters <-- this should be in the paper!!!
        if (base_partition == HStoreConstants.NULL_PARTITION_ID) {
            if (trace.val)
                LOG.trace(String.format("Selecting a random local partition to execute %s request [force_local=%s]",
                          catalog_proc.getName(), hstore_conf.site.exec_force_localexecution));
            int idx = (int)(Math.abs(client_handle) % this.local_partitions.size());
            base_partition = this.local_partitions.values()[idx];
        }
       
        return (base_partition);
    }
   
    // ----------------------------------------------------------------------------
    // TRANSACTION HANDLE CREATION METHODS
    // ----------------------------------------------------------------------------
   
    /**
     * Create and initialize a LocalTransaction from a serialized StoredProcedureInvocation
     * request sent in from the client. 
     * @param serializedRequest
     * @param client_handle
     * @param base_partition
     * @param catalog_proc
     * @param procParams
     * @param clientCallback
     * @return
     */
    public LocalTransaction createLocalTransaction(ByteBuffer serializedRequest,
                                                   long initiateTime,
                                                   long client_handle,
                                                   int base_partition,
                                                   Procedure catalog_proc,
                                                   ParameterSet procParams,
                                                   RpcCallback<ClientResponseImpl> clientCallback) {
        final int procId = catalog_proc.getId();
        if (debug.val)
            LOG.debug(String.format("Incoming %s transaction request " +
                    "[handle=%d, partition=%d]",
                      catalog_proc.getName(), client_handle, base_partition));

        // -------------------------------
        // TRANSACTION STATE INITIALIZATION
        // -------------------------------
       
        // Grab a new LocalTransactionState object from the target base partition's
        // PartitionExecutor object pool. This will be the handle that is used all
        // throughout this txn's lifespan to keep track of what it does
        LocalTransaction ts = null;
        try {
            if (this.isMapReduce[procId]) {
                ts = new MapReduceTransaction(this.hstore_site);
            } else {
                ts = new LocalTransaction(this.hstore_site);
            }
            assert(ts.isInitialized() == false);
        } catch (Throwable ex) {
            String msg = "Failed to instantiate new local transaction handle for " + catalog_proc.getName();
            throw new RuntimeException(msg, ex);
        }
       
        // Initialize our LocalTransaction handle
        Long txn_id = this.registerTransaction(ts, base_partition);
        this.populateProperties(ts,
                                txn_id,
                                initiateTime,
                                client_handle,
                                base_partition,
                                catalog_proc,
                                procParams,
                                clientCallback);
        // Check whether this guy has already been restarted before
        if (serializedRequest != null) {
            int restartCounter = StoredProcedureInvocation.getRestartCounter(serializedRequest);
            if (restartCounter > 0) {
                ts.setRestartCounter(restartCounter);
            }
        }
       
        // Notify anybody that cares about this new txn
        if (this.newTxnObservable != null) this.newTxnObservable.notifyObservers(ts);
       
        assert(ts.isSysProc() == this.isSysProc[procId]) :
            "Unexpected sysproc mismatch for " + ts;
        return (ts);
    }
   
    /**
     * Create a new LocalTransaction handle from a restart txn
     * @param orig_ts
     * @param base_partition
     * @param predict_touchedPartitions
     * @param predict_readOnly
     * @param predict_abortable
     * @return
     */
    public LocalTransaction createLocalTransaction(LocalTransaction orig_ts,
                                                   int base_partition,
                                                   PartitionSet predict_touchedPartitions,
                                                   boolean predict_readOnly,
                                                   boolean predict_abortable) {
       
        LocalTransaction new_ts = new LocalTransaction(hstore_site);
       
        // Setup TransactionProfiler
        if (hstore_conf.site.txn_profiling) {
            if (this.setupTransactionProfiler(new_ts, orig_ts.isSysProc())) {
                // Since we're restarting the txn, we should probably include
                // the original profiler information the original txn.
                // new_ts.profiler.startTransaction(ProfileMeasurement.getTime());
                new_ts.profiler.setSingledPartitioned(predict_touchedPartitions.size() == 1);               
            }
        }
        else if (new_ts.profiler != null) {
            new_ts.profiler.disableProfiling();
        }
       
        Long new_txn_id = this.registerTransaction(new_ts, base_partition);
        new_ts.init(new_txn_id,
                    orig_ts.getInitiateTime(),
                    orig_ts.getClientHandle(),
                    base_partition,
                    predict_touchedPartitions,
                    predict_readOnly,
                    predict_abortable,
                    orig_ts.getProcedure(),
                    orig_ts.getProcedureParameters(),
                    orig_ts.getClientCallback()
        );
       
        // Make sure that we remove the ParameterSet from the original LocalTransaction
        // so that they don't get returned back to the object pool when it is deleted
        orig_ts.removeProcedureParameters();
       
        // Increase the restart counter in the new transaction
        new_ts.setRestartCounter(orig_ts.getRestartCounter() + 1);
       
        // Notify anybody that cares about this new txn
        if (this.newTxnObservable != null) this.newTxnObservable.notifyObservers(new_ts);
       
        if (debug.val)
            LOG.debug(String.format("Restarted %s as %s [handle=%d, basePartition=%d]",
                      orig_ts, new_ts, orig_ts.getClientHandle(), base_partition));
       
        return (new_ts);
    }
   
    /**
     * Create a RemoteTransaction handle. This obviously only for a remote site.
     * @param txn_id
     * @param request
     * @return
     */
    public RemoteTransaction createRemoteTransaction(Long txn_id,
                                                     PartitionSet partitions,
                                                     ParameterSet procParams,
                                                     int base_partition,
                                                     int proc_id) {
        RemoteTransaction ts = null;
        Procedure catalog_proc = this.catalogContext.getProcedureById(proc_id);
        try {
            ts = new RemoteTransaction(this.hstore_site);
            assert(ts.isInitialized() == false);
            ts.init(txn_id, base_partition, procParams, catalog_proc, partitions, true);
            if (debug.val)
                LOG.debug(String.format("Creating new RemoteTransactionState %s from " +
                      "remote partition %d [partitions=%s, hashCode=%d]",
                          ts, base_partition, partitions, ts.hashCode()));
        } catch (Throwable ex) {
            String msg = "Failed to instantiate new remote transaction handle for " + TransactionUtil.formatTxnName(catalog_proc, txn_id);
            throw new RuntimeException(msg, ex);
        }
        AbstractTransaction dupe = this.inflight_txns.put(txn_id, ts);
        assert(dupe == null) : "Trying to create multiple transaction handles for " + dupe;
       
        if (trace.val)
            LOG.trace(String.format("Stored new transaction state for %s", ts));
        return (ts);
    }
   
    /**
     * Create a MapReduceTransaction handle. This should only be invoked on a remote site.
     * @param txn_id
     * @param invocation
     * @param base_partition
     * @return
     */
    public MapReduceTransaction createMapReduceTransaction(Long txn_id,
                                                           long initiateTime,
                                                           long client_handle,
                                                           int base_partition,
                                                           int procId,
                                                           ByteBuffer paramsBuffer) {
        Procedure catalog_proc = this.catalogContext.getProcedureById(procId);
        if (catalog_proc == null) {
            throw new RuntimeException("Unknown procedure id '" + procId + "'");
        }
       
        // Initialize the ParameterSet
        FastDeserializer incomingDeserializer = new FastDeserializer();
        ParameterSet procParams = new ParameterSet();
        try {
            incomingDeserializer.setBuffer(StoredProcedureInvocation.getParameterSet(paramsBuffer));
            procParams.readExternal(incomingDeserializer);
        } catch (Exception ex) {
            throw new RuntimeException(ex);
        }
        assert(procParams != null) :
            "The parameters object is null for new txn from client #" + client_handle;
       
        MapReduceTransaction ts = new MapReduceTransaction(hstore_site);
       
        // We should never already have a transaction handle for this txnId
        AbstractTransaction dupe = this.inflight_txns.put(txn_id, ts);
        assert(dupe == null) : "Trying to create multiple transaction handles for " + dupe;

        ts.init(txn_id, initiateTime, client_handle, base_partition, catalog_proc, procParams);
        if (debug.val)
            LOG.debug(String.format("Created new MapReduceTransaction state %s from remote partition %d",
                      ts, base_partition));
        return (ts);
    }
  
    // ----------------------------------------------------------------------------
    // TRANSACTION HANDLE INITIALIZATION METHODS
    // These don't normally need to be invoked from outside of this class
    // ----------------------------------------------------------------------------
   
    /**
     * This method allows you to reset the txnId for an already initialized LocalTransaction handle.
     * This is primarily needed for the AntiCacheManager stuff
     * @param ts
     * @param base_partition
     * @return
     */
    protected Long resetTransactionId(AbstractTransaction ts, int base_partition) {
        Long oldTxnId = ts.getTransactionId();
        assert(oldTxnId != null);
        AbstractTransaction removed = this.inflight_txns.remove(oldTxnId);
        assert(ts == removed);
       
        Long newTxnId = this.registerTransaction(ts, base_partition);
        ts.setTransactionId(newTxnId);
       
        if (debug.val)
            LOG.debug(String.format("Changed txnId from %d to %d: %s", oldTxnId, newTxnId, ts));
        return (newTxnId);
    }
   
    /**
     * Register a new LocalTransaction handle with this HStoreSite
     * We will return a txnId that is guaranteed to be globally unique
     * @param ts
     * @param base_partition
     * @return
     */
    protected Long registerTransaction(AbstractTransaction ts, int base_partition) {
        TransactionIdManager idManager = this.txnIdManagers[base_partition];
        Long txn_id = idManager.getNextUniqueTransactionId();
       
        // For some odd reason we sometimes get duplicate transaction ids from the VoltDB id generator
        // So we'll just double check to make sure that it's unique, and if not, we'll just ask for a new one
        AbstractTransaction dupe = this.inflight_txns.put(txn_id, ts);
        if (dupe != null) {
            // HACK!
            this.inflight_txns.put(txn_id, dupe);
            Long new_txn_id = idManager.getNextUniqueTransactionId();
            if (new_txn_id.equals(txn_id)) {
                String msg = "Duplicate transaction id #" + txn_id;
                LOG.fatal("ORIG TRANSACTION:\n" + dupe);
                LOG.fatal("NEW TRANSACTION:\n" + ts);
                Exception error = new Exception(msg);
                this.hstore_site.getCoordinator().shutdownClusterBlocking(error);
            }
            LOG.warn(String.format("Had to fix duplicate txn ids: %d -> %d", txn_id, new_txn_id));
            txn_id = new_txn_id;
            this.inflight_txns.put(txn_id, ts);
        }
       
        return (txn_id);
    }
   
    /**
     * Register a new LocalTransaction handle with this HStoreSite with a new id
     * @param ts
     * @param oldTxnId
     * @param newTxnId
     * @return
     */
    protected void registerTransactionRestartWithId(AbstractTransaction ts, Long oldTxnId, Long newTxnId) {
      AbstractTransaction removed = this.inflight_txns.remove(oldTxnId);
        assert(ts == removed);
        ts.setTransactionId(newTxnId);
       
        this.inflight_txns.put(newTxnId, ts);  
       
        if (debug.val)
            LOG.debug(String.format("Changed txnId from %d to %d: %s", oldTxnId, newTxnId, ts));
            
    }

    /**
     * Initialize the TransactionProfiler for the given txn handle.
     * Returns true if profiling is enabled for this txn.
     * @param ts
     * @param sysproc
     * @return
     */
    private boolean setupTransactionProfiler(LocalTransaction ts, boolean sysproc) {
        if (hstore_conf.site.txn_profiling &&
                sysproc == false &&
                this.rng.nextDouble() < hstore_conf.site.txn_profiling_sample) {
            if (ts.profiler == null) {
                ts.setProfiler(new TransactionProfiler());
            }
            ts.profiler.enableProfiling();
            ts.profiler.startTransaction(ProfileMeasurement.getTime());
            return (true);
        } else if (ts.profiler != null) {
            ts.profiler.disableProfiling();
        }
        return (false);
    }
   
    /**
     * Initialize the execution properties for a new transaction.
     * This is the important part where we try to figure out:
     * <ol>
     *   <li> Where should we execute the transaction (base partition).
     *   <li> What partitions the transaction will touch.
     *   <li> Whether the transaction could abort.
     *   <li> Whether the transaction is read-only.
     * </ol>
     * @param ts
     * @param client_handle
     * @param base_partition
     * @param catalog_proc
     * @param params
     * @param client_callback
     */
    private void populateProperties(LocalTransaction ts,
                                    Long txn_id,
                                    long initiateTime,
                                    long client_handle,
                                    int base_partition,
                                    Procedure catalog_proc,
                                    ParameterSet params,
                                    RpcCallback<ClientResponseImpl> client_callback) {
        final int procId = catalog_proc.getId();
        boolean predict_abortable = (hstore_conf.site.exec_no_undo_logging_all == false);
        boolean predict_readOnly = this.isReadOnly[procId];
        PartitionSet predict_partitions = null;
        EstimatorState t_state = null;
       
        // Setup TransactionProfiler
        if (hstore_conf.site.txn_profiling) {
            this.setupTransactionProfiler(ts, this.isSysProc[procId]);
        }
       
        // -------------------------------
        // SYSTEM PROCEDURES
        // -------------------------------
        if (this.isSysProc[procId]) {
            // Sysprocs can be either all partitions or single-partitioned
            // TODO: It would be nice if the client could pass us a hint when loading the tables
            // It would be just for the loading, and not regular transactions
            if (catalog_proc.getSinglepartition() && catalog_proc.getEverysite() == false) {
                predict_partitions = catalogContext.getPartitionSetSingleton(base_partition);
            } else {
                predict_partitions = catalogContext.getAllPartitionIds();
            }
        }
        // -------------------------------
        // MAPREDUCE TRANSACTIONS
        // -------------------------------
        else if (this.isMapReduce[procId]) {
            // MapReduceTransactions always need all partitions
            if (debug.val)
                LOG.debug(String.format("New request is for MapReduce %s, so it has to be " +
                      "multi-partitioned [clientHandle=%d]",
                          catalog_proc.getName(), ts.getClientHandle()));
            predict_partitions = catalogContext.getAllPartitionIds();
        }
        // -------------------------------
        // VOLTDB @PROCINFO
        // -------------------------------
        else if (hstore_conf.site.exec_voltdb_procinfo) {
            if (debug.val)
                LOG.debug(String.format("Using the catalog information to determine whether the %s transaction " +
                      "is single-partitioned [clientHandle=%d, singleP=%s]",
                          catalog_proc.getName(), ts.getClientHandle(), catalog_proc.getSinglepartition()));
            if (catalog_proc.getSinglepartition()) {
                predict_partitions = catalogContext.getPartitionSetSingleton(base_partition);
            } else {
                predict_partitions = catalogContext.getAllPartitionIds();
            }
        }
        // -------------------------------
        // FORCE DISTRIBUTED
        // -------------------------------
        else if (hstore_conf.site.exec_force_allpartitions) {
            predict_partitions = catalogContext.getAllPartitionIds();
        }
        // -------------------------------
        // TRANSACTION ESTIMATORS
        // -------------------------------
        else if (hstore_conf.site.markov_enable || hstore_conf.site.markov_fixed) {
            // Grab the TransactionEstimator for the destination partition and figure out whether
            // this mofo is likely to be single-partition or not. Anything that we can't estimate
            // will just have to be multi-partitioned. This includes sysprocs
            TransactionEstimator t_estimator = this.t_estimators[base_partition];
            if (t_estimator == null) {
                t_estimator = this.hstore_site.getPartitionExecutor(base_partition).getTransactionEstimator();
                this.t_estimators[base_partition] = t_estimator;
            }
           
            try {
                if (hstore_conf.site.txn_profiling && ts.profiler != null) ts.profiler.startInitEstimation();
                if (t_estimator != null) {
                    if (debug.val)
                        LOG.debug(String.format("%s - Using %s to populate txn properties [clientHandle=%d]",
                                  TransactionUtil.formatTxnName(catalog_proc, txn_id),
                                  t_estimator.getClass().getSimpleName(), client_handle));
                    t_state = t_estimator.startTransaction(txn_id, base_partition, catalog_proc, params.toArray());
                }
               
                // If there is no EstimatorState, then there is nothing we can do
                // It has to be executed as multi-partitioned
                if (t_state == null) {
                    if (debug.val) {
                        LOG.debug(String.format("%s - No %s was returned. Using default estimate.",
                                  TransactionUtil.formatTxnName(catalog_proc, txn_id),
                                  EstimatorState.class.getSimpleName()));
                    }
                }
                // We have a EstimatorState handle, so let's see what it says...
                else {
                    if (trace.val)
                        LOG.trace("\n" + StringBoxUtil.box(t_state.toString()));
                    Estimate t_estimate = t_state.getInitialEstimate();
                   
                    // Bah! We didn't get back a Estimation for some reason...
                    if (t_estimate == null) {
                        if (debug.val)
                            LOG.debug(String.format("%s - No %s handle was return. Using default estimate.",
                                      TransactionUtil.formatTxnName(catalog_proc, txn_id),
                                      Estimate.class.getSimpleName()));
                    }
                    // Invalid Estimation. Stick with defaults
                    else if (t_estimate.isValid() == false) {
                        if (debug.val)
                            LOG.debug(String.format("%s - %s is marked as invalid. Using default estimate.\n%s",
                                      TransactionUtil.formatTxnName(catalog_proc, txn_id),
                                      t_estimate.getClass().getSimpleName(), t_estimate));
                    }   
                    // Use Estimation to determine things
                    else {
                        if (debug.val) {
                            LOG.debug(String.format("%s - Using %s to determine if txn is single-partitioned",
                                      TransactionUtil.formatTxnName(catalog_proc, txn_id),
                                      t_estimate.getClass().getSimpleName()));
                            LOG.trace(String.format("%s %s:\n%s",
                                      TransactionUtil.formatTxnName(catalog_proc, txn_id),
                                      t_estimate.getClass().getSimpleName(), t_estimate));
                        }
                        predict_partitions = t_estimate.getTouchedPartitions(this.thresholds);
                        predict_readOnly = t_estimate.isReadOnlyAllPartitions(this.thresholds);
                        predict_abortable = (predict_partitions.size() == 1 ||
                                             predict_readOnly == false ||
                                             t_estimate.isAbortable(this.thresholds));
                       
                        // Check whether the TransactionEstimator *really* thinks that we should
                        // give it updates about this txn. If the flag is false, then we'll
                        // check whether the updates are enabled in the HStoreConf parameters
                        if (t_state.shouldAllowUpdates() == false) {
                            if (predict_partitions.size() == 1) {
                                if (hstore_conf.site.markov_singlep_updates == false) t_state.disableUpdates();
                            }
                            else if (hstore_conf.site.markov_dtxn_updates == false) {
                                t_state.disableUpdates();
                            }
                        }
                       
                        if (debug.val && predict_partitions.isEmpty()) {
                            LOG.warn(String.format("%s - Unexpected empty predicted %s from %s [updatesEnabled=%s]\n%s",
                                TransactionUtil.formatTxnName(catalog_proc, txn_id),
                                PartitionSet.class.getSimpleName(),
                                t_estimator.getClass().getSimpleName(),
                                t_state.isUpdatesEnabled(), t_estimate));
                            ts.setAllowEarlyPrepare(false);
//                            System.err.println("WROTE MARKOVGRAPH: " + ((MarkovEstimatorState)t_state).dumpMarkovGraph());
//                            System.err.flush();
//                            HStore.crashDB();
                        }
                    }
                }
            } catch (Throwable ex) {
                if (t_state != null && t_state instanceof MarkovEstimatorState) {
                    LOG.warn("WROTE MARKOVGRAPH: " + ((MarkovEstimatorState)t_state).dumpMarkovGraph());
                }
                LOG.error(String.format("Failed calculate estimate for %s request\nParameters: %s",
                          TransactionUtil.formatTxnName(catalog_proc, txn_id),
                          params), ex);
                ex.printStackTrace();
                predict_partitions = catalogContext.getAllPartitionIds();
                predict_readOnly = false;
                predict_abortable = true;
            } finally {
                if (hstore_conf.site.txn_profiling && ts.profiler != null) ts.profiler.stopInitEstimation();
            }
        }
       
        if (predict_partitions == null || predict_partitions.isEmpty()) {
            // -------------------------------
            // FORCE SINGLE-PARTITIONED
            // -------------------------------
            if (hstore_conf.site.exec_force_singlepartitioned) {
                if (debug.val)
                    LOG.debug(String.format("The \"Always Single-Partitioned\" flag is true. " +
                          "Marking new %s transaction as single-partitioned on partition %d [clientHandle=%d]",
                              catalog_proc.getName(), base_partition, client_handle));
                predict_partitions = catalogContext.getPartitionSetSingleton(base_partition);
            }
            // -------------------------------
            // FORCE MULTI-PARTITIONED
            // -------------------------------
            else {
                predict_partitions = catalogContext.getAllPartitionIds();
            }
        }
        assert(predict_partitions != null);
        assert(predict_partitions.isEmpty() == false);
       
        // -------------------------------
        // SET EXECUTION PROPERTIES
        // -------------------------------
       
        ts.init(txn_id,
                initiateTime,
                client_handle,
                base_partition,
                predict_partitions,
                predict_readOnly,
                predict_abortable,
                catalog_proc,
                params,
                client_callback);
        if (t_state != null) ts.setEstimatorState(t_state);
        if (hstore_conf.site.txn_profiling && ts.profiler != null)
            ts.profiler.setSingledPartitioned(ts.isPredictSinglePartition());
       
        if (debug.val) {
            Map<String, Object> m = new LinkedHashMap<String, Object>();
            m.put("ClientHandle", client_handle);
            m.put("Partitions", ts.getPredictTouchedPartitions());
            m.put("Single Partition", ts.isPredictSinglePartition());
            m.put("Read Only", ts.isPredictReadOnly());
            m.put("Abortable", ts.isPredictAbortable());
            LOG.debug(String.format("Initializing %s on partition %d\n%s",
                      ts, base_partition, StringUtil.formatMaps(m).trim()));
        }
    }

}
TOP

Related Classes of edu.brown.hstore.TransactionInitializer

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.