/* This file is part of VoltDB.
* Copyright (C) 2008-2017 VoltDB Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with VoltDB. If not, see <http://www.gnu.org/licenses/>.
*/
package org.voltdb.jdbc;
import java.util.HashMap;
import org.voltcore.utils.ssl.SSLConfiguration;
/**
* Provides support for database connection pooling, allowing for optimal application performance.
* From benchmarking results, optimal TCP socket usage is attained when 50 threads share the same
* socket, sending execution requests through it. The pool incorporate the logic necessary to issue
* newly created connections or pre-existing connections in use to client threads, as well as proper
* management of those connections (releasing resources, etc.).
*
* @author Seb Coursol (originally in exampleutils)
* @since 2.0
*/
public class JDBC4ClientConnectionPool {
private static final HashMap<String, JDBC4ClientConnection> ClientConnections = new HashMap<String, JDBC4ClientConnection>();
/**
* No instantiation allowed.
*/
private JDBC4ClientConnectionPool() {
}
/**
* Gets a client connection to the given VoltDB server(s).
*
* @param servers
* the list of VoltDB servers to connect to.
* @param port
* the VoltDB native protocol port to connect to (usually 21212).
* @param user
* the user name to use when connecting to the server(s).
* @param password
* the password to use when connecting to the server(s).
* @param isHeavyWeight
* the flag indicating callback processes on this connection will be heavy (long
* running callbacks). By default the connection only allocates one background
* processing thread to process callbacks. If those callbacks run for a long time,
* the network stack can get clogged with pending responses that have yet to be
* processed, at which point the server will disconnect the application, thinking it
* died and is not reading responses as fast as it is pushing requests. When the flag
* is set to 'true', an additional 2 processing thread will deal with processing
* callbacks, thus mitigating the issue.
* @param maxOutstandingTxns
* the number of transactions the client application may push against a specific
* connection before getting blocked on back-pressure. By default the connection
* allows 3,000 open transactions before preventing the client from posting more
* work, thus preventing server fire-hosing. In some cases however, with very fast,
* small transactions, this limit can be raised.
* @param reconnectOnConnectionLoss
* Attempts to reconnect to a node with retry after connection loss
* @return the client connection object the caller should use to post requests.
*/
public static JDBC4ClientConnection get(String[] servers, String user,
String password, boolean isHeavyWeight, int maxOutstandingTxns, boolean reconnectOnConnectionLoss) throws Exception {
return get(servers, user, password, isHeavyWeight, maxOutstandingTxns, reconnectOnConnectionLoss, null);
}
/**
* Gets a client connection to the given VoltDB server(s).
*
* @param servers
* the list of VoltDB servers to connect to.
* @param port
* the VoltDB native protocol port to connect to (usually 21212).
* @param user
* the user name to use when connecting to the server(s).
* @param password
* the password to use when connecting to the server(s).
* @param isHeavyWeight
* the flag indicating callback processes on this connection will be heavy (long
* running callbacks). By default the connection only allocates one background
* processing thread to process callbacks. If those callbacks run for a long time,
* the network stack can get clogged with pending responses that have yet to be
* processed, at which point the server will disconnect the application, thinking it
* died and is not reading responses as fast as it is pushing requests. When the flag
* is set to 'true', an additional 2 processing thread will deal with processing
* callbacks, thus mitigating the issue.
* @param maxOutstandingTxns
* the number of transactions the client application may push against a specific
* connection before getting blocked on back-pressure. By default the connection
* allows 3,000 open transactions before preventing the client from posting more
* work, thus preventing server fire-hosing. In some cases however, with very fast,
* small transactions, this limit can be raised.
* @param reconnectOnConnectionLoss
* Attempts to reconnect to a node with retry after connection loss
* @param sslConfig
* Contains properties - trust store path and password, key store path and password,
* used for connecting with server over SSL. For unencrypted connection, passed in ssl
* config is null
* @return the client connection object the caller should use to post requests.
* @see #get(String servers, int port, String user, String password, boolean isHeavyWeight, int
* maxOutstandingTxns, reconnectOnConnectionLoss)
*/
public static JDBC4ClientConnection get(String[] servers, String user, String password, boolean isHeavyWeight,
int maxOutstandingTxns, boolean reconnectOnConnectionLoss, SSLConfiguration.SslConfig sslConfig) throws Exception {
String clientConnectionKeyBase = getClientConnectionKeyBase(servers, user, password,
isHeavyWeight, maxOutstandingTxns, reconnectOnConnectionLoss);
String clientConnectionKey = clientConnectionKeyBase;
synchronized (ClientConnections) {
if (!ClientConnections.containsKey(clientConnectionKey))
ClientConnections.put(clientConnectionKey, new JDBC4ClientConnection(
clientConnectionKeyBase, clientConnectionKey, servers, user,
password, isHeavyWeight, maxOutstandingTxns, reconnectOnConnectionLoss,
sslConfig));
return ClientConnections.get(clientConnectionKey).use();
}
}
/**
* Releases a connection. This method (or connection.close() must be called by the user thread
* once the connection is no longer needed to release it back into the pool where other threads
* can pick it up. Failure to do so will cause a memory leak as more and more new connections
* will be created, never to be released and reused. The pool itself will run the logic to
* decide whether the actual underlying connection should be kept alive (if other threads are
* using it), or closed for good (if the calling thread was the last user of that connection).
*
* @param connection
* the connection to release back into the pool.
*/
public static void dispose(JDBC4ClientConnection connection) {
synchronized (ClientConnections) {
connection.dispose();
if (connection.users == 0)
ClientConnections.remove(connection.key);
}
}
/**
* Generates a hash/key for a connection based on the given list of connection parameters
*
* @param servers
* the list of VoltDB servers to connect to in comma separated hostname[:port] format.
* @param user
* the user name to use when connecting to the server(s).
* @param password
* the password to use when connecting to the server(s).
* @param isHeavyWeight
* the flag indicating callback processes on this connection will be heavy (long
* running callbacks).
* @param maxOutstandingTxns
* the number of transactions the client application may push against a specific
* connection before getting blocked on back-pressure.
* @param reconnectOnConnectionLoss
* Attempts to reconnect to a node with retry after connection loss
* @return the base hash/key for the given connection parameter
*/
private static String getClientConnectionKeyBase(String[] servers, String user,
String password, boolean isHeavyWeight, int maxOutstandingTxns, boolean reconnectOnConnectionLoss) {
String clientConnectionKeyBase = user + ":" + password + "@";
for (int i = 0; i < servers.length; i++)
clientConnectionKeyBase += servers[i].trim() + ",";
clientConnectionKeyBase += "{"
+ Boolean.toString(isHeavyWeight) + ":" + Integer.toString(maxOutstandingTxns)
+ ":" + Boolean.toString(reconnectOnConnectionLoss) + "}";
return clientConnectionKeyBase;
}
}