Package org.voltdb.client

Source Code of org.voltdb.client.ClientImpl$CSL

/* This file is part of VoltDB.
* Copyright (C) 2008-2010 VoltDB L.L.C.
*
* VoltDB is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* VoltDB is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with VoltDB.  If not, see <http://www.gnu.org/licenses/>.
*/

package org.voltdb.client;

import java.io.IOException;
import java.net.UnknownHostException;
import java.util.Random;
import java.util.concurrent.atomic.AtomicLong;

import org.apache.log4j.Logger;
import org.voltdb.CatalogContext;
import org.voltdb.StoredProcedureInvocation;
import org.voltdb.StoredProcedureInvocationHints;
import org.voltdb.VoltTable;
import org.voltdb.catalog.Catalog;
import org.voltdb.catalog.Procedure;
import org.voltdb.messaging.FastSerializer;
import org.voltdb.utils.DBBPool.BBContainer;

import edu.brown.catalog.CatalogUtil;
import edu.brown.hstore.HStoreConstants;
import edu.brown.hstore.Hstoreservice.Status;
import edu.brown.hstore.conf.HStoreConf;
import edu.brown.logging.LoggerUtil;
import edu.brown.logging.LoggerUtil.LoggerBoolean;
import edu.brown.profilers.ProfileMeasurement;
import edu.brown.utils.PartitionEstimator;

/**
*  A client that connects to one or more nodes in a VoltCluster
*  and provides methods to call stored procedures and receive
*  responses.
*/
final class ClientImpl implements Client {
    private static final Logger LOG = Logger.getLogger(ClientImpl.class);
    private static final LoggerBoolean debug = new LoggerBoolean();
    private static final LoggerBoolean trace = new LoggerBoolean();
    static {
        LoggerUtil.attachObserver(LOG, debug, trace);
    }

    private final AtomicLong m_handle = new AtomicLong(new Random().nextLong() * -1l); // Long.MAX_VALUE

    private final int m_expectedOutgoingMessageSize;

    /****************************************************
                        Public API
     ****************************************************/

    /**
     * Clients may queue a wide variety of messages and a lot of them
     * so give them a large max arena size.
     */
    private static final int m_defaultMaxArenaSize = 134217728;
    private volatile boolean m_isShutdown = false;

    /**
     * If we have a catalog, then we'll enable client-side hints
     */
    private Catalog m_catalog;
    private CatalogContext m_catalogContext;
    private PartitionEstimator m_pEstimator;
    private int m_partitionSiteXref[];
    private final HStoreConf m_hstoreConf;
    private final ProfileMeasurement m_queueTime = new ProfileMeasurement("queue");

    /** Create a new client without any initial connections. */
    ClientImpl() {
        this( 128, new int[] {
                m_defaultMaxArenaSize,//16
                m_defaultMaxArenaSize,//32
                m_defaultMaxArenaSize,//64
                m_defaultMaxArenaSize,//128
                m_defaultMaxArenaSize,//256
                m_defaultMaxArenaSize,//512
                m_defaultMaxArenaSize,//1024
                m_defaultMaxArenaSize,//2048
                m_defaultMaxArenaSize,//4096
                m_defaultMaxArenaSize,//8192
                m_defaultMaxArenaSize,//16384
                m_defaultMaxArenaSize,//32768
                m_defaultMaxArenaSize,//65536
                m_defaultMaxArenaSize,//131072
                m_defaultMaxArenaSize//262144
        },
        false,
        null,
        null);
    }

    /**
     * Create a new client without any initial connections.
     * Also provide a hint indicating the expected serialized size of
     * most outgoing procedure invocations. This helps size initial allocations
     * for serializing network writes
     * @param expectedOutgoingMessageSize Expected size of procedure invocations in bytes
     * @param maxArenaSizes Maximum size arenas in the memory pool should grow to
     * @param heavyweight Whether to use multiple or a single thread
     */
    ClientImpl(
            int expectedOutgoingMessageSize,
            int maxArenaSizes[],
            boolean heavyweight,
            StatsUploaderSettings statsSettings,
            Catalog catalog) {
        m_expectedOutgoingMessageSize = expectedOutgoingMessageSize;

        m_hstoreConf = HStoreConf.singleton(true);

        if (catalog != null && m_hstoreConf.client.txn_hints) {
            m_catalog = catalog;
            m_catalogContext = new CatalogContext(m_catalog);
            m_pEstimator = new PartitionEstimator(m_catalogContext);
            m_partitionSiteXref = CatalogUtil.getPartitionSiteXrefArray(m_catalog);
    }

        m_distributer = new Distributer(
                expectedOutgoingMessageSize,
                maxArenaSizes,
                heavyweight,
                m_hstoreConf.global.nanosecond_latencies,
                statsSettings);
        m_distributer.addClientStatusListener(new CSL());
    }

    /**
     * Create a connection to another VoltDB node.
     * @param host
     * @param password
     * @param program
     * @throws UnknownHostException
     * @throws IOException
     */
    public void createConnection(String host, int port) throws UnknownHostException, IOException {
        if (m_isShutdown) {
            throw new IOException("Client instance is shutdown");
        }
        String subProgram = "default";
        String subPassword = "password";
        m_distributer.createConnection(null, host, port, subProgram, subPassword);
    }
   
    /**
     * Create a connection to another VoltDB node.
     * @param host
     * @param password
     * @param program
     * @throws UnknownHostException
     * @throws IOException
     */
    public void createConnection(Integer site_id, String host, int port, String program, String password)
        throws UnknownHostException, IOException
    {
        if (m_isShutdown) {
            throw new IOException("Client instance is shutdown");
        }
        final String subProgram = (program == null) ? "" : program;
        final String subPassword = (password == null) ? "" : password;
        m_distributer.createConnection(site_id, host, port, subProgram, subPassword);
    }

    /**
     * Synchronously invoke a procedure call blocking until a result is available.
     * @param procName class name (not qualified by package) of the procedure to execute.
     * @param parameters vararg list of procedure's parameter values.
     * @return array of VoltTable results.
     * @throws org.voltdb.client.ProcCallException
     * @throws NoConnectionsException
     */
    public final ClientResponse callProcedure(String procName, Object... parameters)
        throws IOException, NoConnectionsException, ProcCallException
    {
        return this.callProcedure(procName, null, parameters);
    }
   
    /**
     * Synchronously invoke a procedure call blocking until a result is available.
     * @param procName class name (not qualified by package) of the procedure to execute.
     * @param hints Extra information about what the transaction will do.
     * @param parameters vararg list of procedure's parameter values.
     * @return array of VoltTable results.
     * @throws org.voltdb.client.ProcCallException
     * @throws NoConnectionsException
     */
    public final ClientResponse callProcedure(String procName, StoredProcedureInvocationHints hints, Object... parameters)
        throws IOException, NoConnectionsException, ProcCallException
    {
        if (m_isShutdown) {
            throw new NoConnectionsException("Client instance is shutdown");
        }
        final SyncCallback cb = new SyncCallback();
        cb.setArgs(parameters);
        final StoredProcedureInvocation invocation =
              new StoredProcedureInvocation(m_handle.getAndIncrement(), procName, parameters);

        Integer site_id = null;
        if (hints != null && hints.basePartition != HStoreConstants.NULL_PARTITION_ID) {
            invocation.setBasePartition(hints.basePartition);
            if (m_partitionSiteXref != null) {
                site_id = m_partitionSiteXref[hints.basePartition];
            }
        }
        else if (m_catalog != null && procName.startsWith("@") == false) {
            try {
                int partition = m_pEstimator.getBasePartition(invocation);
                if (partition != HStoreConstants.NULL_PARTITION_ID) {
                    site_id = m_partitionSiteXref[partition];
                    invocation.setBasePartition(partition);
                }
            } catch (Exception ex) {
                throw new RuntimeException("Failed to estimate base partition for new invocation of '" + procName + "'", ex);
            }
        }

        long start = ProfileMeasurement.getTime();
        m_distributer.queue(
                invocation,
                cb,
                m_expectedOutgoingMessageSize,
                true,
                site_id);
        m_queueTime.appendTime(start, ProfileMeasurement.getTime());

        try {
            if (trace.val)
                LOG.trace(String.format("Waiting for response for %s txn [clientHandle=%d]",
                                        procName, invocation.getClientHandle()));
            cb.waitForResponse();
        } catch (final InterruptedException e) {
            throw new java.io.InterruptedIOException("Interrupted while waiting for response");
        }
        if (cb.getResponse().getStatus() != Status.OK) {
          if (debug.val) LOG.error("the response failed!!!");
            throw new ProcCallException(cb.getResponse(), cb.getResponse().getStatusString(), cb.getResponse().getException());
        }
        // cb.result() throws ProcCallException if procedure failed
        return cb.getResponse();
    }

    /**
     * Asynchronously invoke a procedure call.
     * @param callback TransactionCallback that will be invoked with procedure results.
     * @param procName class name (not qualified by package) of the procedure to execute.
     * @param parameters vararg list of procedure's parameter values.
     * @return True if the procedure was queued and false otherwise
     */
    public final boolean callProcedure(ProcedureCallback callback, String procName, Object... parameters)
    throws IOException, NoConnectionsException {
        if (m_isShutdown) {
            return false;
        }
        return callProcedure(callback, m_expectedOutgoingMessageSize, procName, null, parameters);
    }
   
    public final boolean callProcedure(ProcedureCallback callback, String procName, StoredProcedureInvocationHints hints, Object... parameters)
    throws IOException, NoConnectionsException {
        if (m_isShutdown) {
            return false;
        }
        return callProcedure(callback, m_expectedOutgoingMessageSize, procName, hints, parameters);
    }

    @Override
    public int calculateInvocationSerializedSize(String procName,
            Object... parameters) {
        final StoredProcedureInvocation invocation =
            new StoredProcedureInvocation(0, procName, parameters);
        final FastSerializer fds = new FastSerializer();
        int size = 0;
        try {
            final BBContainer c = fds.writeObjectForMessaging(invocation);
            size = c.b.remaining();
            c.discard();
        } catch (final IOException e) {
            throw new RuntimeException(e);
        }
        return size;
    }

    @Override
    public final boolean callProcedure(
            ProcedureCallback callback,
            int expectedSerializedSize,
            String procName,
            StoredProcedureInvocationHints hints,
            Object... parameters)
            throws IOException, NoConnectionsException {
        if (m_isShutdown) {
            return false;
        }
        if (callback == null) {
            callback = new NullCallback();
        } else if (callback instanceof ProcedureArgumentCacher) {
            ((ProcedureArgumentCacher)callback).setArgs(parameters);
        }
        StoredProcedureInvocation invocation =
            new StoredProcedureInvocation(m_handle.getAndIncrement(), procName, parameters);

        Integer site_id = null;
        if (m_catalog != null) {
            Procedure catalog_proc = m_catalogContext.procedures.getIgnoreCase(procName);
            if (catalog_proc != null) {
                // OPTIMIZATION: If we have the the catalog, then we'll send just
                // the procId. This reduces the number of strings that we need to
                // allocate on the server side.
                invocation.setProcedureId(catalog_proc.getId());
               
                // OPTIMIZATION: If this isn't a sysproc, then we can tell them
                // what the base partition for this request will be
                if ((hints == null || hints.basePartition == HStoreConstants.NULL_PARTITION_ID) &&
                    catalog_proc.getSystemproc() == false) {
                    try {
                        int partition = m_pEstimator.getBasePartition(invocation);
                        if (partition != HStoreConstants.NULL_PARTITION_ID) {
                            site_id = m_partitionSiteXref[partition];
                            invocation.setBasePartition(partition);
                        }
                    } catch (Exception ex) {
                        throw new RuntimeException("Failed to estimate base partition for new invocation of '" + procName + "'", ex);
                    }
                }
            }
        }
        if (hints != null && hints.basePartition != HStoreConstants.NULL_PARTITION_ID) {
            invocation.setBasePartition(hints.basePartition);
        }

        if (m_blockingQueue) {
            long start = ProfileMeasurement.getTime();
            while (!m_distributer.queue(invocation, callback, expectedSerializedSize, true, site_id)) {
                try {
                    backpressureBarrier();
                } catch (InterruptedException e) {
                    throw new java.io.InterruptedIOException("Interrupted while invoking procedure asynchronously");
                }
            }
            m_queueTime.appendTime(start, ProfileMeasurement.getTime(), 1);
            return true;
        } else {
            long start = ProfileMeasurement.getTime();
            boolean ret = m_distributer.queue(invocation, callback, expectedSerializedSize, false, site_id);
            m_queueTime.appendTime(start, ProfileMeasurement.getTime(), 1);
            return ret;
        }
    }

    public void drain() throws NoConnectionsException, InterruptedException {
        if (m_isShutdown) {
            return;
        }
        m_distributer.drain();
    }

    /**
     * Shutdown the client closing all network connections and release
     * all memory resources.
     * @throws InterruptedException
     */
    public void close() throws InterruptedException {
        m_isShutdown = true;
        synchronized (m_backpressureLock) {
            m_backpressureLock.notifyAll();
        }
        m_distributer.shutdown();
    }

    public void addClientStatusListener(ClientStatusListener listener) {
        m_distributer.addClientStatusListener(listener);
    }

    public boolean removeClientStatusListener(ClientStatusListener listener) {
        return m_distributer.removeClientStatusListener(listener);
    }

    public void backpressureBarrier() throws InterruptedException {
        if (m_isShutdown) {
            return;
        }
        if (m_backpressure) {
            synchronized (m_backpressureLock) {
                while (m_backpressure && !m_isShutdown) {
                    if (debug.val)
                        LOG.debug(String.format("Blocking client due to backup pressure [backPressure=%s, #connections=%d]",
                                                m_backpressure, m_distributer.getConnectionCount()));
                    m_backpressureLock.wait();
                    m_backpressure = false;
                    if (debug.val)
                        LOG.debug(String.format("Unblocking client [m_backpressure=%s]", m_backpressure));
                    break;
                } // WHILE
            } // SYNCH
        }
    }

    class CSL implements ClientStatusListener {

        @Override
        public void backpressure(boolean status) {
            synchronized (m_backpressureLock) {
                if (status) {
                    m_backpressure = true;
                } else {
                    m_backpressure = false;
                    m_backpressureLock.notifyAll();
                }
            } // SYNCH
        }

        @Override
        public void connectionLost(String hostname, int connectionsLeft) {
            if (connectionsLeft == 0) {
                //Wake up client and let it attempt to queue work
                //and then fail with a NoConnectionsException
                synchronized (m_backpressureLock) {
                    m_backpressure = false;
                    m_backpressureLock.notifyAll();
                }
            }
        }

        @Override
        public void uncaughtException(ProcedureCallback callback, ClientResponse r, Throwable e) {
        }

    }
     /****************************************************
                        Implementation
     ****************************************************/



    // static final Logger LOG = Logger.getLogger(ClientImpl.class.getName());  // Logger shared by client package.
    private final Distributer m_distributer;                             // de/multiplexes connections to a cluster
    private final Object m_backpressureLock = new Object();
    private boolean m_backpressure = false;
    private boolean m_blockingQueue = false;

    @Override
    public void configureBlocking(boolean blocking) {
        LOG.info("Set Blocking Queue: " + blocking);
        m_blockingQueue = blocking;
    }

    @Override
    public VoltTable getIOStats() {
        return m_distributer.getConnectionStats(false);
    }

    @Override
    public VoltTable getIOStatsInterval() {
        return m_distributer.getConnectionStats(true);
    }

    @Override
    public Object[] getInstanceId() {
        return m_distributer.getInstanceId();
    }

    @Override
    public VoltTable getProcedureStats() {
        return m_distributer.getProcedureStats(false);
    }

    @Override
    public VoltTable getProcedureStatsInterval() {
        return m_distributer.getProcedureStats(false);
    }

    @Override
    public String getBuildString() {
        return m_distributer.getBuildString();
    }

    @Override
    public boolean blocking() {
        return m_blockingQueue;
    }

    @Override
    public ProfileMeasurement getQueueTime() {
        return m_queueTime;
    }
}
TOP

Related Classes of org.voltdb.client.ClientImpl$CSL

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.