Java tutorial
/* 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.nio.ByteBuffer; import java.nio.channels.SocketChannel; import java.sql.SQLException; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.concurrent.atomic.AtomicBoolean; import org.apache.commons.collections15.map.ListOrderedMap; import org.apache.log4j.Logger; import org.voltdb.ClientResponseImpl; import org.voltdb.StoredProcedureInvocation; import org.voltdb.VoltTable; import org.voltdb.VoltTable.ColumnInfo; import org.voltdb.VoltType; import org.voltdb.messaging.FastDeserializer; import org.voltdb.messaging.FastSerializable; import org.voltdb.messaging.FastSerializer; import org.voltdb.network.Connection; import org.voltdb.network.QueueMonitor; import org.voltdb.network.VoltNetwork; import org.voltdb.network.VoltProtocolHandler; import org.voltdb.utils.DBBPool; import org.voltdb.utils.DBBPool.BBContainer; import org.voltdb.utils.Pair; import edu.brown.hstore.HStoreThreadManager; import edu.brown.hstore.Hstoreservice; import edu.brown.logging.LoggerUtil; import edu.brown.logging.LoggerUtil.LoggerBoolean; import edu.brown.utils.CollectionUtil; import edu.brown.utils.StringUtil; /** * De/multiplexes transactions across a cluster * * It is safe to synchronized on an individual connection and then the distributer, but it is always unsafe * to synchronized on the distributer and then an individual connection. */ class Distributer { private static final Logger LOG = Logger.getLogger(Distributer.class); private static final LoggerBoolean debug = new LoggerBoolean(LOG.isDebugEnabled()); private static final LoggerBoolean trace = new LoggerBoolean(LOG.isTraceEnabled()); static { LoggerUtil.attachObserver(LOG, debug, trace); } // collection of connections to the cluster private final ArrayList<NodeConnection> m_connections = new ArrayList<NodeConnection>(); /** SiteId -> NodeConnection */ private final Map<Integer, Collection<NodeConnection>> m_connectionSiteXref = new HashMap<Integer, Collection<NodeConnection>>(); private final ArrayList<ClientStatusListener> m_listeners = new ArrayList<ClientStatusListener>(); //Selector and connection handling, does all work in blocking selection thread private final VoltNetwork m_network; // Temporary until a distribution/affinity algorithm is written private int m_nextConnection = 0; private final int m_expectedOutgoingMessageSize; private final DBBPool m_pool; private final boolean m_useMultipleThreads; private final int m_backpressureWait; private final String m_hostname; /** * Server's instances id. Unique for the cluster */ private Object m_clusterInstanceId[]; private final ClientStatsLoader m_statsLoader; private String m_buildString; private static class ProcedureStats { private final String m_name; private long m_invocationsCompleted = 0; private long m_lastInvocationsCompleted = 0; private long m_invocationAborts = 0; private long m_lastInvocationAborts = 0; private long m_invocationErrors = 0; private long m_lastInvocationErrors = 0; private long m_restartCounter = 0; // cumulative latency measured by client, used to calculate avg. lat. private long m_roundTripTime = 0; private long m_lastRoundTripTime = 0; private int m_maxRoundTripTime = Integer.MIN_VALUE; private int m_lastMaxRoundTripTime = Integer.MIN_VALUE; private int m_minRoundTripTime = Integer.MAX_VALUE; private int m_lastMinRoundTripTime = Integer.MAX_VALUE; // cumulative latency measured by the cluster, used to calculate avg lat. private long m_clusterRoundTripTime = 0; private long m_lastClusterRoundTripTime = 0; // 10ms buckets. Last bucket is all transactions > 190ms. static int m_numberOfBuckets = 20; private long m_clusterRoundTripTimeBuckets[] = new long[m_numberOfBuckets]; private long m_roundTripTimeBuckets[] = new long[m_numberOfBuckets]; private int m_maxClusterRoundTripTime = Integer.MIN_VALUE; private int m_lastMaxClusterRoundTripTime = Integer.MIN_VALUE; private int m_minClusterRoundTripTime = Integer.MAX_VALUE; private int m_lastMinClusterRoundTripTime = Integer.MAX_VALUE; public ProcedureStats(String name) { m_name = name; } public void update(int roundTripTime, int clusterRoundTripTime, boolean abort, boolean error, int restartCounter) { m_maxRoundTripTime = Math.max(roundTripTime, m_maxRoundTripTime); m_lastMaxRoundTripTime = Math.max(roundTripTime, m_lastMaxRoundTripTime); m_minRoundTripTime = Math.min(roundTripTime, m_minRoundTripTime); m_lastMinRoundTripTime = Math.max(roundTripTime, m_lastMinRoundTripTime); m_maxClusterRoundTripTime = Math.max(clusterRoundTripTime, m_maxClusterRoundTripTime); m_lastMaxClusterRoundTripTime = Math.max(clusterRoundTripTime, m_lastMaxClusterRoundTripTime); m_minClusterRoundTripTime = Math.min(clusterRoundTripTime, m_minClusterRoundTripTime); m_lastMinClusterRoundTripTime = Math.min(clusterRoundTripTime, m_lastMinClusterRoundTripTime); m_invocationsCompleted++; if (abort) { m_invocationAborts++; } if (error) { m_invocationErrors++; } m_roundTripTime += roundTripTime; m_clusterRoundTripTime += clusterRoundTripTime; m_restartCounter += restartCounter; // calculate the latency buckets to increment and increment. int rttBucket = (int) (Math.floor(roundTripTime / 10)); if (rttBucket >= m_roundTripTimeBuckets.length) { rttBucket = m_roundTripTimeBuckets.length - 1; } m_roundTripTimeBuckets[rttBucket] += 1; int rttClusterBucket = (int) (Math.floor(clusterRoundTripTime / 10)); if (rttClusterBucket >= m_clusterRoundTripTimeBuckets.length) { rttClusterBucket = m_clusterRoundTripTimeBuckets.length - 1; } m_clusterRoundTripTimeBuckets[rttClusterBucket] += 1; } } class CallbackValues { final long time; final ProcedureCallback callback; final String name; public CallbackValues(long time, ProcedureCallback callback, String name) { this.time = time; this.callback = callback; this.name = name; } } class NodeConnection extends VoltProtocolHandler implements org.voltdb.network.QueueMonitor { private final HashMap<Long, CallbackValues> m_callbacks; private final HashMap<String, ProcedureStats> m_stats = new HashMap<String, ProcedureStats>(); private final int m_hostId; private final long m_connectionId; private Connection m_connection; private String m_hostname; private int m_port; private boolean m_isConnected = true; private final AtomicBoolean m_hasBackPressure = new AtomicBoolean(false); private long m_hasBackPressureTimestamp = -1; private int m_lastServerTimestamp = Integer.MIN_VALUE; private long m_invocationsThrottled = 0; private long m_lastInvocationsThrottled = 0; private long m_invocationsCompleted = 0; private long m_lastInvocationsCompleted = 0; private long m_invocationAborts = 0; private long m_lastInvocationAborts = 0; private long m_invocationErrors = 0; private long m_lastInvocationErrors = 0; public NodeConnection(long ids[]) { m_callbacks = new HashMap<Long, CallbackValues>(); m_hostId = (int) ids[0]; m_connectionId = ids[1]; } @Override public String toString() { return (String.format("NodeConnection[id=%d, host=%s, port=%d]", m_hostId, m_hostname, m_port)); } public void createWork(long now, long handle, String name, BBContainer c, ProcedureCallback callback) { synchronized (this) { if (!m_isConnected) { final ClientResponse r = new ClientResponseImpl(-1, -1, -1, Hstoreservice.Status.ABORT_CONNECTION_LOST, new VoltTable[0], "Connection to database host (" + m_hostname + ") was lost before a response was received"); callback.clientCallback(r); c.discard(); return; } m_callbacks.put(handle, new CallbackValues(now, callback, name)); } m_connection.writeStream().enqueue(c); } public void createWork(long now, long handle, String name, FastSerializable f, ProcedureCallback callback) { synchronized (this) { if (!m_isConnected) { final ClientResponse r = new ClientResponseImpl(-1, -1, -1, Hstoreservice.Status.ABORT_CONNECTION_LOST, new VoltTable[0], "Connection to database host (" + m_hostname + ") was lost before a response was received"); callback.clientCallback(r); return; } m_callbacks.put(handle, new CallbackValues(now, callback, name)); } m_connection.writeStream().enqueue(f); } private void updateStats(String name, int roundTrip, int clusterRoundTrip, boolean abort, boolean error, int restartCounter) { ProcedureStats stats = m_stats.get(name); if (stats == null) { stats = new ProcedureStats(name); m_stats.put(name, stats); } stats.update(roundTrip, clusterRoundTrip, abort, error, restartCounter); } @Override public void handleMessage(ByteBuffer buf, Connection c) { ClientResponseImpl response = null; FastDeserializer fds = new FastDeserializer(buf); try { response = fds.readObject(ClientResponseImpl.class); } catch (IOException e) { LOG.error("Invalid ClientResponse object returned by " + this, e); } ProcedureCallback cb = null; long callTime = 0; int delta = 0; if (response == null) { LOG.warn("Got back null ClientResponse. Ignoring..."); return; } final long clientHandle = response.getClientHandle(); final boolean should_throttle = response.getThrottleFlag(); final Hstoreservice.Status status = response.getStatus(); final int timestamp = response.getRequestCounter(); final int restart_counter = response.getRestartCounter(); boolean abort = false; boolean error = false; CallbackValues stuff = null; long now = System.currentTimeMillis(); synchronized (this) { stuff = m_callbacks.remove(clientHandle); if (stuff != null) { callTime = stuff.time; delta = (int) (now - callTime); cb = stuff.callback; m_invocationsCompleted++; if (debug.get()) { Map<String, Object> m0 = new ListOrderedMap<String, Object>(); m0.put("Txn #", response.getTransactionId()); m0.put("Status", response.getStatus()); m0.put("ClientHandle", clientHandle); m0.put("ThrottleFlag", should_throttle); m0.put("Timestamp", timestamp); m0.put("RestartCounter", restart_counter); Map<String, Object> m1 = new ListOrderedMap<String, Object>(); m1.put("Connection", this); m1.put("Back Pressure", m_hasBackPressure.get()); m1.put("BackPressure Timestamp", m_hasBackPressureTimestamp); m1.put("Last Server Timestamp", m_lastServerTimestamp); m1.put("Completed Invocations", m_invocationsCompleted); m1.put("Error Invocations", m_invocationErrors); m1.put("Abort Invocations", m_invocationAborts); m1.put("Throttled Invocations", m_invocationsThrottled); LOG.debug("ClientResponse Information:\n" + StringUtil.formatMaps(m0, m1)); } // BackPressure (Throttle) // If this response's timestamp is greater than the last one that we processed, then we'll allow it to modify whether // we are throttled or not. This ensures that we don't get stuck because the messages came back out of order if (timestamp > m_lastServerTimestamp) { m_lastServerTimestamp = timestamp; if (should_throttle == false && m_hasBackPressure.compareAndSet(true, should_throttle)) { if (debug.get()) LOG.debug(String.format("Disabling throttling mode [counter=%d]", m_invocationsThrottled)); m_connection.writeStream().setBackPressure(false); } else if (should_throttle == true && m_hasBackPressure.compareAndSet(false, true)) { m_invocationsThrottled++; if (debug.get()) LOG.debug(String.format("Enabling throttling mode [counter=%d]", m_invocationsThrottled)); m_connection.writeStream().setBackPressure(true); m_hasBackPressureTimestamp = now; } } } else { LOG.warn("Failed to get callback for client handle #" + clientHandle + " from " + this); } } // SYNCH if (stuff != null) { if (status == Hstoreservice.Status.ABORT_USER || status == Hstoreservice.Status.ABORT_GRACEFUL) { m_invocationAborts++; abort = true; } else if (status != Hstoreservice.Status.OK) { m_invocationErrors++; error = true; } updateStats(stuff.name, delta, response.getClusterRoundtrip(), abort, error, restart_counter); } if (cb != null) { // We always need to call this so that we unblock the blocking client // if (status != Hstoreservice.Status.ABORT_THROTTLED && status != Hstoreservice.Status.ABORT_REJECT) { response.setClientRoundtrip(delta); cb.clientCallback(response); //} } else if (m_isConnected) { // TODO: what's the right error path here? // LOG.warn("No callback available for clientHandle " + clientHandle); // assert(false); } } /** * A number specify the expected size of the majority of outgoing messages. * Used to determine the tipping point between where a heap byte buffer vs. direct byte buffer will be * used. Also effects the usage of gathering writes. */ @Override public int getExpectedOutgoingMessageSize() { return m_expectedOutgoingMessageSize; } @Override public int getMaxRead() { return Integer.MAX_VALUE; } public boolean hadBackPressure(long now) { if (trace.get()) LOG.trace(String.format("Checking whether %s has backup pressure: %s", m_connection, m_hasBackPressure)); if (m_hasBackPressure.get()) { if (now - m_hasBackPressureTimestamp > m_backpressureWait) { if (trace.get()) LOG.trace(String.format("Disabling backpresure at %s because client has waited for %d ms", this, (now - m_hasBackPressureTimestamp))); // assert(m_hasBackPressureTimestamp >= 0); m_hasBackPressure.set(false); m_hasBackPressureTimestamp = -1; return (false); } return (true); } return (false); } @Override public void stopping(Connection c) { super.stopping(c); synchronized (this) { //Prevent queueing of new work to this connection synchronized (Distributer.this) { m_connections.remove(this); //Notify listeners that a connection has been lost for (ClientStatusListener s : m_listeners) { s.connectionLost(m_hostname, m_connections.size()); } } m_isConnected = false; //Invoke callbacks for all queued invocations with a failure response final ClientResponse r = new ClientResponseImpl(-1, -1, -1, Hstoreservice.Status.ABORT_CONNECTION_LOST, new VoltTable[0], "Connection to database host (" + m_hostname + ") was lost before a response was received"); for (final CallbackValues cbv : m_callbacks.values()) { cbv.callback.clientCallback(r); } } } @Override public Runnable offBackPressure() { return new Runnable() { @Override public void run() { /* * Synchronization on Distributer.this is critical to ensure that queue * does not report backpressure AFTER the write stream reports that backpressure * has ended thus resulting in a lost wakeup. */ synchronized (Distributer.this) { for (final ClientStatusListener csl : m_listeners) { csl.backpressure(false); } } } }; } @Override public Runnable onBackPressure() { return null; } @Override public QueueMonitor writestreamMonitor() { return this; } /** * Get counters for invocations completed, aborted, errors. In that order. */ public synchronized long[] getCounters() { return new long[] { m_invocationsCompleted, m_invocationAborts, m_invocationErrors }; } /** * Get counters for invocations completed, aborted, errors. In that order. * Count returns count since this method was last invoked */ public synchronized long[] getCountersInterval() { final long invocationsCompletedThisTime = m_invocationsCompleted - m_lastInvocationsCompleted; m_lastInvocationsCompleted = m_invocationsCompleted; final long invocationsAbortsThisTime = m_invocationAborts - m_lastInvocationAborts; m_lastInvocationAborts = m_invocationAborts; final long invocationErrorsThisTime = m_invocationErrors - m_lastInvocationErrors; m_lastInvocationErrors = m_invocationErrors; final long invocationsThrottledThisTime = m_invocationsThrottled - m_lastInvocationsThrottled; m_lastInvocationsThrottled = m_invocationsThrottled; return new long[] { invocationsCompletedThisTime, invocationsAbortsThisTime, invocationErrorsThisTime, invocationsThrottledThisTime }; } private int m_queuedBytes = 0; private final int m_maxQueuedBytes = 2097152; @Override public boolean queue(int bytes) { m_queuedBytes += bytes; if (m_queuedBytes > m_maxQueuedBytes) { return true; } return false; } } void drain() throws NoConnectionsException { boolean more; do { more = false; synchronized (this) { for (NodeConnection cxn : m_connections) { synchronized (cxn.m_callbacks) { more = more || cxn.m_callbacks.size() > 0; } } } Thread.yield(); } while (more); synchronized (this) { for (NodeConnection cxn : m_connections) { assert (cxn.m_callbacks.size() == 0); } } } Distributer() { this(128, null, false, null); } Distributer(int expectedOutgoingMessageSize, int arenaSizes[], boolean useMultipleThreads, StatsUploaderSettings statsSettings) { this(expectedOutgoingMessageSize, arenaSizes, useMultipleThreads, statsSettings, 100); } Distributer(int expectedOutgoingMessageSize, int arenaSizes[], boolean useMultipleThreads, StatsUploaderSettings statsSettings, int backpressureWait) { if (statsSettings != null) { m_statsLoader = new ClientStatsLoader(statsSettings, this); } else { m_statsLoader = null; } m_useMultipleThreads = useMultipleThreads; m_backpressureWait = backpressureWait; m_network = new VoltNetwork(useMultipleThreads, true, 3); m_expectedOutgoingMessageSize = expectedOutgoingMessageSize; m_network.start(); m_pool = new DBBPool(false, arenaSizes, false); String hostname = ""; try { java.net.InetAddress localMachine = java.net.InetAddress.getLocalHost(); hostname = localMachine.getHostName(); } catch (java.net.UnknownHostException uhe) { } m_hostname = hostname; if (debug.get()) LOG.debug(String.format("Created new Distributer for %s [multiThread=%s, backpressureWait=%d]", m_hostname, m_useMultipleThreads, m_backpressureWait)); // new Thread() { // @Override // public void run() { // long lastBytesRead = 0; // long lastBytesWritten = 0; // long lastRuntime = System.currentTimeMillis(); // try { // while (true) { // Thread.sleep(10000); // final long now = System.currentTimeMillis(); // org.voltdb.utils.Pair<Long, Long> counters = m_network.getCounters(); // final long read = counters.getFirst(); // final long written = counters.getSecond(); // final long readDelta = read - lastBytesRead; // final long writeDelta = written - lastBytesWritten; // final long timeDelta = now - lastRuntime; // lastRuntime = now; // final double seconds = timeDelta / 1000.0; // final double megabytesRead = readDelta / (double)(1024 * 1024); // final double megabytesWritten = writeDelta / (double)(1024 * 1024); // final double readRate = megabytesRead / seconds; // final double writeRate = megabytesWritten / seconds; // lastBytesRead = read; // lastBytesWritten = written; // System.err.printf("Read rate %.2f Write rate %.2f\n", readRate, writeRate); // } // } catch (Exception e) { // e.printStackTrace(); // } // } // }.start(); } // void createConnection(String host, String program, String password) throws UnknownHostException, IOException { // LOG.info(String.format("Creating a new connection [host=%s, program=%s]", host, program)); // // // HACK: If they stick the port # at the end of the host name, we'll extract // // it out because we're generally nice people // int port = Client.VOLTDB_SERVER_PORT; // if (host.contains(":")) { // String split[] = host.split(":"); // host = split[0]; // port = Integer.valueOf(split[1]); // } // createConnection(host, program, password, port); // } public synchronized void createConnection(Integer site_id, String host, int port, String program, String password) throws UnknownHostException, IOException { if (debug.get()) { LOG.debug(String.format("Creating new connection [site=%s, host=%s, port=%d]", HStoreThreadManager.formatSiteName(site_id), host, port)); LOG.debug("Trying for an authenticated connection..."); } Object connectionStuff[] = null; try { connectionStuff = ConnectionUtil.getAuthenticatedConnection(host, program, password, port); } catch (Exception ex) { LOG.error("Failed to get connection to " + host + ":" + port, (debug.get() ? ex : null)); throw new IOException(ex); } if (debug.get()) LOG.debug("We now have an authenticated connection. Let's grab the socket..."); final SocketChannel aChannel = (SocketChannel) connectionStuff[0]; final long numbers[] = (long[]) connectionStuff[1]; if (m_clusterInstanceId == null) { long timestamp = numbers[2]; int addr = (int) numbers[3]; m_clusterInstanceId = new Object[] { timestamp, addr }; if (m_statsLoader != null) { if (debug.get()) LOG.debug("statsLoader = " + m_statsLoader); try { m_statsLoader.start(timestamp, addr); } catch (SQLException e) { throw new IOException(e); } } } else { // if (!(((Long)m_clusterInstanceId[0]).longValue() == numbers[2]) || // !(((Integer)m_clusterInstanceId[1]).longValue() == numbers[3])) { // aChannel.close(); // throw new IOException( // "Cluster instance id mismatch. Current is " + m_clusterInstanceId[0] + "," + m_clusterInstanceId[1] // + " and server's was " + numbers[2] + "," + numbers[3]); // } } m_buildString = (String) connectionStuff[2]; NodeConnection cxn = new NodeConnection(numbers); m_connections.add(cxn); if (site_id != null) { if (debug.get()) LOG.debug(String.format("Created connection for Site %s: %s", HStoreThreadManager.formatSiteName(site_id), cxn)); synchronized (m_connectionSiteXref) { Collection<NodeConnection> nc = m_connectionSiteXref.get(site_id); if (nc == null) { nc = new HashSet<NodeConnection>(); m_connectionSiteXref.put(site_id, nc); } nc.add(cxn); } // SYNCH } Connection c = m_network.registerChannel(aChannel, cxn); cxn.m_hostname = c.getHostname(); cxn.m_port = port; cxn.m_connection = c; if (debug.get()) LOG.debug("From what I can tell, we have a connection: " + cxn); } // private HashMap<String, Long> reportedSizes = new HashMap<String, Long>(); /** * Queue invocation on first node connection without backpressure. If there is none with without backpressure * then return false and don't queue the invocation * @param invocation * @param cb * @param expectedSerializedSize * @param ignoreBackPressure If true the invocation will be queued even if there is backpressure * @return True if the message was queued and false if the message was not queued due to backpressure * @throws NoConnectionsException */ boolean queue(StoredProcedureInvocation invocation, ProcedureCallback cb, int expectedSerializedSize, final boolean ignoreBackpressure) throws NoConnectionsException { return this.queue(invocation, cb, expectedSerializedSize, ignoreBackpressure, null); } boolean queue(StoredProcedureInvocation invocation, ProcedureCallback cb, int expectedSerializedSize, final boolean ignoreBackpressure, final Integer site_id) throws NoConnectionsException { NodeConnection cxn = null; boolean backpressure = true; int queuedInvocations = 0; long now = System.currentTimeMillis(); final int totalConnections = m_connections.size(); if (totalConnections == 0) { throw new NoConnectionsException("No connections."); } if (site_id != null && m_connectionSiteXref.containsKey(site_id)) { cxn = CollectionUtil.random(m_connectionSiteXref.get(site_id)); // cxn = CollectionUtil.first(m_connectionSiteXref.get(site_id)); if (cxn == null) { LOG.warn("No direct connection to " + HStoreThreadManager.formatSiteName(site_id)); } else backpressure = false; // XXX // else if (!cxn.hadBackPressure(now) || ignoreBackpressure) { // backpressure = false; // } // else { // cxn = null; // } } /* * Synchronization is necessary to ensure that m_connections is not modified * as well as to ensure that backpressure is reported correctly */ if (cxn == null) { synchronized (this) { for (int i = 0; i < totalConnections; ++i) { int idx = Math.abs(++m_nextConnection % totalConnections); cxn = m_connections.get(idx); if (trace.get()) LOG.trace("m_nextConnection = " + idx + " / " + totalConnections + " [" + cxn + "]"); queuedInvocations += cxn.m_callbacks.size(); if (cxn.hadBackPressure(now) == false || ignoreBackpressure) { // serialize and queue the invocation backpressure = false; break; } } // FOR } // SYNCH } if (backpressure) { cxn = null; for (ClientStatusListener s : m_listeners) { s.backpressure(true); } } if (debug.get()) LOG.debug(String.format("Queuing new %s Request [clientHandle=%d, siteId=%s]", invocation.getProcName(), invocation.getClientHandle(), site_id)); /* * Do the heavy weight serialization outside the synchronized block. * createWork synchronizes on an individual connection which allows for more concurrency */ if (cxn != null) { if (m_useMultipleThreads) { cxn.createWork(now, invocation.getClientHandle(), invocation.getProcName(), invocation, cb); } else { final FastSerializer fs = new FastSerializer(m_pool, expectedSerializedSize); BBContainer c = null; try { c = fs.writeObjectForMessaging(invocation); } catch (IOException e) { fs.getBBContainer().discard(); throw new RuntimeException(e); } cxn.createWork(now, invocation.getClientHandle(), invocation.getProcName(), c, cb); } // final String invocationName = invocation.getProcName(); // if (reportedSizes.containsKey(invocationName)) { // if (reportedSizes.get(invocationName) < c.b.remaining()) { // System.err.println("Queued invocation for " + invocationName + " is " + c.b.remaining() + " which is greater then last value of " + reportedSizes.get(invocationName)); // reportedSizes.put(invocationName, (long)c.b.remaining()); // } // } else { // reportedSizes.put(invocationName, (long)c.b.remaining()); // System.err.println("Queued invocation for " + invocationName + " is " + c.b.remaining()); // } } return !backpressure; } /** * Shutdown the VoltNetwork allowing the Ports to close and free resources * like memory pools * @throws InterruptedException */ final void shutdown() throws InterruptedException { if (m_statsLoader != null) { m_statsLoader.stop(); } m_network.shutdown(); synchronized (this) { m_pool.clear(); } } synchronized void addClientStatusListener(ClientStatusListener listener) { if (!m_listeners.contains(listener)) { m_listeners.add(listener); } } synchronized boolean removeClientStatusListener(ClientStatusListener listener) { return m_listeners.remove(listener); } private final ColumnInfo connectionStatsColumns[] = new ColumnInfo[] { new ColumnInfo("TIMESTAMP", VoltType.BIGINT), new ColumnInfo("HOSTNAME", VoltType.STRING), new ColumnInfo("CONNECTION_ID", VoltType.BIGINT), new ColumnInfo("SERVER_HOST_ID", VoltType.BIGINT), new ColumnInfo("SERVER_HOSTNAME", VoltType.STRING), new ColumnInfo("SERVER_CONNECTION_ID", VoltType.BIGINT), new ColumnInfo("INVOCATIONS_COMPLETED", VoltType.BIGINT), new ColumnInfo("INVOCATIONS_ABORTED", VoltType.BIGINT), new ColumnInfo("INVOCATIONS_FAILED", VoltType.BIGINT), new ColumnInfo("INVOCATIONS_THROTTLED", VoltType.BIGINT), new ColumnInfo("BYTES_READ", VoltType.BIGINT), new ColumnInfo("MESSAGES_READ", VoltType.BIGINT), new ColumnInfo("BYTES_WRITTEN", VoltType.BIGINT), new ColumnInfo("MESSAGES_WRITTEN", VoltType.BIGINT) }; private final ColumnInfo procedureStatsColumns[] = new ColumnInfo[] { new ColumnInfo("TIMESTAMP", VoltType.BIGINT), new ColumnInfo("HOSTNAME", VoltType.STRING), new ColumnInfo("CONNECTION_ID", VoltType.BIGINT), new ColumnInfo("SERVER_HOST_ID", VoltType.BIGINT), new ColumnInfo("SERVER_HOSTNAME", VoltType.STRING), new ColumnInfo("SERVER_CONNECTION_ID", VoltType.BIGINT), new ColumnInfo("PROCEDURE_NAME", VoltType.STRING), new ColumnInfo("ROUNDTRIPTIME_AVG", VoltType.INTEGER), new ColumnInfo("ROUNDTRIPTIME_MIN", VoltType.INTEGER), new ColumnInfo("ROUNDTRIPTIME_MAX", VoltType.INTEGER), new ColumnInfo("CLUSTER_ROUNDTRIPTIME_AVG", VoltType.INTEGER), new ColumnInfo("CLUSTER_ROUNDTRIPTIME_MIN", VoltType.INTEGER), new ColumnInfo("CLUSTER_ROUNDTRIPTIME_MAX", VoltType.INTEGER), new ColumnInfo("INVOCATIONS_COMPLETED", VoltType.BIGINT), new ColumnInfo("INVOCATIONS_ABORTED", VoltType.BIGINT), new ColumnInfo("INVOCATIONS_FAILED", VoltType.BIGINT), new ColumnInfo("TIMES_RESTARTED", VoltType.BIGINT) }; VoltTable getProcedureStats(final boolean interval) { final Long now = System.currentTimeMillis(); final VoltTable retval = new VoltTable(procedureStatsColumns); long totalInvocations = 0; long totalAbortedInvocations = 0; long totalFailedInvocations = 0; long totalRoundTripTime = 0; int totalRoundTripMax = Integer.MIN_VALUE; int totalRoundTripMin = Integer.MAX_VALUE; long totalClusterRoundTripTime = 0; int totalClusterRoundTripMax = Integer.MIN_VALUE; int totalClusterRoundTripMin = Integer.MAX_VALUE; long totalRestarts = 0; synchronized (m_connections) { for (NodeConnection cxn : m_connections) { synchronized (cxn) { for (ProcedureStats stats : cxn.m_stats.values()) { long invocationsCompleted = stats.m_invocationsCompleted; long invocationAborts = stats.m_invocationAborts; long invocationErrors = stats.m_invocationErrors; long roundTripTime = stats.m_roundTripTime; int maxRoundTripTime = stats.m_maxRoundTripTime; int minRoundTripTime = stats.m_minRoundTripTime; long clusterRoundTripTime = stats.m_clusterRoundTripTime; int clusterMinRoundTripTime = stats.m_minClusterRoundTripTime; int clusterMaxRoundTripTime = stats.m_maxClusterRoundTripTime; long restartCounter = stats.m_restartCounter; if (interval) { invocationsCompleted = stats.m_invocationsCompleted - stats.m_lastInvocationsCompleted; if (invocationsCompleted == 0) { //No invocations since last interval continue; } stats.m_lastInvocationsCompleted = stats.m_invocationsCompleted; invocationAborts = stats.m_invocationAborts - stats.m_lastInvocationAborts; stats.m_lastInvocationAborts = stats.m_invocationAborts; invocationErrors = stats.m_invocationErrors - stats.m_lastInvocationErrors; stats.m_lastInvocationErrors = stats.m_invocationErrors; roundTripTime = stats.m_roundTripTime - stats.m_lastRoundTripTime; stats.m_lastRoundTripTime = stats.m_roundTripTime; maxRoundTripTime = stats.m_lastMaxRoundTripTime; minRoundTripTime = stats.m_lastMinRoundTripTime; stats.m_lastMaxRoundTripTime = Integer.MIN_VALUE; stats.m_lastMinRoundTripTime = Integer.MAX_VALUE; clusterRoundTripTime = stats.m_clusterRoundTripTime - stats.m_lastClusterRoundTripTime; stats.m_lastClusterRoundTripTime = stats.m_clusterRoundTripTime; clusterMaxRoundTripTime = stats.m_lastMaxClusterRoundTripTime; clusterMinRoundTripTime = stats.m_lastMinClusterRoundTripTime; stats.m_lastMaxClusterRoundTripTime = Integer.MIN_VALUE; stats.m_lastMinClusterRoundTripTime = Integer.MAX_VALUE; } totalInvocations += invocationsCompleted; totalAbortedInvocations += invocationAborts; totalFailedInvocations += invocationErrors; totalRoundTripTime += roundTripTime; totalRoundTripMax = Math.max(maxRoundTripTime, totalRoundTripMax); totalRoundTripMin = Math.min(minRoundTripTime, totalRoundTripMin); totalClusterRoundTripTime += clusterRoundTripTime; totalClusterRoundTripMax = Math.max(clusterMaxRoundTripTime, totalClusterRoundTripMax); totalClusterRoundTripMin = Math.min(clusterMinRoundTripTime, totalClusterRoundTripMin); totalRestarts += restartCounter; retval.addRow(now, m_hostname, cxn.connectionId(), cxn.m_hostId, cxn.m_hostname, cxn.m_connectionId, stats.m_name, (int) (roundTripTime / invocationsCompleted), minRoundTripTime, maxRoundTripTime, (int) (clusterRoundTripTime / invocationsCompleted), clusterMinRoundTripTime, clusterMaxRoundTripTime, invocationsCompleted, invocationAborts, invocationErrors, restartCounter); } } } } return retval; } VoltTable getConnectionStats(final boolean interval) { final Long now = System.currentTimeMillis(); final VoltTable retval = new VoltTable(connectionStatsColumns); final Map<Long, Pair<String, long[]>> networkStats = m_network.getIOStats(interval); long totalInvocations = 0; long totalAbortedInvocations = 0; long totalFailedInvocations = 0; long totalThrottledInvocations = 0; synchronized (m_connections) { for (NodeConnection cxn : m_connections) { synchronized (cxn) { long counters[]; if (interval) { counters = cxn.getCountersInterval(); } else { counters = cxn.getCounters(); } totalInvocations += counters[0]; totalAbortedInvocations += counters[1]; totalFailedInvocations += counters[2]; totalThrottledInvocations += counters[3]; final long networkCounters[] = networkStats.get(cxn.connectionId()).getSecond(); final String hostname = networkStats.get(cxn.connectionId()).getFirst(); long bytesRead = 0; long messagesRead = 0; long bytesWritten = 0; long messagesWritten = 0; if (networkCounters != null) { bytesRead = networkCounters[0]; messagesRead = networkCounters[1]; bytesWritten = networkCounters[2]; messagesWritten = networkCounters[3]; } retval.addRow(now, m_hostname, cxn.connectionId(), cxn.m_hostId, hostname, cxn.m_connectionId, counters[0], counters[1], counters[2], counters[3], bytesRead, messagesRead, bytesWritten, messagesWritten); } } } final long globalIOStats[] = networkStats.get(-1L).getSecond(); retval.addRow(now, m_hostname, -1, -1, "GLOBAL", -1, totalInvocations, totalAbortedInvocations, totalFailedInvocations, totalThrottledInvocations, globalIOStats[0], globalIOStats[1], globalIOStats[2], globalIOStats[3]); return retval; } public Object[] getInstanceId() { return m_clusterInstanceId; } public String getBuildString() { return m_buildString; } public int getConnectionCount() { return m_connections.size(); } }