!103 【feature】 jdbc实现最小连接模式和集群状态变化时触发快速负载均衡
Merge pull request !103 from 周聪/dev_loadbalance_20230223_pr
This commit is contained in:
@ -11,6 +11,8 @@ import org.postgresql.jdbc.PgConnection;
|
|||||||
import org.postgresql.log.Logger;
|
import org.postgresql.log.Logger;
|
||||||
import org.postgresql.log.Log;
|
import org.postgresql.log.Log;
|
||||||
import org.postgresql.log.Tracer;
|
import org.postgresql.log.Tracer;
|
||||||
|
import org.postgresql.quickautobalance.ConnectionManager;
|
||||||
|
import org.postgresql.quickautobalance.LoadBalanceHeartBeating;
|
||||||
import org.postgresql.util.DriverInfo;
|
import org.postgresql.util.DriverInfo;
|
||||||
import org.postgresql.util.GT;
|
import org.postgresql.util.GT;
|
||||||
import org.postgresql.util.HostSpec;
|
import org.postgresql.util.HostSpec;
|
||||||
@ -558,8 +560,12 @@ public class Driver implements java.sql.Driver {
|
|||||||
* @throws SQLException if the connection could not be made
|
* @throws SQLException if the connection could not be made
|
||||||
*/
|
*/
|
||||||
private static Connection makeConnection(String url, Properties props) throws SQLException {
|
private static Connection makeConnection(String url, Properties props) throws SQLException {
|
||||||
|
ConnectionManager.getInstance().setCluster(props);
|
||||||
PgConnection pgConnection = new PgConnection(hostSpecs(props), user(props), database(props), props, url);
|
PgConnection pgConnection = new PgConnection(hostSpecs(props), user(props), database(props), props, url);
|
||||||
GlobalConnectionTracker.possessConnectionReference(pgConnection.getQueryExecutor(), props);
|
GlobalConnectionTracker.possessConnectionReference(pgConnection.getQueryExecutor(), props);
|
||||||
|
if (ConnectionManager.getInstance().setConnection(pgConnection, props)) {
|
||||||
|
LoadBalanceHeartBeating.startScheduledExecutorService(props);
|
||||||
|
}
|
||||||
return pgConnection;
|
return pgConnection;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -473,6 +473,42 @@ public enum PGProperty {
|
|||||||
+ "from that database. "
|
+ "from that database. "
|
||||||
+ "(backend >= 9.4)"),
|
+ "(backend >= 9.4)"),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enable quick auto balancing.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
ENABLE_QUICK_AUTO_BALANCE("enableQuickAutoBalance", "false",
|
||||||
|
"If the connection enable quickAutoBalance, this parameter only takes effect when autoBalance=leastconn."
|
||||||
|
+ "value: true or false.",
|
||||||
|
false, "true", "false"),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Idle time threshold of connections when quick auto balancing filters connections.
|
||||||
|
*/
|
||||||
|
MAX_IDLE_TIME_BEFORE_TERMINAL("maxIdleTimeBeforeTerminal", "30",
|
||||||
|
"During quick load balancing, if the connection is idle for more than maxIdleTimeBeforeTerminal seconds, "
|
||||||
|
+ "the connection may be closed to achieve load balancing for each data node."
|
||||||
|
+ "Value range: long && [0, Long.MAX_VALUE / 1000]."
|
||||||
|
+ "This parameter only takes effect when autoBalance=leastconn and enableQuickAutoBalance=true"),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Percentage of min reserved connections pre cluster when executing quick auto balancing.
|
||||||
|
*/
|
||||||
|
MIN_RESERVED_CON_PER_CLUSTER("minReservedConPerCluster", null,
|
||||||
|
"Percentage of min reserved connections pre cluster when executing quick auto balancing, "
|
||||||
|
+ "jdbc will retain minReservedConPerCluster percent of the connections per cluster that meet the closing conditions during quick auto balancing."
|
||||||
|
+ "Value range: int && [0, 100]."
|
||||||
|
+ "This parameter only takes effect when autoBalance=leastconn and enableQuickAutoBalance=true"),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Percentage of min reserved connections pre data node when executing quick auto balancing.
|
||||||
|
*/
|
||||||
|
MIN_RESERVED_CON_PER_DATANODE("minReservedConPerDatanode", null,
|
||||||
|
"Percentage of min reserved connections pre data node when executing quick auto balancing, "
|
||||||
|
+ "jdbc will retain minReservedConPerCluster percent of the connections pre data node that meet the closing conditions during quick auto balancing."
|
||||||
|
+ "Value range: int && [0, 100]."
|
||||||
|
+ "This parameter only takes effect when autoBalance=leastconn and enableQuickAutoBalance=true"),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Supported TLS cipher suites
|
* Supported TLS cipher suites
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -20,6 +20,7 @@ import org.postgresql.core.Version;
|
|||||||
import org.postgresql.hostchooser.*;
|
import org.postgresql.hostchooser.*;
|
||||||
import org.postgresql.jdbc.SslMode;
|
import org.postgresql.jdbc.SslMode;
|
||||||
import org.postgresql.QueryCNListUtils;
|
import org.postgresql.QueryCNListUtils;
|
||||||
|
import org.postgresql.quickautobalance.ConnectionManager;
|
||||||
import org.postgresql.util.*;
|
import org.postgresql.util.*;
|
||||||
import org.postgresql.log.Logger;
|
import org.postgresql.log.Logger;
|
||||||
import org.postgresql.log.Log;
|
import org.postgresql.log.Log;
|
||||||
@ -214,6 +215,7 @@ public class ConnectionFactoryImpl extends ConnectionFactory {
|
|||||||
HostChooserFactory.createHostChooser(currentHostSpecs, targetServerType, info);
|
HostChooserFactory.createHostChooser(currentHostSpecs, targetServerType, info);
|
||||||
Iterator<CandidateHost> hostIter = hostChooser.iterator();
|
Iterator<CandidateHost> hostIter = hostChooser.iterator();
|
||||||
boolean isMasterCluster = false;
|
boolean isMasterCluster = false;
|
||||||
|
boolean isFirstIter = true;
|
||||||
while (hostIter.hasNext()) {
|
while (hostIter.hasNext()) {
|
||||||
CandidateHost candidateHost = hostIter.next();
|
CandidateHost candidateHost = hostIter.next();
|
||||||
HostSpec hostSpec = candidateHost.hostSpec;
|
HostSpec hostSpec = candidateHost.hostSpec;
|
||||||
@ -223,10 +225,16 @@ public class ConnectionFactoryImpl extends ConnectionFactory {
|
|||||||
// In that case, the system tries to connect to each host in order, thus it should not look into
|
// In that case, the system tries to connect to each host in order, thus it should not look into
|
||||||
// GlobalHostStatusTracker
|
// GlobalHostStatusTracker
|
||||||
HostStatus knownStatus = knownStates.get(hostSpec);
|
HostStatus knownStatus = knownStates.get(hostSpec);
|
||||||
|
if (isFirstIter) {
|
||||||
|
isFirstIter = false;
|
||||||
|
} else {
|
||||||
|
ConnectionManager.getInstance().incrementCachedCreatingConnectionSize(hostSpec, info);
|
||||||
|
}
|
||||||
if (knownStatus != null && !candidateHost.targetServerType.allowConnectingTo(knownStatus)) {
|
if (knownStatus != null && !candidateHost.targetServerType.allowConnectingTo(knownStatus)) {
|
||||||
if (LOGGER.isDebugEnabled()) {
|
if (LOGGER.isDebugEnabled()) {
|
||||||
LOGGER.debug("Known status of host " + hostSpec + " is " + knownStatus + ", and required status was " + candidateHost.targetServerType + ". Will try next host");
|
LOGGER.debug("Known status of host " + hostSpec + " is " + knownStatus + ", and required status was " + candidateHost.targetServerType + ". Will try next host");
|
||||||
}
|
}
|
||||||
|
ConnectionManager.getInstance().decrementCachedCreatingConnectionSize(hostSpec, info);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -308,6 +316,7 @@ public class ConnectionFactoryImpl extends ConnectionFactory {
|
|||||||
GlobalClusterStatusTracker.reportMasterCluster(info, clusterSpec);
|
GlobalClusterStatusTracker.reportMasterCluster(info, clusterSpec);
|
||||||
} else {
|
} else {
|
||||||
queryExecutor.close();
|
queryExecutor.close();
|
||||||
|
ConnectionManager.getInstance().decrementCachedCreatingConnectionSize(hostSpec, info);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -322,6 +331,7 @@ public class ConnectionFactoryImpl extends ConnectionFactory {
|
|||||||
knownStates.put(hostSpec, hostStatus);
|
knownStates.put(hostSpec, hostStatus);
|
||||||
if (!candidateHost.targetServerType.allowConnectingTo(hostStatus)) {
|
if (!candidateHost.targetServerType.allowConnectingTo(hostStatus)) {
|
||||||
queryExecutor.close();
|
queryExecutor.close();
|
||||||
|
ConnectionManager.getInstance().decrementCachedCreatingConnectionSize(hostSpec, info);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -349,12 +359,14 @@ public class ConnectionFactoryImpl extends ConnectionFactory {
|
|||||||
if (hostIter.hasNext() || clusterIter.hasNext()) {
|
if (hostIter.hasNext() || clusterIter.hasNext()) {
|
||||||
LOGGER.info("ConnectException occured while connecting to {0}" + hostSpec, cex);
|
LOGGER.info("ConnectException occured while connecting to {0}" + hostSpec, cex);
|
||||||
exception.addSuppressed(cex);
|
exception.addSuppressed(cex);
|
||||||
|
ConnectionManager.getInstance().decrementCachedCreatingConnectionSize(hostSpec, info);
|
||||||
// still more addresses to try
|
// still more addresses to try
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (exception.getSuppressed().length > 0) {
|
if (exception.getSuppressed().length > 0) {
|
||||||
cex.addSuppressed(exception);
|
cex.addSuppressed(exception);
|
||||||
}
|
}
|
||||||
|
ConnectionManager.getInstance().decrementCachedCreatingConnectionSize(hostSpec, info);
|
||||||
throw new PSQLException(GT.tr(
|
throw new PSQLException(GT.tr(
|
||||||
"Connection to {0} refused. Check that the hostname and port are correct and that the postmaster is accepting TCP/IP connections.",
|
"Connection to {0} refused. Check that the hostname and port are correct and that the postmaster is accepting TCP/IP connections.",
|
||||||
hostSpec), PSQLState.CONNECTION_UNABLE_TO_CONNECT, cex);
|
hostSpec), PSQLState.CONNECTION_UNABLE_TO_CONNECT, cex);
|
||||||
@ -365,12 +377,14 @@ public class ConnectionFactoryImpl extends ConnectionFactory {
|
|||||||
if (hostIter.hasNext() || clusterIter.hasNext()) {
|
if (hostIter.hasNext() || clusterIter.hasNext()) {
|
||||||
LOGGER.info("IOException occured while connecting to " + hostSpec, ioe);
|
LOGGER.info("IOException occured while connecting to " + hostSpec, ioe);
|
||||||
exception.addSuppressed(ioe);
|
exception.addSuppressed(ioe);
|
||||||
|
ConnectionManager.getInstance().decrementCachedCreatingConnectionSize(hostSpec, info);
|
||||||
// still more addresses to try
|
// still more addresses to try
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (exception.getSuppressed().length > 0) {
|
if (exception.getSuppressed().length > 0) {
|
||||||
ioe.addSuppressed(exception);
|
ioe.addSuppressed(exception);
|
||||||
}
|
}
|
||||||
|
ConnectionManager.getInstance().decrementCachedCreatingConnectionSize(hostSpec, info);
|
||||||
throw new PSQLException(GT.tr("The connection attempt failed."),
|
throw new PSQLException(GT.tr("The connection attempt failed."),
|
||||||
PSQLState.CONNECTION_UNABLE_TO_CONNECT, ioe);
|
PSQLState.CONNECTION_UNABLE_TO_CONNECT, ioe);
|
||||||
} catch (SQLException se) {
|
} catch (SQLException se) {
|
||||||
@ -381,11 +395,13 @@ public class ConnectionFactoryImpl extends ConnectionFactory {
|
|||||||
LOGGER.info("SQLException occured while connecting to " + hostSpec, se);
|
LOGGER.info("SQLException occured while connecting to " + hostSpec, se);
|
||||||
exception.addSuppressed(se);
|
exception.addSuppressed(se);
|
||||||
// still more addresses to try
|
// still more addresses to try
|
||||||
|
ConnectionManager.getInstance().decrementCachedCreatingConnectionSize(hostSpec, info);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (exception.getSuppressed().length > 0) {
|
if (exception.getSuppressed().length > 0) {
|
||||||
se.addSuppressed(exception);
|
se.addSuppressed(exception);
|
||||||
}
|
}
|
||||||
|
ConnectionManager.getInstance().decrementCachedCreatingConnectionSize(hostSpec, info);
|
||||||
throw se;
|
throw se;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -12,6 +12,8 @@ import org.postgresql.PGProperty;
|
|||||||
import org.postgresql.log.Log;
|
import org.postgresql.log.Log;
|
||||||
import org.postgresql.log.Logger;
|
import org.postgresql.log.Logger;
|
||||||
import org.postgresql.QueryCNListUtils;
|
import org.postgresql.QueryCNListUtils;
|
||||||
|
import org.postgresql.quickautobalance.ConnectionManager;
|
||||||
|
import org.postgresql.quickautobalance.Cluster;
|
||||||
import org.postgresql.util.HostSpec;
|
import org.postgresql.util.HostSpec;
|
||||||
import org.postgresql.util.PSQLException;
|
import org.postgresql.util.PSQLException;
|
||||||
|
|
||||||
@ -95,6 +97,7 @@ public class MultiHostChooser implements HostChooser {
|
|||||||
allHosts = priorityRoundRobin(allHosts);
|
allHosts = priorityRoundRobin(allHosts);
|
||||||
break;
|
break;
|
||||||
case LeastConn:
|
case LeastConn:
|
||||||
|
allHosts = leastConn(allHosts);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
isOutPutLog = false;
|
isOutPutLog = false;
|
||||||
@ -108,6 +111,7 @@ public class MultiHostChooser implements HostChooser {
|
|||||||
}
|
}
|
||||||
return allHosts;
|
return allHosts;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns a counter and increments it by one.
|
// Returns a counter and increments it by one.
|
||||||
// Because it is possible to use it in multiple instances, use synchronized (MultiHostChooser.class).
|
// Because it is possible to use it in multiple instances, use synchronized (MultiHostChooser.class).
|
||||||
private int getRRIndex() {
|
private int getRRIndex() {
|
||||||
@ -138,6 +142,17 @@ public class MultiHostChooser implements HostChooser {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private List<HostSpec> leastConn(List<HostSpec> hostSpecs) {
|
||||||
|
if (hostSpecs.size() <= 1) {
|
||||||
|
return hostSpecs;
|
||||||
|
}
|
||||||
|
Cluster cluster = ConnectionManager.getInstance().getCluster(URLIdentifier);
|
||||||
|
if (cluster == null) {
|
||||||
|
return hostSpecs;
|
||||||
|
}
|
||||||
|
return cluster.sortDnsByLeastConn(hostSpecs);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Use for RR algorithm. In case of first CN is not been connected, jdbc will
|
* Use for RR algorithm. In case of first CN is not been connected, jdbc will
|
||||||
* try to connect the second one. So shuffering all CN except of the first one
|
* try to connect the second one. So shuffering all CN except of the first one
|
||||||
|
|||||||
@ -7,6 +7,8 @@ package org.postgresql.jdbc;
|
|||||||
|
|
||||||
import org.postgresql.Driver;
|
import org.postgresql.Driver;
|
||||||
import org.postgresql.core.*;
|
import org.postgresql.core.*;
|
||||||
|
import org.postgresql.quickautobalance.ConnectionManager;
|
||||||
|
import org.postgresql.quickautobalance.LoadBalanceHeartBeating;
|
||||||
import org.postgresql.util.GT;
|
import org.postgresql.util.GT;
|
||||||
import org.postgresql.util.PGbytea;
|
import org.postgresql.util.PGbytea;
|
||||||
import org.postgresql.util.PSQLException;
|
import org.postgresql.util.PSQLException;
|
||||||
@ -22,7 +24,6 @@ import java.sql.Statement;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.TimerTask;
|
import java.util.TimerTask;
|
||||||
import org.postgresql.core.v3.ConnectionFactoryImpl;
|
|
||||||
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
|
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
|
||||||
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
|
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
|
||||||
|
|
||||||
@ -1115,12 +1116,14 @@ public class PgStatement implements Statement, BaseStatement {
|
|||||||
// Not in query, there's nothing to cancel
|
// Not in query, there's nothing to cancel
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
setConnectionState(StatementCancelState.CANCELING);
|
||||||
// Synchronize on connection to avoid spinning in killTimerTask
|
// Synchronize on connection to avoid spinning in killTimerTask
|
||||||
synchronized (connection) {
|
synchronized (connection) {
|
||||||
try {
|
try {
|
||||||
connection.cancelQuery();
|
connection.cancelQuery();
|
||||||
} finally {
|
} finally {
|
||||||
STATE_UPDATER.set(this, StatementCancelState.CANCELLED);
|
STATE_UPDATER.set(this, StatementCancelState.CANCELLED);
|
||||||
|
setConnectionState(StatementCancelState.CANCELLED);
|
||||||
connection.notifyAll(); // wake-up killTimerTask
|
connection.notifyAll(); // wake-up killTimerTask
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1164,14 +1167,14 @@ public class PgStatement implements Statement, BaseStatement {
|
|||||||
fetchSize = rows;
|
fetchSize = rows;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void startTimer() {
|
private void startTimer() throws SQLException {
|
||||||
/*
|
/*
|
||||||
* there shouldn't be any previous timer active, but better safe than sorry.
|
* there shouldn't be any previous timer active, but better safe than sorry.
|
||||||
*/
|
*/
|
||||||
cleanupTimer();
|
cleanupTimer();
|
||||||
|
|
||||||
STATE_UPDATER.set(this, StatementCancelState.IN_QUERY);
|
STATE_UPDATER.set(this, StatementCancelState.IN_QUERY);
|
||||||
|
setConnectionState(StatementCancelState.IN_QUERY);
|
||||||
if (timeout == 0) {
|
if (timeout == 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -1216,13 +1219,20 @@ public class PgStatement implements Statement, BaseStatement {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void killTimerTask() {
|
private void setConnectionState(StatementCancelState state) throws SQLException {
|
||||||
|
if (LoadBalanceHeartBeating.isLoadBalanceHeartBeatingStarted() && this.connection instanceof PgConnection) {
|
||||||
|
ConnectionManager.getInstance().setConnectionState(this.connection.unwrap(PgConnection.class), state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void killTimerTask() throws SQLException {
|
||||||
boolean timerTaskIsClear = cleanupTimer();
|
boolean timerTaskIsClear = cleanupTimer();
|
||||||
// The order is important here: in case we need to wait for the cancel task, the state must be
|
// The order is important here: in case we need to wait for the cancel task, the state must be
|
||||||
// kept StatementCancelState.IN_QUERY, so cancelTask would be able to cancel the query.
|
// kept StatementCancelState.IN_QUERY, so cancelTask would be able to cancel the query.
|
||||||
// It is believed that this case is very rare, so "additional cancel and wait below" would not
|
// It is believed that this case is very rare, so "additional cancel and wait below" would not
|
||||||
// harm it.
|
// harm it.
|
||||||
if (timerTaskIsClear && STATE_UPDATER.compareAndSet(this, StatementCancelState.IN_QUERY, StatementCancelState.IDLE)) {
|
if (timerTaskIsClear && STATE_UPDATER.compareAndSet(this, StatementCancelState.IN_QUERY, StatementCancelState.IDLE)) {
|
||||||
|
setConnectionState(StatementCancelState.IDLE);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1243,6 +1253,7 @@ public class PgStatement implements Statement, BaseStatement {
|
|||||||
interrupted = true;
|
interrupted = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
setConnectionState(StatementCancelState.IDLE);
|
||||||
}
|
}
|
||||||
if (interrupted) {
|
if (interrupted) {
|
||||||
Thread.currentThread().interrupt();
|
Thread.currentThread().interrupt();
|
||||||
|
|||||||
@ -8,7 +8,7 @@ package org.postgresql.jdbc;
|
|||||||
/**
|
/**
|
||||||
* Represents {@link PgStatement#cancel()} state.
|
* Represents {@link PgStatement#cancel()} state.
|
||||||
*/
|
*/
|
||||||
enum StatementCancelState {
|
public enum StatementCancelState {
|
||||||
IDLE,
|
IDLE,
|
||||||
IN_QUERY,
|
IN_QUERY,
|
||||||
CANCELING,
|
CANCELING,
|
||||||
|
|||||||
@ -0,0 +1,626 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) Huawei Technologies Co., Ltd. 2023. All rights reserved.
|
||||||
|
*
|
||||||
|
* openGauss is licensed under Mulan PSL v2.
|
||||||
|
* You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||||
|
* You may obtain a copy of Mulan PSL v2 at:
|
||||||
|
*
|
||||||
|
* http://license.coscl.org.cn/MulanPSL2
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||||
|
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||||
|
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||||
|
* See the Mulan PSL v2 for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.postgresql.quickautobalance;
|
||||||
|
|
||||||
|
import org.postgresql.Driver;
|
||||||
|
import org.postgresql.PGProperty;
|
||||||
|
import org.postgresql.jdbc.PgConnection;
|
||||||
|
import org.postgresql.jdbc.StatementCancelState;
|
||||||
|
import org.postgresql.log.Log;
|
||||||
|
import org.postgresql.log.Logger;
|
||||||
|
import org.postgresql.quickautobalance.DataNode.CheckDnStateResult;
|
||||||
|
import org.postgresql.util.GT;
|
||||||
|
import org.postgresql.util.HostSpec;
|
||||||
|
import org.postgresql.util.PSQLException;
|
||||||
|
import org.postgresql.util.PSQLState;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Properties;
|
||||||
|
import java.util.Queue;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.Vector;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A Cluster, which cached the information of the dataNodes in this cluster.
|
||||||
|
*/
|
||||||
|
public class Cluster {
|
||||||
|
private static Log LOGGER = Logger.getLogger(Cluster.class.getName());
|
||||||
|
|
||||||
|
private static final double CLOSE_CONNECTION_PERCENTAGE_EACH_TIME = 0.2d;
|
||||||
|
|
||||||
|
private final String urlIdentifier;
|
||||||
|
|
||||||
|
private final Set<HostSpec> dns;
|
||||||
|
|
||||||
|
private final Queue<ConnectionInfo> abandonedConnectionList;
|
||||||
|
|
||||||
|
private final Map<HostSpec, DataNode> cachedDnList;
|
||||||
|
|
||||||
|
private final List<Properties> cachedPropertiesList;
|
||||||
|
|
||||||
|
// Percentage of minimum reserved connections that can be closed in a cluster, value range: [0,100].
|
||||||
|
private volatile int minReservedConPerCluster;
|
||||||
|
|
||||||
|
private volatile boolean enableMinReservedConPerCluster;
|
||||||
|
|
||||||
|
// Percentage of minimum reserved connections that can be closed in a datanode, value range: [0,100].
|
||||||
|
private volatile int minReservedConPerDatanode;
|
||||||
|
|
||||||
|
private volatile boolean enableMinReservedConPerDatanode;
|
||||||
|
|
||||||
|
private volatile long quickAutoBalanceStartTime;
|
||||||
|
|
||||||
|
private int totalAbandonedConnectionSize = 0;
|
||||||
|
|
||||||
|
public Cluster(final String urlIdentifier, final Properties properties) throws PSQLException {
|
||||||
|
this.urlIdentifier = urlIdentifier;
|
||||||
|
HostSpec[] hostSpecs = Driver.getURLHostSpecs(properties);
|
||||||
|
this.dns = new HashSet<>();
|
||||||
|
this.dns.addAll(Arrays.asList(hostSpecs));
|
||||||
|
this.cachedDnList = new ConcurrentHashMap<>();
|
||||||
|
for (HostSpec hostSpec : hostSpecs) {
|
||||||
|
DataNode dataNode = new DataNode(hostSpec);
|
||||||
|
this.cachedDnList.put(hostSpec, dataNode);
|
||||||
|
}
|
||||||
|
updateParams(properties);
|
||||||
|
this.abandonedConnectionList = new ConcurrentLinkedQueue<>();
|
||||||
|
this.cachedPropertiesList = new Vector<>();
|
||||||
|
this.cachedPropertiesList.add(properties);
|
||||||
|
this.quickAutoBalanceStartTime = 0;
|
||||||
|
this.totalAbandonedConnectionSize = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* set connection state of cached connections if it exists.
|
||||||
|
*
|
||||||
|
* @param pgConnection pgConnection
|
||||||
|
* @param state new state
|
||||||
|
*/
|
||||||
|
public void setConnectionState(final PgConnection pgConnection, final StatementCancelState state) {
|
||||||
|
String socketAddress = pgConnection.getSocketAddress();
|
||||||
|
HostSpec hostSpec = calculateHostSpec(socketAddress);
|
||||||
|
if (hostSpec != null && !dns.contains(hostSpec)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
DataNode dataNode = cachedDnList.get(hostSpec);
|
||||||
|
if (dataNode != null) {
|
||||||
|
dataNode.setConnectionState(pgConnection, state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// calculate the hostSpec of destination
|
||||||
|
private HostSpec calculateHostSpec(String socketAddress) {
|
||||||
|
String urlClient = socketAddress.split("/")[1];
|
||||||
|
String[] urlClientSplit = urlClient.split(":");
|
||||||
|
if (urlClientSplit.length == 2) {
|
||||||
|
String host = urlClientSplit[0];
|
||||||
|
int port = Integer.parseInt(urlClientSplit[1]);
|
||||||
|
return new HostSpec(host, port);
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a new connection.
|
||||||
|
*
|
||||||
|
* @param pgConnection pgConnection
|
||||||
|
* @param properties properties
|
||||||
|
*/
|
||||||
|
public void setConnection(final PgConnection pgConnection, final Properties properties)
|
||||||
|
throws PSQLException {
|
||||||
|
if (pgConnection == null || properties == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String socketAddress = pgConnection.getSocketAddress();
|
||||||
|
HostSpec hostSpec = calculateHostSpec(socketAddress);
|
||||||
|
if (hostSpec != null && !dns.contains(hostSpec)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setProperties(properties);
|
||||||
|
synchronized (cachedDnList) {
|
||||||
|
cachedDnList.get(hostSpec).setConnection(pgConnection, properties, hostSpec);
|
||||||
|
decrementCachedCreatingConnectionSize(hostSpec);
|
||||||
|
updateParams(properties);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set new properties if the user doesn't exist in cachedPropertiesList,
|
||||||
|
* or update properties if exists.
|
||||||
|
*
|
||||||
|
* @param properties properties
|
||||||
|
*/
|
||||||
|
public void setProperties(Properties properties) {
|
||||||
|
synchronized (cachedPropertiesList) {
|
||||||
|
for (int i = 0; i < cachedPropertiesList.size(); i++) {
|
||||||
|
Properties prop = cachedPropertiesList.get(i);
|
||||||
|
if (prop.getProperty("user", "").equals(properties.getProperty("user", null))) {
|
||||||
|
cachedPropertiesList.set(i, properties);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cachedPropertiesList.add(properties);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CacheCreatingConnectionNum - 1;
|
||||||
|
*
|
||||||
|
* @param hostSpec hostSpec
|
||||||
|
* @return cachedCreatingConnectionNum
|
||||||
|
*/
|
||||||
|
public int decrementCachedCreatingConnectionSize(final HostSpec hostSpec) {
|
||||||
|
if (!cachedDnList.containsKey(hostSpec)) {
|
||||||
|
LOGGER.error(GT.tr("Can not find hostSpec: {0} in Cluster: {1}.", hostSpec.toString(), urlIdentifier));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
DataNode dataNode = cachedDnList.get(hostSpec);
|
||||||
|
if (dataNode != null) {
|
||||||
|
return dataNode.decrementCachedCreatingConnectionSize();
|
||||||
|
} else {
|
||||||
|
LOGGER.error(GT.tr("Can not find hostSpec: {0} in Cluster: {1}.", hostSpec.toString(), urlIdentifier));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateMinReservedConPerCluster(Properties properties) throws PSQLException {
|
||||||
|
int perCluster = 0;
|
||||||
|
String param = PGProperty.MIN_RESERVED_CON_PER_CLUSTER.get(properties);
|
||||||
|
if (param == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
perCluster = Integer.parseInt(param);
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
throw new PSQLException(GT.tr("Parameter minReservedConPerCluster={0} parsed failed, value range: int && [0, 100]."
|
||||||
|
, PGProperty.MIN_RESERVED_CON_PER_CLUSTER.get(properties)), PSQLState.INVALID_PARAMETER_TYPE);
|
||||||
|
}
|
||||||
|
if (perCluster < 0 || perCluster > 100) {
|
||||||
|
throw new PSQLException(GT.tr("Parameter minReservedConPerCluster={0} parsed failed, value range: int && [0, 100]."
|
||||||
|
, perCluster), PSQLState.INVALID_PARAMETER_VALUE);
|
||||||
|
} else {
|
||||||
|
if (!this.enableMinReservedConPerCluster) {
|
||||||
|
this.enableMinReservedConPerCluster = true;
|
||||||
|
this.minReservedConPerCluster = perCluster;
|
||||||
|
} else {
|
||||||
|
this.minReservedConPerCluster = Math.min(this.minReservedConPerCluster, perCluster);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateMinReservedConPerDatanode(Properties properties) throws PSQLException {
|
||||||
|
int perDatanode = 0;
|
||||||
|
String param = PGProperty.MIN_RESERVED_CON_PER_DATANODE.get(properties);
|
||||||
|
if (param == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
perDatanode = Integer.parseInt(param);
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
throw new PSQLException(GT.tr("Parameter minReservedConPerDatanode={0} parsed failed, value range: int && [0, 100]."
|
||||||
|
, PGProperty.MIN_RESERVED_CON_PER_DATANODE.get(properties)), PSQLState.INVALID_PARAMETER_TYPE);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (perDatanode < 0 || perDatanode > 100) {
|
||||||
|
throw new PSQLException(GT.tr("Parameter minReservedConPerDatanode={0} parsed failed, value range: int && [0, 100].", perDatanode), PSQLState.INVALID_PARAMETER_VALUE);
|
||||||
|
} else {
|
||||||
|
if (!this.enableMinReservedConPerDatanode) {
|
||||||
|
this.enableMinReservedConPerDatanode = true;
|
||||||
|
this.minReservedConPerDatanode = perDatanode;
|
||||||
|
} else {
|
||||||
|
this.minReservedConPerDatanode = Math.min(this.minReservedConPerDatanode, perDatanode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateParams(Properties properties) throws PSQLException {
|
||||||
|
updateMinReservedConPerCluster(properties);
|
||||||
|
updateMinReservedConPerDatanode(properties);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get connection info of the connection if exists.
|
||||||
|
*
|
||||||
|
* @param connection connection
|
||||||
|
* @return connection info
|
||||||
|
*/
|
||||||
|
public ConnectionInfo getConnectionInfo(PgConnection connection) {
|
||||||
|
String socketAddress = connection.getSocketAddress();
|
||||||
|
HostSpec hostSpec = calculateHostSpec(socketAddress);
|
||||||
|
DataNode dataNode = cachedDnList.get(hostSpec);
|
||||||
|
return hostSpec != null && dataNode != null ? dataNode.getConnectionInfo(connection) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sort hostSpec list in ascending order by amount of connections.
|
||||||
|
* Put the hostSpec to the tail, if dataNodeState = false.
|
||||||
|
*
|
||||||
|
* @param hostSpecs host specs
|
||||||
|
* @return hostSpec list
|
||||||
|
*/
|
||||||
|
public List<HostSpec> sortDnsByLeastConn(List<HostSpec> hostSpecs) {
|
||||||
|
Map<HostSpec, DataNodeCompareInfo> dataNodeCompareInfoMap;
|
||||||
|
// Copy dataNodeCompareInfo from cachedDnList.
|
||||||
|
synchronized (cachedDnList) {
|
||||||
|
dataNodeCompareInfoMap = hostSpecs.stream()
|
||||||
|
.collect(Collectors.toMap(Function.identity(), hostSpec -> {
|
||||||
|
DataNode dataNode = cachedDnList.get(hostSpec);
|
||||||
|
int cachedConnectionListSize = dataNode.getCachedConnectionListSize();
|
||||||
|
int cachedCreatingConnectionSize = dataNode.getCachedCreatingConnectionSize();
|
||||||
|
boolean dataNodeState = dataNode.getDataNodeState();
|
||||||
|
return new DataNodeCompareInfo(cachedConnectionListSize, cachedCreatingConnectionSize, dataNodeState);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
hostSpecs.sort((o1, o2) -> {
|
||||||
|
boolean o1State = dataNodeCompareInfoMap.get(o1).getDataNodeState();
|
||||||
|
boolean o2State = dataNodeCompareInfoMap.get(o2).getDataNodeState();
|
||||||
|
if (!o1State && o2State) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (!o2State && o1State) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
int o1ConnectionSize = dataNodeCompareInfoMap.get(o1).getConnectionListSize() + dataNodeCompareInfoMap.get(o1).getCachedCreatedConnectionSize();
|
||||||
|
int o2ConnectionSize = dataNodeCompareInfoMap.get(o2).getConnectionListSize() + dataNodeCompareInfoMap.get(o2).getCachedCreatedConnectionSize();
|
||||||
|
return o1ConnectionSize - o2ConnectionSize;
|
||||||
|
});
|
||||||
|
if (hostSpecs.get(0) != null) {
|
||||||
|
this.cachedDnList.get(hostSpecs.get(0)).incrementCachedCreatingConnectionSize();
|
||||||
|
}
|
||||||
|
LOGGER.info(GT.tr("SortDnsByLeastConn: {0}."
|
||||||
|
, dataNodeCompareInfoMap));
|
||||||
|
return hostSpecs;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The necessary info when sorting data nodes.
|
||||||
|
*/
|
||||||
|
class DataNodeCompareInfo {
|
||||||
|
int connectionListSize;
|
||||||
|
int cachedCreatedConnectionSize;
|
||||||
|
boolean dataNodeState;
|
||||||
|
|
||||||
|
public DataNodeCompareInfo(final int connectionListSize, final int cachedCreatedConnectionSize, final boolean dataNodeState) {
|
||||||
|
this.connectionListSize = connectionListSize;
|
||||||
|
this.cachedCreatedConnectionSize = cachedCreatedConnectionSize;
|
||||||
|
this.dataNodeState = dataNodeState;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get connectionList size.
|
||||||
|
*
|
||||||
|
* @return size of connectionList
|
||||||
|
*/
|
||||||
|
public int getConnectionListSize() {
|
||||||
|
return connectionListSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get cached created connection size.
|
||||||
|
*
|
||||||
|
* @return cachedCreateConnectionSize
|
||||||
|
*/
|
||||||
|
public int getCachedCreatedConnectionSize() {
|
||||||
|
return cachedCreatedConnectionSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get data node state.
|
||||||
|
*
|
||||||
|
* @return dataNodeState
|
||||||
|
*/
|
||||||
|
public boolean getDataNodeState() {
|
||||||
|
return dataNodeState;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "{" +
|
||||||
|
"connectionListSize=" + connectionListSize +
|
||||||
|
", cachedCreatedConnectionSize=" + cachedCreatedConnectionSize +
|
||||||
|
", dataNodeState=" + dataNodeState +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check each data nodes' validity of cluster,
|
||||||
|
* use cached properties to tryConnect to each data nodes,
|
||||||
|
* if cached properties is invalid, remove cached properties,
|
||||||
|
* if state change to valid from invalid, quick load balancing start,
|
||||||
|
* if state change to invalid from valid, clear cached connections of this node.
|
||||||
|
*
|
||||||
|
* @return the number of invalid data nodes
|
||||||
|
*/
|
||||||
|
public int checkClusterState() {
|
||||||
|
// Count the state of all data nodes before and after checking cluster state.
|
||||||
|
Map<HostSpec, Boolean> oldStates = cachedDnList.entrySet().stream()
|
||||||
|
.collect(Collectors.toMap(Entry::getKey,
|
||||||
|
(val) -> val.getValue().getDataNodeState()));
|
||||||
|
Map<HostSpec, Boolean> newStates = cachedDnList.entrySet().stream()
|
||||||
|
.collect(Collectors.toMap(Entry::getKey,
|
||||||
|
(val) -> this.checkDnState(val.getKey())));
|
||||||
|
Map<DataNodeChangedState, List<HostSpec>> checkResult = new HashMap<>();
|
||||||
|
for (DataNodeChangedState dataNodeChangedState : DataNodeChangedState.values()) {
|
||||||
|
checkResult.put(dataNodeChangedState, new ArrayList<>());
|
||||||
|
}
|
||||||
|
for (Map.Entry<HostSpec, DataNode> entry : cachedDnList.entrySet()) {
|
||||||
|
HostSpec hostSpec = entry.getKey();
|
||||||
|
boolean oldState = oldStates.get(hostSpec);
|
||||||
|
boolean newState = newStates.get(hostSpec);
|
||||||
|
if (oldState && !newState) {
|
||||||
|
// If data node fails, clear its cacheConnectionList.
|
||||||
|
int removed = cachedDnList.get(hostSpec).clearCachedConnections();
|
||||||
|
checkResult.get(DataNodeChangedState.CHANGE_TO_INVALID).add(hostSpec);
|
||||||
|
LOGGER.info(GT.tr("A data node failed, clear cached connections, cluster: {0}, " +
|
||||||
|
"hostSpec: {1}, cached connections: {2}.",
|
||||||
|
urlIdentifier, hostSpec.toString(), removed));
|
||||||
|
} else if (!oldState && newState) {
|
||||||
|
checkResult.get(DataNodeChangedState.CHANGE_TO_VALID).add(hostSpec);
|
||||||
|
} else if (oldState) {
|
||||||
|
checkResult.get(DataNodeChangedState.KEEP_VALID).add(hostSpec);
|
||||||
|
} else {
|
||||||
|
checkResult.get(DataNodeChangedState.KEEP_INVALID).add(hostSpec);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LOGGER.info(GT.tr("Check cluster states in cluster: {0}, result: {1}.",
|
||||||
|
urlIdentifier, checkResult.toString()));
|
||||||
|
// Start to quickLoadBalance.
|
||||||
|
if (checkResult.get(DataNodeChangedState.CHANGE_TO_VALID).size() != 0
|
||||||
|
&& LoadBalanceHeartBeating.isQuickAutoBalanceStarted()) {
|
||||||
|
quickLoadBalance(checkResult.get(DataNodeChangedState.KEEP_VALID));
|
||||||
|
}
|
||||||
|
return checkResult.get(DataNodeChangedState.KEEP_INVALID).size() +
|
||||||
|
checkResult.get(DataNodeChangedState.CHANGE_TO_INVALID).size();
|
||||||
|
}
|
||||||
|
|
||||||
|
enum DataNodeChangedState {
|
||||||
|
KEEP_VALID, KEEP_INVALID, CHANGE_TO_VALID, CHANGE_TO_INVALID
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check dn state by cached properties, and remove invalid properties.
|
||||||
|
*
|
||||||
|
* @param hostSpec hostSpec
|
||||||
|
* @return state of the date node
|
||||||
|
*/
|
||||||
|
public boolean checkDnState(HostSpec hostSpec) {
|
||||||
|
synchronized (cachedPropertiesList) {
|
||||||
|
DataNode dataNode = cachedDnList.get(hostSpec);
|
||||||
|
if (dataNode == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (Iterator<Properties> iterator = cachedPropertiesList.iterator(); iterator.hasNext(); ) {
|
||||||
|
Properties properties = iterator.next();
|
||||||
|
CheckDnStateResult result = dataNode.checkDnStateAndProperties(properties);
|
||||||
|
if (CheckDnStateResult.DN_VALID.equals(result)) {
|
||||||
|
dataNode.setDataNodeState(true);
|
||||||
|
return true;
|
||||||
|
} else if (CheckDnStateResult.DN_INVALID.equals(result)) {
|
||||||
|
dataNode.setDataNodeState(false);
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
iterator.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dataNode.setDataNodeState(false);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int quickLoadBalance(List<HostSpec> validDns) {
|
||||||
|
synchronized (abandonedConnectionList) {
|
||||||
|
this.quickAutoBalanceStartTime = System.currentTimeMillis();
|
||||||
|
// the connections added into abandonedConnectionList
|
||||||
|
int removed = 0;
|
||||||
|
// cachedConnectionList size
|
||||||
|
int total = 0;
|
||||||
|
// idle connection filtered from cachedConnectionList
|
||||||
|
int idle = 0;
|
||||||
|
int minReservedConnectionPercentage;
|
||||||
|
if (!enableMinReservedConPerCluster) {
|
||||||
|
minReservedConnectionPercentage = minReservedConPerDatanode;
|
||||||
|
} else if (!enableMinReservedConPerDatanode) {
|
||||||
|
minReservedConnectionPercentage = minReservedConPerCluster;
|
||||||
|
} else {
|
||||||
|
minReservedConnectionPercentage = Math.max(minReservedConPerDatanode, minReservedConPerCluster);
|
||||||
|
}
|
||||||
|
for (Entry<HostSpec, DataNode> entry : cachedDnList.entrySet()) {
|
||||||
|
DataNode dataNode = entry.getValue();
|
||||||
|
if (dataNode != null) {
|
||||||
|
total += dataNode.getCachedConnectionListSize();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Start to quickLoadBalance.
|
||||||
|
HashSet<ConnectionInfo> removedConnectionList = new HashSet<>();
|
||||||
|
for (HostSpec hostSpec : validDns) {
|
||||||
|
DataNode dataNode = cachedDnList.get(hostSpec);
|
||||||
|
if (dataNode != null) {
|
||||||
|
List<ConnectionInfo> idleConnections = dataNode.filterIdleConnections(quickAutoBalanceStartTime);
|
||||||
|
idle += idleConnections.size();
|
||||||
|
int removedConnectionsSize = (int) (idleConnections.size() * (((double) (100 - minReservedConnectionPercentage)) / 100.0));
|
||||||
|
for (int i = 0; i < removedConnectionsSize; i++) {
|
||||||
|
removedConnectionList.add(idleConnections.get(i));
|
||||||
|
removed++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.abandonedConnectionList.clear();
|
||||||
|
this.abandonedConnectionList.addAll(removedConnectionList);
|
||||||
|
this.totalAbandonedConnectionSize = abandonedConnectionList.size();
|
||||||
|
LOGGER.info(GT.tr("QuickLoadBalancing executes in cluster: {0}, " +
|
||||||
|
"put {1} idle connections into abandonedConnectionList, connections can be closed: {2}, " +
|
||||||
|
"total connection: {3}, minReservedConPerCluster: {4}, minReservedConPerDatanode: {5}.",
|
||||||
|
urlIdentifier, removed, idle, total, this.minReservedConPerCluster, this.minReservedConPerDatanode));
|
||||||
|
return removed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check cached connections validity, and remove invalid connections.
|
||||||
|
*
|
||||||
|
* @return the amount of removed connections of each dn.
|
||||||
|
*/
|
||||||
|
public List<Integer> checkConnectionsValidity() {
|
||||||
|
List<Integer> ans = new ArrayList<>();
|
||||||
|
for (Entry<HostSpec, DataNode> entry : cachedDnList.entrySet()) {
|
||||||
|
DataNode dataNode = entry.getValue();
|
||||||
|
ans.add(dataNode.checkConnectionsValidity());
|
||||||
|
}
|
||||||
|
return ans;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CachedCreatingConnection + 1
|
||||||
|
*
|
||||||
|
* @param hostSpec hostSpec
|
||||||
|
* @return cachedCreatingConnection
|
||||||
|
*/
|
||||||
|
public int incrementCachedCreatingConnectionSize(final HostSpec hostSpec) {
|
||||||
|
if (!cachedDnList.containsKey(hostSpec)) {
|
||||||
|
LOGGER.error(GT.tr("Can not find hostSpec: {0} in Cluster: {1}.",
|
||||||
|
hostSpec.toString(), urlIdentifier));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
DataNode dataNode = cachedDnList.get(hostSpec);
|
||||||
|
if (dataNode != null) {
|
||||||
|
return dataNode.incrementCachedCreatingConnectionSize();
|
||||||
|
} else {
|
||||||
|
LOGGER.error(GT.tr("Can not find hostSpec: {0} in Cluster: {1}.",
|
||||||
|
hostSpec.toString(), urlIdentifier));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get minReservedConPerCluster.
|
||||||
|
*
|
||||||
|
* @return minReservedConPerCluster
|
||||||
|
*/
|
||||||
|
public int getMinReservedConPerCluster() {
|
||||||
|
return minReservedConPerCluster;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get enableMinReservedConPerCluster.
|
||||||
|
*
|
||||||
|
* @return enableMinReservedConPerCluster
|
||||||
|
*/
|
||||||
|
public boolean isEnableMinReservedConPerCluster() {
|
||||||
|
return enableMinReservedConPerCluster;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get minReservedConPerDatanode.
|
||||||
|
*
|
||||||
|
* @return minReservedConPerDatanode
|
||||||
|
*/
|
||||||
|
public int getMinReservedConPerDatanode() {
|
||||||
|
return minReservedConPerDatanode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get enableMInReservedConPerDatanode.
|
||||||
|
*
|
||||||
|
* @return enableMInReservedConPerDatanode
|
||||||
|
*/
|
||||||
|
public boolean isEnableMinReservedConPerDatanode() {
|
||||||
|
return enableMinReservedConPerDatanode;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(urlIdentifier, dns, abandonedConnectionList, cachedDnList, cachedPropertiesList);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(final Object o) {
|
||||||
|
if (this == o)
|
||||||
|
return true;
|
||||||
|
if (o == null || getClass() != o.getClass())
|
||||||
|
return false;
|
||||||
|
final Cluster that = (Cluster) o;
|
||||||
|
return Objects.equals(urlIdentifier, that.urlIdentifier) && Objects.equals(dns, that.dns);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Close connections from abandonedConnectionList.
|
||||||
|
* Jdbc will continue to pop connections from abandonedConnectionList and determine whether each connection can be closed.
|
||||||
|
* If it does, the connection will be close. If it does not, the connection won't be put back into abandonedConnectionList.
|
||||||
|
* The number of connections closed at each cluster for each scheduled task at most: 20% * totalAbandonedConnectionSize.
|
||||||
|
*
|
||||||
|
* @return the number of connections which are closed
|
||||||
|
*/
|
||||||
|
public int closeConnections() {
|
||||||
|
int closed = 0;
|
||||||
|
double ceilError = 0.001;
|
||||||
|
int atMost = (int) (Math.ceil(CLOSE_CONNECTION_PERCENTAGE_EACH_TIME * totalAbandonedConnectionSize) + + ceilError);
|
||||||
|
synchronized (abandonedConnectionList) {
|
||||||
|
if (abandonedConnectionList.isEmpty()) {
|
||||||
|
return closed;
|
||||||
|
}
|
||||||
|
int oldSize = abandonedConnectionList.size();
|
||||||
|
while (!abandonedConnectionList.isEmpty() && closed < atMost) {
|
||||||
|
ConnectionInfo connectionInfo = abandonedConnectionList.poll();
|
||||||
|
HostSpec hostSpec = connectionInfo.getHostSpec();
|
||||||
|
// The connections shouldn't be null.
|
||||||
|
if (hostSpec == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// The connections should be valid.
|
||||||
|
if (!connectionInfo.checkConnectionIsValid()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// The state of connection may change after put into abandonedConnectionList,
|
||||||
|
// so it's necessary to recheck it which can be closed.
|
||||||
|
if (!connectionInfo.checkConnectionCanBeClosed(quickAutoBalanceStartTime)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
DataNode dataNode = cachedDnList.get(hostSpec);
|
||||||
|
if (dataNode == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
boolean hasClosed = dataNode.closeConnection(connectionInfo.getPgConnection());
|
||||||
|
if (hasClosed) {
|
||||||
|
closed++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (abandonedConnectionList.isEmpty()) {
|
||||||
|
this.quickAutoBalanceStartTime = 0;
|
||||||
|
this.totalAbandonedConnectionSize = 0;
|
||||||
|
}
|
||||||
|
LOGGER.info(GT.tr("Close connections execute in cluster: {0}, closed connections: {1}, " +
|
||||||
|
"size of abandonedConnectionList before closing: {2}," +
|
||||||
|
" size of abandonedConnectionList after closing: {3}.",
|
||||||
|
urlIdentifier, closed, oldSize, abandonedConnectionList.size()));
|
||||||
|
}
|
||||||
|
return closed;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,246 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) Huawei Technologies Co., Ltd. 2023. All rights reserved.
|
||||||
|
*
|
||||||
|
* openGauss is licensed under Mulan PSL v2.
|
||||||
|
* You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||||
|
* You may obtain a copy of Mulan PSL v2 at:
|
||||||
|
*
|
||||||
|
* http://license.coscl.org.cn/MulanPSL2
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||||
|
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||||
|
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||||
|
* See the Mulan PSL v2 for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.postgresql.quickautobalance;
|
||||||
|
|
||||||
|
import org.postgresql.PGProperty;
|
||||||
|
import org.postgresql.core.QueryExecutor;
|
||||||
|
import org.postgresql.core.SetupQueryRunner;
|
||||||
|
import org.postgresql.jdbc.PgConnection;
|
||||||
|
import org.postgresql.jdbc.StatementCancelState;
|
||||||
|
import org.postgresql.log.Log;
|
||||||
|
import org.postgresql.log.Logger;
|
||||||
|
import org.postgresql.util.GT;
|
||||||
|
import org.postgresql.util.HostSpec;
|
||||||
|
import org.postgresql.util.PSQLException;
|
||||||
|
import org.postgresql.util.PSQLState;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Properties;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Connection info used in quick auto balance.
|
||||||
|
*/
|
||||||
|
public class ConnectionInfo {
|
||||||
|
/**
|
||||||
|
* Default maxIdleTimeBeforeTerminal.
|
||||||
|
*/
|
||||||
|
public static final long DEFAULT_MAX_IDLE_TIME_BEFORE_TERMINAL = 30L;
|
||||||
|
|
||||||
|
public static final String ENABLE_QUICK_AUTO_BALANCE_PARAMS = "true";
|
||||||
|
|
||||||
|
private static Log LOGGER = Logger.getLogger(ConnectionInfo.class.getName());
|
||||||
|
|
||||||
|
private final PgConnection pgConnection;
|
||||||
|
|
||||||
|
private final long createTimeStamp;
|
||||||
|
|
||||||
|
private final String autoBalance;
|
||||||
|
|
||||||
|
private boolean enableQuickAutoBalance;
|
||||||
|
|
||||||
|
// max idle time of connection, units: second
|
||||||
|
private long maxIdleTimeBeforeTerminal;
|
||||||
|
|
||||||
|
private final HostSpec hostSpec;
|
||||||
|
|
||||||
|
private volatile StatementCancelState connectionState;
|
||||||
|
|
||||||
|
// the timestamp when state change last time
|
||||||
|
private volatile long stateLastChangedTimeStamp;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(pgConnection, createTimeStamp, autoBalance, enableQuickAutoBalance,
|
||||||
|
maxIdleTimeBeforeTerminal, hostSpec);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConnectionInfo(PgConnection pgConnection, Properties properties, HostSpec hostSpec)
|
||||||
|
throws PSQLException {
|
||||||
|
this.pgConnection = pgConnection;
|
||||||
|
this.connectionState = StatementCancelState.IDLE;
|
||||||
|
this.createTimeStamp = System.currentTimeMillis();
|
||||||
|
this.stateLastChangedTimeStamp = createTimeStamp;
|
||||||
|
this.autoBalance = properties.getProperty("autoBalance", "");
|
||||||
|
this.maxIdleTimeBeforeTerminal = DEFAULT_MAX_IDLE_TIME_BEFORE_TERMINAL;
|
||||||
|
this.hostSpec = hostSpec;
|
||||||
|
parseEnableQuickAutoBalance(properties);
|
||||||
|
parseMaxIdleTimeBeforeTerminal(properties);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void parseEnableQuickAutoBalance(Properties properties) throws PSQLException {
|
||||||
|
if (EnableQuickAutoBalanceParams.TRUE.getValue()
|
||||||
|
.equals(PGProperty.ENABLE_QUICK_AUTO_BALANCE.get(properties))) {
|
||||||
|
this.enableQuickAutoBalance = true;
|
||||||
|
} else if (EnableQuickAutoBalanceParams.FALSE.getValue()
|
||||||
|
.equals(PGProperty.ENABLE_QUICK_AUTO_BALANCE.get(properties))) {
|
||||||
|
this.enableQuickAutoBalance = false;
|
||||||
|
} else {
|
||||||
|
throw new PSQLException(
|
||||||
|
GT.tr("Parameter enableQuickAutoBalance={0} parsed failed, value range: '{true, false'}).",
|
||||||
|
PGProperty.ENABLE_QUICK_AUTO_BALANCE.get(properties)), PSQLState.INVALID_PARAMETER_VALUE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void parseMaxIdleTimeBeforeTerminal(Properties properties) throws PSQLException {
|
||||||
|
long inputMaxIdleTime;
|
||||||
|
try {
|
||||||
|
String param = PGProperty.MAX_IDLE_TIME_BEFORE_TERMINAL.get(properties);
|
||||||
|
inputMaxIdleTime = Long.parseLong(param);
|
||||||
|
if (inputMaxIdleTime >= Long.MAX_VALUE / 1000) {
|
||||||
|
throw new PSQLException(
|
||||||
|
GT.tr("Parameter maxIdleTimeBeforeTerminal={0} can not be bigger than {1}, value range: long & [0,{1}).",
|
||||||
|
inputMaxIdleTime, Long.MAX_VALUE / 1000), PSQLState.INVALID_PARAMETER_VALUE);
|
||||||
|
}
|
||||||
|
if (inputMaxIdleTime < 0) {
|
||||||
|
throw new PSQLException(
|
||||||
|
GT.tr("Parameter maxIdleTimeBeforeTerminal={0} can not be less than 0, value range: long & [0,{1}).",
|
||||||
|
inputMaxIdleTime, Long.MAX_VALUE / 1000), PSQLState.INVALID_PARAMETER_VALUE);
|
||||||
|
|
||||||
|
}
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
throw new PSQLException(
|
||||||
|
GT.tr("Parameter maxIdleTimeBeforeTerminal parsed failed, value range: long & [0,{0}).",
|
||||||
|
Long.MAX_VALUE / 1000), PSQLState.INVALID_PARAMETER_TYPE);
|
||||||
|
}
|
||||||
|
this.maxIdleTimeBeforeTerminal = inputMaxIdleTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum EnableQuickAutoBalanceParams {
|
||||||
|
TRUE("true"),
|
||||||
|
FALSE("false");
|
||||||
|
|
||||||
|
private final String value;
|
||||||
|
|
||||||
|
EnableQuickAutoBalanceParams(String value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get value.
|
||||||
|
*
|
||||||
|
* @return value
|
||||||
|
*/
|
||||||
|
public String getValue() {
|
||||||
|
return this.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public StatementCancelState getConnectionState() {
|
||||||
|
return connectionState;
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void setConnectionState(StatementCancelState state) {
|
||||||
|
if (state != null && !connectionState.equals(state)) {
|
||||||
|
connectionState = state;
|
||||||
|
stateLastChangedTimeStamp = System.currentTimeMillis();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getMaxIdleTimeBeforeTerminal() {
|
||||||
|
return maxIdleTimeBeforeTerminal;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAutoBalance() {
|
||||||
|
return autoBalance;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PgConnection getPgConnection() {
|
||||||
|
return pgConnection;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(final Object o) {
|
||||||
|
if (this == o)
|
||||||
|
return true;
|
||||||
|
if (o == null || getClass() != o.getClass())
|
||||||
|
return false;
|
||||||
|
final ConnectionInfo that = (ConnectionInfo) o;
|
||||||
|
return createTimeStamp == that.createTimeStamp && enableQuickAutoBalance == that.enableQuickAutoBalance &&
|
||||||
|
maxIdleTimeBeforeTerminal == that.maxIdleTimeBeforeTerminal && pgConnection.equals(that.pgConnection) &&
|
||||||
|
autoBalance.equals(that.autoBalance) && hostSpec.equals(that.hostSpec);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check whether the connection can be closed.
|
||||||
|
* The judgement conditions are as follows:
|
||||||
|
* 1. The connection enables quickAutoBalance.
|
||||||
|
* 2. The quickAutoBalance start time is later than the connection create time.
|
||||||
|
* 3. The connection state is idle.
|
||||||
|
* 4. The connection keeps idle at least maxIdleTimeBeforeTerminal seconds.
|
||||||
|
*
|
||||||
|
* @param quickAutoBalanceStartTime quickAutoBalance start time
|
||||||
|
* @return whether the connection can be closed
|
||||||
|
*/
|
||||||
|
public synchronized boolean checkConnectionCanBeClosed(long quickAutoBalanceStartTime) {
|
||||||
|
if (pgConnection == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (LOGGER.isDebugEnabled()) {
|
||||||
|
LOGGER.debug(GT.tr("checkConnectionCanBeClosed: server ip={0}, enableQuickAutoBalance={1}, " +
|
||||||
|
"quickAutoBalanceStartTime={2}, createTimeStamp={3}, connectionState={4}, " +
|
||||||
|
"stateLastChangedTimeStamp={5}, currentTimeMillis={6}",
|
||||||
|
hostSpec.toString(), isEnableQuickAutoBalance(), quickAutoBalanceStartTime, createTimeStamp,
|
||||||
|
connectionState, stateLastChangedTimeStamp, System.currentTimeMillis()));
|
||||||
|
}
|
||||||
|
if (!isEnableQuickAutoBalance()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (quickAutoBalanceStartTime < createTimeStamp) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!connectionState.equals(StatementCancelState.IDLE)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return System.currentTimeMillis() - stateLastChangedTimeStamp > maxIdleTimeBeforeTerminal * 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isEnableQuickAutoBalance() {
|
||||||
|
return enableQuickAutoBalance;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check whether a connection is valid.
|
||||||
|
*
|
||||||
|
* @return whether a connection is valid
|
||||||
|
*/
|
||||||
|
public boolean checkConnectionIsValid() {
|
||||||
|
boolean isConnectionValid;
|
||||||
|
try {
|
||||||
|
QueryExecutor queryExecutor = pgConnection.getQueryExecutor();
|
||||||
|
byte[][] bit = SetupQueryRunner.run(queryExecutor, "select 1", true);
|
||||||
|
if (bit == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
String result = queryExecutor.getEncoding().decode(bit[0]);
|
||||||
|
isConnectionValid = result != null && result.equals("1");
|
||||||
|
} catch (SQLException | IOException e) {
|
||||||
|
LOGGER.info(GT.tr("Check connection isValid failed."));
|
||||||
|
isConnectionValid = false;
|
||||||
|
}
|
||||||
|
return isConnectionValid;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get hostSpec
|
||||||
|
*
|
||||||
|
* @return hostSpec
|
||||||
|
*/
|
||||||
|
public HostSpec getHostSpec() {
|
||||||
|
return hostSpec;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,295 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) Huawei Technologies Co., Ltd. 2023. All rights reserved.
|
||||||
|
*
|
||||||
|
* openGauss is licensed under Mulan PSL v2.
|
||||||
|
* You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||||
|
* You may obtain a copy of Mulan PSL v2 at:
|
||||||
|
*
|
||||||
|
* http://license.coscl.org.cn/MulanPSL2
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||||
|
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||||
|
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||||
|
* See the Mulan PSL v2 for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.postgresql.quickautobalance;
|
||||||
|
|
||||||
|
import org.postgresql.Driver;
|
||||||
|
import org.postgresql.QueryCNListUtils;
|
||||||
|
import org.postgresql.jdbc.PgConnection;
|
||||||
|
import org.postgresql.jdbc.StatementCancelState;
|
||||||
|
import org.postgresql.log.Log;
|
||||||
|
import org.postgresql.log.Logger;
|
||||||
|
import org.postgresql.util.GT;
|
||||||
|
import org.postgresql.util.HostSpec;
|
||||||
|
import org.postgresql.util.PSQLException;
|
||||||
|
import org.postgresql.util.PSQLState;
|
||||||
|
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
import java.util.Properties;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Connection manager of quick load balancing.
|
||||||
|
*/
|
||||||
|
public class ConnectionManager {
|
||||||
|
private static final Log LOGGER = Logger.getLogger(ConnectionManager.class.getName());
|
||||||
|
|
||||||
|
private static final String AUTO_BALANCE = "autoBalance";
|
||||||
|
|
||||||
|
private static final String LEAST_CONN = "leastconn";
|
||||||
|
|
||||||
|
private final Map<String, Cluster> cachedClusters;
|
||||||
|
|
||||||
|
private ConnectionManager() {
|
||||||
|
this.cachedClusters = new ConcurrentHashMap<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Choose abandoned connections from abandonedConnections of each cluster.
|
||||||
|
* The number of connections that each cluster closes : CLOSE_CONNECTION_PERIOD * CLOSE_CONNECTION_PER_SECOND.
|
||||||
|
*
|
||||||
|
* @return the number of connections closed per cluster
|
||||||
|
*/
|
||||||
|
public List<Integer> closeConnections() {
|
||||||
|
List<Integer> ans = new ArrayList<>();
|
||||||
|
for (Entry<String, Cluster> entry : cachedClusters.entrySet()) {
|
||||||
|
Cluster cluster = entry.getValue();
|
||||||
|
int num = 0;
|
||||||
|
num += cluster != null ? cluster.closeConnections() : 0;
|
||||||
|
ans.add(num);
|
||||||
|
}
|
||||||
|
return ans;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* check whether the properties enable leastconn.
|
||||||
|
*
|
||||||
|
* @param properties properties
|
||||||
|
* @return whether the properties enable leastconn.
|
||||||
|
*/
|
||||||
|
public static boolean checkEnableLeastConn(Properties properties) {
|
||||||
|
if (properties == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!LEAST_CONN.equals(properties.getProperty(AUTO_BALANCE, ""))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
HostSpec[] hostSpecs = Driver.getURLHostSpecs(properties);
|
||||||
|
if (hostSpecs.length <= 1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class Holder {
|
||||||
|
private static final ConnectionManager INSTANCE = new ConnectionManager();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ConnectionManager getInstance() {
|
||||||
|
return Holder.INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set cluster into connection manager.
|
||||||
|
*
|
||||||
|
* @param properties properties
|
||||||
|
* @return set or not.
|
||||||
|
*/
|
||||||
|
public boolean setCluster(Properties properties) throws PSQLException {
|
||||||
|
if (!checkEnableLeastConn(properties)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
String urlIdentifier = QueryCNListUtils.keyFromURL(properties);
|
||||||
|
// create a cluster if it doesn't exist in cachedClusters.
|
||||||
|
if (!cachedClusters.containsKey(urlIdentifier)) {
|
||||||
|
synchronized (cachedClusters) {
|
||||||
|
if (!cachedClusters.containsKey(urlIdentifier)) {
|
||||||
|
Cluster cluster = new Cluster(urlIdentifier, properties);
|
||||||
|
cachedClusters.put(urlIdentifier, cluster);
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cache this connection, if it's configured with autoBalance = "leastconn".
|
||||||
|
*
|
||||||
|
* @param pgConnection connection
|
||||||
|
* @param properties properties
|
||||||
|
* @return if insert the connection into connectionManager.
|
||||||
|
*/
|
||||||
|
public boolean setConnection(PgConnection pgConnection, Properties properties) throws PSQLException {
|
||||||
|
if (!checkEnableLeastConn(properties)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
String URLIdentifier = QueryCNListUtils.keyFromURL(properties);
|
||||||
|
// create a cluster if it doesn't exist in cachedClusters.
|
||||||
|
if (!cachedClusters.containsKey(URLIdentifier)) {
|
||||||
|
synchronized (cachedClusters) {
|
||||||
|
if (!cachedClusters.containsKey(URLIdentifier)) {
|
||||||
|
Cluster cluster = new Cluster(URLIdentifier, properties);
|
||||||
|
cluster.setConnection(pgConnection, properties);
|
||||||
|
cachedClusters.put(URLIdentifier, cluster);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
cachedClusters.get(URLIdentifier).setConnection(pgConnection, properties);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set connection state of cached connections if it exists.
|
||||||
|
*
|
||||||
|
* @param pgConnection pgConnection
|
||||||
|
* @param state state
|
||||||
|
*/
|
||||||
|
public void setConnectionState(PgConnection pgConnection, StatementCancelState state) throws PSQLException {
|
||||||
|
String url;
|
||||||
|
try {
|
||||||
|
url = pgConnection.getURL();
|
||||||
|
} catch (SQLException e) {
|
||||||
|
LOGGER.error(GT.tr("Can't get url from pgConnection."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String URLIdentifier = getURLIdentifierFromUrl(url);
|
||||||
|
Cluster cluster = cachedClusters.get(URLIdentifier);
|
||||||
|
if (cluster != null) {
|
||||||
|
cluster.setConnectionState(pgConnection, state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Cluster getCluster(String URLIdentifier) {
|
||||||
|
return cachedClusters.get(URLIdentifier);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get URLIdentifier from url, which is a unique id of cluster.
|
||||||
|
*
|
||||||
|
* @param url url
|
||||||
|
* @return URLIdentifier
|
||||||
|
*/
|
||||||
|
public static String getURLIdentifierFromUrl(String url) throws PSQLException {
|
||||||
|
HostSpec[] hostSpecs;
|
||||||
|
try {
|
||||||
|
String pgHostUrl = url.split("//")[1].split("/")[0];
|
||||||
|
String[] pgHosts = pgHostUrl.split(",");
|
||||||
|
hostSpecs = new HostSpec[pgHosts.length];
|
||||||
|
for (int i = 0; i < hostSpecs.length; i++) {
|
||||||
|
hostSpecs[i] = new HostSpec(pgHosts[i].split(":")[0],
|
||||||
|
Integer.parseInt(pgHosts[i].split(":")[1]));
|
||||||
|
}
|
||||||
|
Arrays.sort(hostSpecs);
|
||||||
|
|
||||||
|
} catch (ArrayIndexOutOfBoundsException e) {
|
||||||
|
throw new PSQLException(
|
||||||
|
GT.tr("Parsed url={0} failed.", url), PSQLState.INVALID_PARAMETER_VALUE);
|
||||||
|
}
|
||||||
|
return Arrays.toString(hostSpecs);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check whether the connections are valid in each cluster, and remove invalid connections.
|
||||||
|
*
|
||||||
|
* @return the number of connections removed from cache.
|
||||||
|
*/
|
||||||
|
public List<Integer> checkConnectionsValidity() {
|
||||||
|
List<Integer> ans = new ArrayList<>();
|
||||||
|
for (Entry<String, Cluster> entry : cachedClusters.entrySet()) {
|
||||||
|
Cluster cluster = entry.getValue();
|
||||||
|
int num = 0;
|
||||||
|
if (cluster != null) {
|
||||||
|
List<Integer> removes = cluster.checkConnectionsValidity();
|
||||||
|
for (int remove : removes) {
|
||||||
|
num += remove;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ans.add(num);
|
||||||
|
}
|
||||||
|
return ans;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check datanode states of each cluster.
|
||||||
|
*
|
||||||
|
* @return the number of invalid data nodes of all cluster
|
||||||
|
*/
|
||||||
|
public int checkClusterStates() {
|
||||||
|
int invalidDataNodes = 0;
|
||||||
|
for (Entry<String, Cluster> entry : cachedClusters.entrySet()) {
|
||||||
|
Cluster cluster = entry.getValue();
|
||||||
|
if (cluster != null) {
|
||||||
|
invalidDataNodes += cluster.checkClusterState();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return invalidDataNodes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Increment cachedCreatingConnectionSize.
|
||||||
|
* CachedCreatingConnectionSize indicates the number of connections that have been load balanced,
|
||||||
|
* but haven't been cached into cachedConnectionList yet.
|
||||||
|
*
|
||||||
|
* @param hostSpec hostSpec
|
||||||
|
* @param properties properties
|
||||||
|
* @return cachedCreatingConnectionSize after updating
|
||||||
|
*/
|
||||||
|
public int incrementCachedCreatingConnectionSize(HostSpec hostSpec, Properties properties) {
|
||||||
|
if (!checkEnableLeastConn(properties)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
String URLIdentifier = QueryCNListUtils.keyFromURL(properties);
|
||||||
|
if (cachedClusters.containsKey(URLIdentifier)) {
|
||||||
|
Cluster cluster = cachedClusters.get(URLIdentifier);
|
||||||
|
if (cluster != null) {
|
||||||
|
return cluster.incrementCachedCreatingConnectionSize(hostSpec);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
LOGGER.info(GT.tr("Can not find cluster: {0} in cached clusters.", URLIdentifier));
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decrement cachedCreatingConnectionSize.
|
||||||
|
* CachedCreatingConnectionSize indicates the number of connections that have been load balanced,
|
||||||
|
* but haven't been cached into cachedConnectionList yet.
|
||||||
|
*
|
||||||
|
* @param hostSpec hostSpec
|
||||||
|
* @param properties properties
|
||||||
|
* @return cachedCreatingConnectionSize after updating
|
||||||
|
*/
|
||||||
|
public int decrementCachedCreatingConnectionSize(HostSpec hostSpec, Properties properties) {
|
||||||
|
if (!checkEnableLeastConn(properties)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
String URLIdentifier = QueryCNListUtils.keyFromURL(properties);
|
||||||
|
if (cachedClusters.containsKey(URLIdentifier)) {
|
||||||
|
Cluster cluster = cachedClusters.get(URLIdentifier);
|
||||||
|
if (cluster != null) {
|
||||||
|
return cluster.decrementCachedCreatingConnectionSize(hostSpec);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
LOGGER.info(GT.tr("Can not find cluster: {0} in cached clusters.", URLIdentifier));
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clear() {
|
||||||
|
synchronized (cachedClusters) {
|
||||||
|
cachedClusters.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,361 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) Huawei Technologies Co., Ltd. 2023. All rights reserved.
|
||||||
|
*
|
||||||
|
* openGauss is licensed under Mulan PSL v2.
|
||||||
|
* You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||||
|
* You may obtain a copy of Mulan PSL v2 at:
|
||||||
|
*
|
||||||
|
* http://license.coscl.org.cn/MulanPSL2
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||||
|
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||||
|
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||||
|
* See the Mulan PSL v2 for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.postgresql.quickautobalance;
|
||||||
|
|
||||||
|
import org.postgresql.PGProperty;
|
||||||
|
import org.postgresql.core.PGStream;
|
||||||
|
import org.postgresql.core.QueryExecutor;
|
||||||
|
import org.postgresql.core.SocketFactoryFactory;
|
||||||
|
import org.postgresql.core.v3.ConnectionFactoryImpl;
|
||||||
|
import org.postgresql.jdbc.PgConnection;
|
||||||
|
import org.postgresql.jdbc.SslMode;
|
||||||
|
import org.postgresql.jdbc.StatementCancelState;
|
||||||
|
import org.postgresql.log.Log;
|
||||||
|
import org.postgresql.log.Logger;
|
||||||
|
import org.postgresql.util.GT;
|
||||||
|
import org.postgresql.util.HostSpec;
|
||||||
|
import org.postgresql.util.PSQLException;
|
||||||
|
import org.postgresql.util.PSQLState;
|
||||||
|
|
||||||
|
import javax.net.SocketFactory;
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Properties;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Datanode.
|
||||||
|
*/
|
||||||
|
public class DataNode {
|
||||||
|
private static Log LOGGER = Logger.getLogger(DataNode.class.getName());
|
||||||
|
|
||||||
|
private static final String USERNAME_OR_PASSWORD_INVALID_ERROR_CODE = "28P01";
|
||||||
|
|
||||||
|
// the host of datanode (ip + port)
|
||||||
|
private final HostSpec hostSpec;
|
||||||
|
|
||||||
|
// cached connections
|
||||||
|
private final Map<PgConnection, ConnectionInfo> cachedConnectionList;
|
||||||
|
|
||||||
|
// number of cached connections, before set into cachedConnectionList, and after load balance by leastconn.
|
||||||
|
private final AtomicInteger cachedCreatingConnectionSize;
|
||||||
|
|
||||||
|
private volatile boolean dataNodeState;
|
||||||
|
|
||||||
|
public DataNode(final HostSpec hostSpec) {
|
||||||
|
this.hostSpec = hostSpec;
|
||||||
|
this.cachedConnectionList = new ConcurrentHashMap<>();
|
||||||
|
this.cachedCreatingConnectionSize = new AtomicInteger(0);
|
||||||
|
this.dataNodeState = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set connection state.
|
||||||
|
*
|
||||||
|
* @param pgConnection pgConnection
|
||||||
|
* @param state state
|
||||||
|
*/
|
||||||
|
public void setConnectionState(final PgConnection pgConnection, final StatementCancelState state) {
|
||||||
|
ConnectionInfo connectionInfo = cachedConnectionList.get(pgConnection);
|
||||||
|
if (connectionInfo != null) {
|
||||||
|
connectionInfo.setConnectionState(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set connection.
|
||||||
|
*
|
||||||
|
* @param pgConnection pgConnection
|
||||||
|
* @param properties properties
|
||||||
|
* @param hostSpec hostSpec
|
||||||
|
*/
|
||||||
|
public void setConnection(final PgConnection pgConnection, final Properties properties, final HostSpec hostSpec)
|
||||||
|
throws PSQLException {
|
||||||
|
if (pgConnection == null || properties == null || hostSpec == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!hostSpec.equals(this.hostSpec)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ConnectionInfo connectionInfo = new ConnectionInfo(pgConnection, properties, hostSpec);
|
||||||
|
cachedConnectionList.put(pgConnection, connectionInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get connection info if the connection exits.
|
||||||
|
*
|
||||||
|
* @param pgConnection pgConnection
|
||||||
|
* @return connectionInfo
|
||||||
|
*/
|
||||||
|
public ConnectionInfo getConnectionInfo(PgConnection pgConnection) {
|
||||||
|
if (pgConnection == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return cachedConnectionList.get(pgConnection);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the size of cachedConnectionList.
|
||||||
|
*
|
||||||
|
* @return size of cachedConnectionList
|
||||||
|
*/
|
||||||
|
public int getCachedConnectionListSize() {
|
||||||
|
return cachedConnectionList.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check dn state and the validity of properties.
|
||||||
|
*
|
||||||
|
* @param properties properties
|
||||||
|
* @return result (dnValid, dnInvalid, propertiesInvalid)
|
||||||
|
*/
|
||||||
|
public CheckDnStateResult checkDnStateAndProperties(Properties properties) {
|
||||||
|
boolean isDataNodeValid;
|
||||||
|
Properties singleNodeProperties = new Properties();
|
||||||
|
PGProperty.USER.set(singleNodeProperties, PGProperty.USER.get(properties));
|
||||||
|
PGProperty.PASSWORD.set(singleNodeProperties, PGProperty.PASSWORD.get(properties));
|
||||||
|
PGProperty.PG_DBNAME.set(singleNodeProperties, PGProperty.PG_DBNAME.get(properties));
|
||||||
|
PGProperty.PG_HOST.set(singleNodeProperties, hostSpec.getHost());
|
||||||
|
PGProperty.PG_PORT.set(singleNodeProperties, hostSpec.getPort());
|
||||||
|
try {
|
||||||
|
isDataNodeValid = checkDnState(singleNodeProperties);
|
||||||
|
} catch (PSQLException e) {
|
||||||
|
String cause = e.getCause() != null ? e.getCause().getMessage() : "";
|
||||||
|
LOGGER.info(GT.tr("Can not try connect to dn: {0}, {1}.", hostSpec.toString(), cause.toString()));
|
||||||
|
return CheckDnStateResult.DN_INVALID;
|
||||||
|
} catch (InvocationTargetException e) {
|
||||||
|
Throwable invocationTargetExceptionCause = e.getCause();
|
||||||
|
if (invocationTargetExceptionCause instanceof PSQLException) {
|
||||||
|
PSQLException psqlException = (PSQLException) invocationTargetExceptionCause;
|
||||||
|
String sqlState = psqlException.getSQLState();
|
||||||
|
if (USERNAME_OR_PASSWORD_INVALID_ERROR_CODE.equals(sqlState)) {
|
||||||
|
String cause = e.getCause() != null ? e.getCause().getMessage() : "";
|
||||||
|
LOGGER.info(GT.tr("Cached properties is invalid: {0}.", cause.toString()));
|
||||||
|
return CheckDnStateResult.PROPERTIES_INVALID;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
String cause = e.getCause() != null ? e.getCause().getMessage() : "";
|
||||||
|
LOGGER.info(GT.tr("Can not try connect to dn: {0}, {1}.", hostSpec.toString(), cause.toString()));
|
||||||
|
return CheckDnStateResult.DN_INVALID;
|
||||||
|
}
|
||||||
|
if (isDataNodeValid) {
|
||||||
|
return CheckDnStateResult.DN_VALID;
|
||||||
|
} else {
|
||||||
|
return CheckDnStateResult.DN_INVALID;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filter idle connections from cachedConnectionsList.
|
||||||
|
*
|
||||||
|
* @param quickAutoBalanceStartTime the time since the start of quickAutoBalance
|
||||||
|
* @return idle Connection list
|
||||||
|
*/
|
||||||
|
public List<ConnectionInfo> filterIdleConnections(final long quickAutoBalanceStartTime) {
|
||||||
|
synchronized (cachedConnectionList) {
|
||||||
|
List<ConnectionInfo> idleConnectionList = new ArrayList<>();
|
||||||
|
for (Entry<PgConnection, ConnectionInfo> entry : cachedConnectionList.entrySet()) {
|
||||||
|
ConnectionInfo connectionInfo = entry.getValue();
|
||||||
|
if (connectionInfo != null && connectionInfo.checkConnectionCanBeClosed(quickAutoBalanceStartTime)) {
|
||||||
|
idleConnectionList.add(connectionInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return idleConnectionList;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The result of checking dn state.
|
||||||
|
*/
|
||||||
|
public enum CheckDnStateResult {
|
||||||
|
DN_VALID,
|
||||||
|
DN_INVALID,
|
||||||
|
PROPERTIES_INVALID
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDataNodeState(boolean isDnValid) {
|
||||||
|
this.dataNodeState = isDnValid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean getDataNodeState() {
|
||||||
|
return this.dataNodeState;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* check a dn of the cluster if valid,
|
||||||
|
*
|
||||||
|
* @param properties properties
|
||||||
|
* @return if the dn is valid.
|
||||||
|
* @throws PSQLException psql exception
|
||||||
|
* @throws InvocationTargetException invocation target exception
|
||||||
|
*/
|
||||||
|
public boolean checkDnState(Properties properties) throws PSQLException, InvocationTargetException {
|
||||||
|
Object pgStream = new Object();
|
||||||
|
try {
|
||||||
|
HostSpec dnHostSpec = new HostSpec(properties.getProperty("PGHOST")
|
||||||
|
, Integer.parseInt(properties.getProperty("PGPORT")));
|
||||||
|
SocketFactory socketFactory = SocketFactoryFactory.getSocketFactory(properties);
|
||||||
|
SslMode sslMode = SslMode.of(properties);
|
||||||
|
Class<?> classForName = Class.forName("org.postgresql.core.v3.ConnectionFactoryImpl");
|
||||||
|
Object object = classForName.newInstance();
|
||||||
|
if (!(object instanceof ConnectionFactoryImpl)) {
|
||||||
|
LOGGER.error(GT.tr("classForName.newInstance() doesn't instanceof ConnectionFactoryImpl."));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
ConnectionFactoryImpl connectionFactory = (ConnectionFactoryImpl) object;
|
||||||
|
Method method = connectionFactory.getClass().getDeclaredMethod("tryConnect", String.class,
|
||||||
|
String.class, Properties.class, SocketFactory.class, HostSpec.class, SslMode.class);
|
||||||
|
method.setAccessible(true);
|
||||||
|
pgStream = method.invoke(connectionFactory, properties.getProperty("user"),
|
||||||
|
properties.getProperty("PGDBNAME"), properties, socketFactory, dnHostSpec, sslMode);
|
||||||
|
} catch (InstantiationException | IllegalAccessException | NoSuchMethodException |
|
||||||
|
ClassNotFoundException e) {
|
||||||
|
throw new PSQLException("The queryExecutor of connection can't execute tryConnect",
|
||||||
|
PSQLState.WRONG_OBJECT_TYPE);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pgStream instanceof PGStream) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
LOGGER.error(GT.tr("Stream doesn't instanceof PGStream."));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check cached connections validity, and remove invalid connections.
|
||||||
|
*
|
||||||
|
* @return the amount of removed connections.
|
||||||
|
*/
|
||||||
|
public int checkConnectionsValidity() {
|
||||||
|
int num = 0;
|
||||||
|
for (Entry<PgConnection, ConnectionInfo> entry : cachedConnectionList.entrySet()) {
|
||||||
|
PgConnection pgConnection = entry.getKey();
|
||||||
|
ConnectionInfo connectionInfo = entry.getValue();
|
||||||
|
if (!connectionInfo.checkConnectionIsValid()) {
|
||||||
|
cachedConnectionList.remove(pgConnection);
|
||||||
|
num++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return num;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Close cached connections, and clear cachedConnectionList.
|
||||||
|
* JDBC execute clearCachedConnections when jdbc find an invalid datanode.
|
||||||
|
*
|
||||||
|
* @return size of cachedConnectionList before cleared
|
||||||
|
*/
|
||||||
|
public int clearCachedConnections() {
|
||||||
|
synchronized (cachedConnectionList) {
|
||||||
|
int num = cachedConnectionList.size();
|
||||||
|
for (Map.Entry<PgConnection, ConnectionInfo> entry : cachedConnectionList.entrySet()) {
|
||||||
|
PgConnection pgConnection = entry.getKey();
|
||||||
|
if (pgConnection != null) {
|
||||||
|
QueryExecutor queryExecutor = pgConnection.getQueryExecutor();
|
||||||
|
if (queryExecutor != null && !queryExecutor.isClosed()) {
|
||||||
|
queryExecutor.setAvailability(false);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
LOGGER.error(GT.tr("Fail to close connection, pgConnection = null."));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cachedConnectionList.clear();
|
||||||
|
return num;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Close connection.
|
||||||
|
*
|
||||||
|
* @param pgConnection pgConnection
|
||||||
|
* @return if closed
|
||||||
|
*/
|
||||||
|
public boolean closeConnection(PgConnection pgConnection) {
|
||||||
|
if (pgConnection == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
ConnectionInfo connectionInfo = cachedConnectionList.remove(pgConnection);
|
||||||
|
if (connectionInfo != null) {
|
||||||
|
try {
|
||||||
|
pgConnection.close();
|
||||||
|
return true;
|
||||||
|
} catch (SQLException e) {
|
||||||
|
LOGGER.info(GT.tr("Connection closed failed."), e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get cachedCreatingConnectionSize
|
||||||
|
*
|
||||||
|
* @return cachedCreatingConnectionSize
|
||||||
|
*/
|
||||||
|
public int getCachedCreatingConnectionSize() {
|
||||||
|
return cachedCreatingConnectionSize.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* increment cachedCreatingConnectionSize
|
||||||
|
*
|
||||||
|
* @return cachedCreatingConnectionSize after updated
|
||||||
|
*/
|
||||||
|
public int incrementCachedCreatingConnectionSize() {
|
||||||
|
return cachedCreatingConnectionSize.incrementAndGet();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* decrement cachedCreatingConnectionSize
|
||||||
|
*
|
||||||
|
* @return cachedCreatingConnectionSize after updated
|
||||||
|
*/
|
||||||
|
public int decrementCachedCreatingConnectionSize() {
|
||||||
|
if (cachedCreatingConnectionSize.get() == 0) {
|
||||||
|
// Some of junit tests don't load balance, but setConnection, can generate this error.
|
||||||
|
LOGGER.error(GT.tr("CachedCreatingConnectionSize should not be less than 0, reset to 0."));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return cachedCreatingConnectionSize.decrementAndGet();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(final Object obj) {
|
||||||
|
if (this == obj) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (obj == null || getClass() != obj.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
final DataNode dataNode = (DataNode) obj;
|
||||||
|
return dataNodeState == dataNode.dataNodeState && Objects.equals(hostSpec, dataNode.hostSpec) &&
|
||||||
|
Objects.equals(cachedConnectionList, dataNode.cachedConnectionList);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(hostSpec);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,170 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) Huawei Technologies Co., Ltd. 2023. All rights reserved.
|
||||||
|
*
|
||||||
|
* openGauss is licensed under Mulan PSL v2.
|
||||||
|
* You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||||
|
* You may obtain a copy of Mulan PSL v2 at:
|
||||||
|
*
|
||||||
|
* http://license.coscl.org.cn/MulanPSL2
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||||
|
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||||
|
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||||
|
* See the Mulan PSL v2 for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.postgresql.quickautobalance;
|
||||||
|
|
||||||
|
import org.postgresql.PGProperty;
|
||||||
|
import org.postgresql.log.Log;
|
||||||
|
import org.postgresql.log.Logger;
|
||||||
|
import org.postgresql.util.GT;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Properties;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
|
import java.util.concurrent.ScheduledFuture;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load balance heartBeating.
|
||||||
|
*/
|
||||||
|
public class LoadBalanceHeartBeating {
|
||||||
|
|
||||||
|
private static final int INITIAL_DELAY = 1000;
|
||||||
|
|
||||||
|
private static final int CHECK_CLUSTER_STATE_PERIOD = 1000 * 20;
|
||||||
|
|
||||||
|
// unit: CLOSE_CONNECTION_PER_SECOND
|
||||||
|
private static final int CLOSE_CONNECTION_PERIOD = 1000 * 5;
|
||||||
|
|
||||||
|
// A heartBeating thread used to check clusters' state.
|
||||||
|
// If user has configured 'autoBalance=leastconn', jdbc will start checkConnectionScheduledExecutorService.
|
||||||
|
private static final ScheduledExecutorService checkClusterStateScheduledExecutorService = Executors
|
||||||
|
.newSingleThreadScheduledExecutor(r -> new Thread(r, "loadBalanceHeartBeatingThread"));
|
||||||
|
|
||||||
|
// A heartBeating thread used to close abandoned connections.
|
||||||
|
// If user has configured 'autoBalance=leastconn&enableQuickAutoBalance=true', jdbc will start closeConnectionExecutorService.
|
||||||
|
private static final ScheduledExecutorService closeConnectionExecutorService = Executors
|
||||||
|
.newSingleThreadScheduledExecutor(r -> new Thread(r, "closeConnectionsHeartBeatingThread"));
|
||||||
|
private static Log LOGGER = Logger.getLogger(LoadBalanceHeartBeating.class.getName());
|
||||||
|
private static volatile ScheduledFuture<?> checkClusterStateScheduledFuture = null;
|
||||||
|
|
||||||
|
private static volatile ScheduledFuture<?> closeConnectionScheduledFuture = null;
|
||||||
|
|
||||||
|
// If singleton checkConnectionScheduledExecutorService has started.
|
||||||
|
private static volatile boolean leastConnStarted = false;
|
||||||
|
|
||||||
|
// If singleton closeConnectionExecutorService has started.
|
||||||
|
private static volatile boolean quickAutoBalanceStarted = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether quickAutoBalance has started.
|
||||||
|
*
|
||||||
|
* @return whether quickAutoBalance has started
|
||||||
|
*/
|
||||||
|
public static boolean isLoadBalanceHeartBeatingStarted() {
|
||||||
|
return leastConnStarted && quickAutoBalanceStarted;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get if quickAutoBalance has started.
|
||||||
|
*
|
||||||
|
* @return if quickAutoBalance has started
|
||||||
|
*/
|
||||||
|
public static boolean isQuickAutoBalanceStarted() {
|
||||||
|
return quickAutoBalanceStarted;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get if leastConnStarted has started.
|
||||||
|
*
|
||||||
|
* @return if leastConnStarted has started
|
||||||
|
*/
|
||||||
|
public static boolean isLeastConnStarted() {
|
||||||
|
return leastConnStarted;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start scheduled executor service. There are two singleton scheduled executor service.
|
||||||
|
* If user has configured 'autoBalance=leastconn', jdbc will start checkConnectionScheduledExecutorService.
|
||||||
|
* If user has configured 'autoBalance=leastconn&enableQuickAutoBalance=true', jdbc will start closeConnectionExecutorService.
|
||||||
|
*
|
||||||
|
* @param properties properties
|
||||||
|
*/
|
||||||
|
public static void startScheduledExecutorService(Properties properties) {
|
||||||
|
if (!leastConnStarted) {
|
||||||
|
if (ConnectionManager.checkEnableLeastConn(properties)) {
|
||||||
|
synchronized (LoadBalanceHeartBeating.class) {
|
||||||
|
if (!leastConnStarted) {
|
||||||
|
leastConnStarted = true;
|
||||||
|
checkClusterStateScheduledFuture = checkClusterStateScheduledExecutorService
|
||||||
|
.scheduleAtFixedRate(LoadBalanceHeartBeating::checkClusterStateScheduleTask,
|
||||||
|
INITIAL_DELAY, CHECK_CLUSTER_STATE_PERIOD, TimeUnit.MILLISECONDS);
|
||||||
|
LOGGER.info(GT.tr("Start scheduleExecutorService, period:{0} milliseconds.",
|
||||||
|
CHECK_CLUSTER_STATE_PERIOD));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!quickAutoBalanceStarted) {
|
||||||
|
if (ConnectionManager.checkEnableLeastConn(properties)
|
||||||
|
&& ConnectionInfo.ENABLE_QUICK_AUTO_BALANCE_PARAMS.equals(PGProperty.ENABLE_QUICK_AUTO_BALANCE.get(properties))) {
|
||||||
|
synchronized (LoadBalanceHeartBeating.class) {
|
||||||
|
if (!quickAutoBalanceStarted) {
|
||||||
|
quickAutoBalanceStarted = true;
|
||||||
|
closeConnectionScheduledFuture = closeConnectionExecutorService
|
||||||
|
.scheduleAtFixedRate(LoadBalanceHeartBeating::closeAbandonedConnections,
|
||||||
|
INITIAL_DELAY, CLOSE_CONNECTION_PERIOD, TimeUnit.MILLISECONDS);
|
||||||
|
LOGGER.info(GT.tr("Start closeConnectionScheduledFuture, period:{0} milliseconds.",
|
||||||
|
CLOSE_CONNECTION_PERIOD));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void checkClusterStateScheduleTask() {
|
||||||
|
checkClusterState();
|
||||||
|
checkConnectionValidity();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void closeAbandonedConnections() {
|
||||||
|
List<Integer> closedConnections = ConnectionManager.getInstance().closeConnections();
|
||||||
|
int sum = closedConnections.stream().mapToInt(Integer::intValue).sum();
|
||||||
|
LOGGER.info(GT.tr("Scheduled task: closeAbandonedConnections(), thread id: {0}, " +
|
||||||
|
"amount of closed connections: {1}.", Thread.currentThread().getId(), sum));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void checkClusterState() {
|
||||||
|
int invalidDataNodes = ConnectionManager.getInstance().checkClusterStates();
|
||||||
|
LOGGER.info(GT.tr("Scheduled task: checkClusterState(), thread id: {0}, " +
|
||||||
|
"amount of invalid data nodes: {1}.", Thread.currentThread().getId(), invalidDataNodes));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void checkConnectionValidity() {
|
||||||
|
List<Integer> removes = ConnectionManager.getInstance().checkConnectionsValidity();
|
||||||
|
int sum = removes.stream().mapToInt(Integer::intValue).sum();
|
||||||
|
LOGGER.info(GT.tr("Scheduled task: checkConnectionValidity(), thread id: {0}, " +
|
||||||
|
"amount of removed connections: {1}.", Thread.currentThread().getId(), sum));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stop scheduled executor service.
|
||||||
|
*/
|
||||||
|
public static void stop() {
|
||||||
|
if (checkClusterStateScheduledFuture != null || closeConnectionScheduledFuture != null) {
|
||||||
|
synchronized (LoadBalanceHeartBeating.class) {
|
||||||
|
if (checkClusterStateScheduledFuture != null) {
|
||||||
|
checkClusterStateScheduledFuture.cancel(true);
|
||||||
|
leastConnStarted = false;
|
||||||
|
}
|
||||||
|
if (closeConnectionScheduledFuture != null) {
|
||||||
|
closeConnectionScheduledFuture.cancel(true);
|
||||||
|
quickAutoBalanceStarted = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,67 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) Huawei Technologies Co., Ltd. 2023. All rights reserved.
|
||||||
|
*
|
||||||
|
* openGauss is licensed under Mulan PSL v2.
|
||||||
|
* You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||||
|
* You may obtain a copy of Mulan PSL v2 at:
|
||||||
|
*
|
||||||
|
* http://license.coscl.org.cn/MulanPSL2
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||||
|
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||||
|
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||||
|
* See the Mulan PSL v2 for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.postgresql.quickautobalance;
|
||||||
|
|
||||||
|
import org.postgresql.log.Log;
|
||||||
|
import org.postgresql.log.Logger;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reflect util
|
||||||
|
*/
|
||||||
|
public class ReflectUtil {
|
||||||
|
private static Log LOGGER = Logger.getLogger(ReflectUtil.class.getName());
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the private property of an object.
|
||||||
|
*
|
||||||
|
* @param classz class of object
|
||||||
|
* @param object object
|
||||||
|
* @param t class of the private property
|
||||||
|
* @param fieldName of the private property
|
||||||
|
* @param <T> class of the private property
|
||||||
|
* @return the private property
|
||||||
|
*/
|
||||||
|
public static <T> T getField(Class classz, Object object, Class<T> t, String fieldName) {
|
||||||
|
try {
|
||||||
|
Field field = classz.getDeclaredField(fieldName);
|
||||||
|
field.setAccessible(true);
|
||||||
|
return (T) field.get(object);
|
||||||
|
} catch (NoSuchFieldException | IllegalAccessException e) {
|
||||||
|
LOGGER.error("get reflect field " + classz + "." + fieldName + " error.");
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the private property of an object.
|
||||||
|
*
|
||||||
|
* @param classz class of object
|
||||||
|
* @param object object
|
||||||
|
* @param fieldName of the private property
|
||||||
|
* @param value value of the private property
|
||||||
|
*/
|
||||||
|
public static void setField(Class classz, Object object, String fieldName, Object value) {
|
||||||
|
try {
|
||||||
|
Field field = classz.getDeclaredField(fieldName);
|
||||||
|
field.setAccessible(true);
|
||||||
|
field.set(object, value);
|
||||||
|
} catch (NoSuchFieldException | IllegalAccessException e) {
|
||||||
|
LOGGER.error("set reflect field " + classz + "." + fieldName + " error.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -109,6 +109,20 @@ public class TestUtil {
|
|||||||
return System.getProperty("server", "localhost");
|
return System.getProperty("server", "localhost");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns the Secondary Test server
|
||||||
|
*/
|
||||||
|
public static String getSecondaryServer() {
|
||||||
|
return System.getProperty("secondaryServer", "localhost");
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns the third Test server
|
||||||
|
*/
|
||||||
|
public static String getSecondaryServer2() {
|
||||||
|
return System.getProperty("secondaryServer2", "localhost");
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Returns the Test port
|
* Returns the Test port
|
||||||
*/
|
*/
|
||||||
@ -116,6 +130,20 @@ public class TestUtil {
|
|||||||
return Integer.parseInt(System.getProperty("port", System.getProperty("def_pgport")));
|
return Integer.parseInt(System.getProperty("port", System.getProperty("def_pgport")));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns the Secondary Test port
|
||||||
|
*/
|
||||||
|
public static int getSecondaryPort() {
|
||||||
|
return Integer.parseInt(System.getProperty("secondaryPort", System.getProperty("def_pgport")));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns the Third Test port
|
||||||
|
*/
|
||||||
|
public static int getSecondaryServerPort2() {
|
||||||
|
return Integer.parseInt(System.getProperty("secondaryServerPort2", System.getProperty("def_pgport")));
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Returns the server side prepared statement threshold.
|
* Returns the server side prepared statement threshold.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -0,0 +1,851 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) Huawei Technologies Co., Ltd. 2023. All rights reserved.
|
||||||
|
*
|
||||||
|
* openGauss is licensed under Mulan PSL v2.
|
||||||
|
* You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||||
|
* You may obtain a copy of Mulan PSL v2 at:
|
||||||
|
*
|
||||||
|
* http://license.coscl.org.cn/MulanPSL2
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||||
|
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||||
|
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||||
|
* See the Mulan PSL v2 for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.postgresql.test.quickautobalance;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.postgresql.QueryCNListUtils;
|
||||||
|
import org.postgresql.jdbc.PgConnection;
|
||||||
|
import org.postgresql.log.Log;
|
||||||
|
import org.postgresql.log.Logger;
|
||||||
|
import org.postgresql.quickautobalance.Cluster;
|
||||||
|
import org.postgresql.quickautobalance.ConnectionInfo;
|
||||||
|
import org.postgresql.quickautobalance.ConnectionManager;
|
||||||
|
import org.postgresql.quickautobalance.DataNode;
|
||||||
|
import org.postgresql.quickautobalance.ReflectUtil;
|
||||||
|
import org.postgresql.test.TestUtil;
|
||||||
|
import org.postgresql.util.GT;
|
||||||
|
import org.postgresql.util.HostSpec;
|
||||||
|
import org.postgresql.util.PSQLException;
|
||||||
|
import org.postgresql.util.PSQLState;
|
||||||
|
|
||||||
|
import java.sql.DriverManager;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Properties;
|
||||||
|
import java.util.Queue;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertFalse;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
import static org.junit.Assert.fail;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cluster test
|
||||||
|
*/
|
||||||
|
public class ClusterTest {
|
||||||
|
private static Log LOGGER = Logger.getLogger(ClusterTest.class.getName());
|
||||||
|
|
||||||
|
private static final String FAKE_HOST = "127.127.217.217";
|
||||||
|
|
||||||
|
private static final String FAKE_PORT = "1";
|
||||||
|
|
||||||
|
private static final String FAKE_USER = "fakeuser";
|
||||||
|
|
||||||
|
private static final String FAKE_PASSWORD = "fakepassword";
|
||||||
|
|
||||||
|
private static final int DN_NUM = 3;
|
||||||
|
|
||||||
|
private PgConnection getConnection(String url, Properties properties) throws SQLException {
|
||||||
|
return DriverManager.getConnection(url, properties).unwrap(PgConnection.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
private HostSpec[] initHostSpecs() {
|
||||||
|
HostSpec[] hostSpecs = new HostSpec[DN_NUM];
|
||||||
|
hostSpecs[0] = new HostSpec(TestUtil.getServer(), TestUtil.getPort());
|
||||||
|
hostSpecs[1] = new HostSpec(TestUtil.getSecondaryServer(), TestUtil.getSecondaryPort());
|
||||||
|
hostSpecs[2] = new HostSpec(TestUtil.getSecondaryServer2(), TestUtil.getSecondaryServerPort2());
|
||||||
|
return hostSpecs;
|
||||||
|
}
|
||||||
|
|
||||||
|
private HostSpec[] initHostSpecsWithInvalidNode() {
|
||||||
|
HostSpec[] hostSpecs = new HostSpec[DN_NUM];
|
||||||
|
hostSpecs[0] = new HostSpec(TestUtil.getServer(), TestUtil.getPort());
|
||||||
|
hostSpecs[1] = new HostSpec(TestUtil.getSecondaryServer(), TestUtil.getSecondaryPort());
|
||||||
|
hostSpecs[2] = new HostSpec(FAKE_HOST, Integer.parseInt(FAKE_PORT));
|
||||||
|
return hostSpecs;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean checkHostSpecs(HostSpec[] hostSpecs) {
|
||||||
|
if (hostSpecs.length != DN_NUM) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < DN_NUM; i++) {
|
||||||
|
if ("localhost".equals(hostSpecs[i].getHost())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Properties initPriority(HostSpec[] hostSpecs) {
|
||||||
|
String host = hostSpecs[0].getHost() + "," + hostSpecs[1].getHost() + "," + hostSpecs[2].getHost();
|
||||||
|
String port = hostSpecs[0].getPort() + "," + hostSpecs[1].getPort() + "," + hostSpecs[2].getPort();
|
||||||
|
Properties properties = new Properties();
|
||||||
|
properties.setProperty("PGDBNAME", TestUtil.getDatabase());
|
||||||
|
properties.setProperty("PGHOSTURL", host);
|
||||||
|
properties.setProperty("PGPORT", port);
|
||||||
|
properties.setProperty("PGPORTURL", port);
|
||||||
|
properties.setProperty("PGHOST", host);
|
||||||
|
properties.setProperty("user", TestUtil.getUser());
|
||||||
|
properties.setProperty("password", TestUtil.getPassword());
|
||||||
|
return properties;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String initURL(HostSpec[] hostSpecs) {
|
||||||
|
String host1 = hostSpecs[0].getHost() + ":" + hostSpecs[0].getPort();
|
||||||
|
String host2 = hostSpecs[1].getHost() + ":" + hostSpecs[1].getPort();
|
||||||
|
String host3 = hostSpecs[2].getHost() + ":" + hostSpecs[2].getPort();
|
||||||
|
return "jdbc:postgresql://" + host1 + "," + host2 + "," + host3 + "/" + TestUtil.getDatabase();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void checkConnectionsValidityTest() throws PSQLException {
|
||||||
|
HostSpec[] hostSpecs = initHostSpecs();
|
||||||
|
if (!checkHostSpecs(hostSpecs)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Properties properties = initPriority(hostSpecs);
|
||||||
|
String URLIdentifier = QueryCNListUtils.keyFromURL(properties);
|
||||||
|
Cluster cluster = new Cluster(URLIdentifier, properties);
|
||||||
|
String url = initURL(hostSpecs) + "?autoBalance=leastconn";
|
||||||
|
int total = 20;
|
||||||
|
int remove = 5;
|
||||||
|
List<PgConnection> pgConnectionList = new ArrayList<>();
|
||||||
|
for (int i = 0; i < total; i++) {
|
||||||
|
try {
|
||||||
|
PgConnection pgConnection = getConnection(url, properties);
|
||||||
|
pgConnectionList.add(pgConnection);
|
||||||
|
cluster.setConnection(pgConnection, properties);
|
||||||
|
} catch (SQLException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
fail();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
List<Integer> result = cluster.checkConnectionsValidity();
|
||||||
|
int sum = result.stream().reduce(Integer::sum).orElse(0);
|
||||||
|
assertEquals(0, sum);
|
||||||
|
|
||||||
|
for (int i = 0; i < remove; i++) {
|
||||||
|
try {
|
||||||
|
pgConnectionList.get(i).close();
|
||||||
|
} catch (SQLException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
fail();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sum = 0;
|
||||||
|
result = cluster.checkConnectionsValidity();
|
||||||
|
sum = result.stream().reduce(Integer::sum).orElse(0);
|
||||||
|
assertEquals(remove, sum);
|
||||||
|
ConnectionManager.getInstance().clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void setMinReservedConPerClusterDefaultParamsTest() throws PSQLException {
|
||||||
|
HostSpec[] hostSpecs = initHostSpecs();
|
||||||
|
if (!checkHostSpecs(hostSpecs)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Properties properties = initPriority(hostSpecs);
|
||||||
|
String URLIdentifier = QueryCNListUtils.keyFromURL(properties);
|
||||||
|
Cluster cluster = new Cluster(URLIdentifier, properties);
|
||||||
|
assertFalse(cluster.isEnableMinReservedConPerCluster());
|
||||||
|
assertFalse(cluster.isEnableMinReservedConPerDatanode());
|
||||||
|
assertEquals(cluster.getMinReservedConPerCluster(), 0);
|
||||||
|
assertEquals(cluster.getMinReservedConPerDatanode(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test (expected = SQLException.class)
|
||||||
|
public void setMinReservedConPerClusterParsedFailedTest() throws SQLException {
|
||||||
|
HostSpec[] hostSpecs = initHostSpecs();
|
||||||
|
if (!checkHostSpecs(hostSpecs)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Properties properties = initPriority(hostSpecs);
|
||||||
|
String URLIdentifier = QueryCNListUtils.keyFromURL(properties);
|
||||||
|
Cluster cluster = new Cluster(URLIdentifier, properties);
|
||||||
|
|
||||||
|
String url1 = initURL(hostSpecs) + "?autoBalance=leastconn";
|
||||||
|
properties.setProperty("minReservedConPerCluster", "asafaas");
|
||||||
|
try (PgConnection pgConnection = getConnection(url1, properties)) {
|
||||||
|
cluster.setConnection(pgConnection, properties);
|
||||||
|
} catch (SQLException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
assertEquals(PSQLState.INVALID_PARAMETER_TYPE.getState(), e.getSQLState());
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test (expected = SQLException.class)
|
||||||
|
public void setMinReservedConPerClusterParsedTooSmallTest() throws SQLException {
|
||||||
|
HostSpec[] hostSpecs = initHostSpecs();
|
||||||
|
if (!checkHostSpecs(hostSpecs)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Properties properties = initPriority(hostSpecs);
|
||||||
|
String URLIdentifier = QueryCNListUtils.keyFromURL(properties);
|
||||||
|
Cluster cluster = new Cluster(URLIdentifier, properties);
|
||||||
|
|
||||||
|
String url1 = initURL(hostSpecs) + "?autoBalance=leastconn";
|
||||||
|
properties.setProperty("minReservedConPerCluster", "-1");
|
||||||
|
try (PgConnection pgConnection = getConnection(url1, properties)) {
|
||||||
|
cluster.setConnection(pgConnection, properties);
|
||||||
|
} catch (SQLException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
assertEquals(PSQLState.INVALID_PARAMETER_VALUE.getState(), e.getSQLState());
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test (expected = SQLException.class)
|
||||||
|
public void setMinReservedConPerClusterParsedTooBigTest() throws SQLException {
|
||||||
|
HostSpec[] hostSpecs = initHostSpecs();
|
||||||
|
if (!checkHostSpecs(hostSpecs)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Properties properties = initPriority(hostSpecs);
|
||||||
|
String URLIdentifier = QueryCNListUtils.keyFromURL(properties);
|
||||||
|
Cluster cluster = new Cluster(URLIdentifier, properties);
|
||||||
|
|
||||||
|
String url1 = initURL(hostSpecs) + "?autoBalance=leastconn";
|
||||||
|
properties.setProperty("minReservedConPerCluster", "200");
|
||||||
|
try (PgConnection pgConnection = getConnection(url1, properties)) {
|
||||||
|
cluster.setConnection(pgConnection, properties);
|
||||||
|
} catch (SQLException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
assertEquals(PSQLState.INVALID_PARAMETER_VALUE.getState(), e.getSQLState());
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test (expected = SQLException.class)
|
||||||
|
public void setMinReservedConPerDatanodeParsedFailedTest() throws SQLException {
|
||||||
|
HostSpec[] hostSpecs = initHostSpecs();
|
||||||
|
if (!checkHostSpecs(hostSpecs)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Properties properties = initPriority(hostSpecs);
|
||||||
|
String URLIdentifier = QueryCNListUtils.keyFromURL(properties);
|
||||||
|
Cluster cluster = new Cluster(URLIdentifier, properties);
|
||||||
|
|
||||||
|
String url1 = initURL(hostSpecs) + "?autoBalance=leastconn";
|
||||||
|
properties.setProperty("minReservedConPerDatanode", "asafaas");
|
||||||
|
try (PgConnection pgConnection = getConnection(url1, properties)) {
|
||||||
|
cluster.setConnection(pgConnection, properties);
|
||||||
|
} catch (SQLException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
assertEquals(PSQLState.INVALID_PARAMETER_TYPE.getState(), e.getSQLState());
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test (expected = SQLException.class)
|
||||||
|
public void setMinReservedConPerDatanodeParsedTooSmallTest() throws SQLException {
|
||||||
|
HostSpec[] hostSpecs = initHostSpecs();
|
||||||
|
if (!checkHostSpecs(hostSpecs)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Properties properties = initPriority(hostSpecs);
|
||||||
|
String URLIdentifier = QueryCNListUtils.keyFromURL(properties);
|
||||||
|
Cluster cluster = new Cluster(URLIdentifier, properties);
|
||||||
|
|
||||||
|
String url1 = initURL(hostSpecs) + "?autoBalance=leastconn";
|
||||||
|
properties.setProperty("minReservedConPerDatanode", "-1");
|
||||||
|
try (PgConnection pgConnection = getConnection(url1, properties)) {
|
||||||
|
cluster.setConnection(pgConnection, properties);
|
||||||
|
} catch (SQLException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
assertEquals(PSQLState.INVALID_PARAMETER_VALUE.getState(), e.getSQLState());
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test (expected = SQLException.class)
|
||||||
|
public void setMinReservedConPerDatanodeParsedTooBigTest() throws SQLException {
|
||||||
|
HostSpec[] hostSpecs = initHostSpecs();
|
||||||
|
if (!checkHostSpecs(hostSpecs)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Properties properties = initPriority(hostSpecs);
|
||||||
|
String URLIdentifier = QueryCNListUtils.keyFromURL(properties);
|
||||||
|
Cluster cluster = new Cluster(URLIdentifier, properties);
|
||||||
|
|
||||||
|
String url1 = initURL(hostSpecs) + "?autoBalance=leastconn";
|
||||||
|
properties.setProperty("minReservedConPerDatanode", "200");
|
||||||
|
try (PgConnection pgConnection = getConnection(url1, properties)) {
|
||||||
|
cluster.setConnection(pgConnection, properties);
|
||||||
|
} catch (SQLException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
assertEquals(PSQLState.INVALID_PARAMETER_VALUE.getState(), e.getSQLState());
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void updateParamsFailedTest() throws SQLException {
|
||||||
|
HostSpec[] hostSpecs = initHostSpecs();
|
||||||
|
if (!checkHostSpecs(hostSpecs)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Properties properties = initPriority(hostSpecs);
|
||||||
|
String URLIdentifier = QueryCNListUtils.keyFromURL(properties);
|
||||||
|
Cluster cluster = new Cluster(URLIdentifier, properties);
|
||||||
|
assertFalse(cluster.isEnableMinReservedConPerCluster());
|
||||||
|
assertFalse(cluster.isEnableMinReservedConPerDatanode());
|
||||||
|
assertEquals(cluster.getMinReservedConPerCluster(), 0);
|
||||||
|
assertEquals(cluster.getMinReservedConPerDatanode(), 0);
|
||||||
|
|
||||||
|
String url2 = initURL(hostSpecs) + "?autoBalance=leastconn";
|
||||||
|
properties.setProperty("minReservedConPerCluster", "40");
|
||||||
|
properties.setProperty("minReservedConPerDatanode", "50");
|
||||||
|
PgConnection pgConnection1 = getConnection(url2, properties);
|
||||||
|
cluster.setConnection(pgConnection1, properties);
|
||||||
|
assertTrue(cluster.isEnableMinReservedConPerCluster());
|
||||||
|
assertTrue(cluster.isEnableMinReservedConPerDatanode());
|
||||||
|
assertEquals(cluster.getMinReservedConPerCluster(), 40);
|
||||||
|
assertEquals(cluster.getMinReservedConPerDatanode(), 50);
|
||||||
|
pgConnection1.close();
|
||||||
|
|
||||||
|
String url3 = initURL(hostSpecs) + "?autoBalance=leastconn";
|
||||||
|
properties.setProperty("minReservedConPerCluster", "60");
|
||||||
|
properties.setProperty("minReservedConPerDatanode", "70");
|
||||||
|
PgConnection pgConnection2 = getConnection(url3, properties);
|
||||||
|
cluster.setConnection(pgConnection2, properties);
|
||||||
|
assertTrue(cluster.isEnableMinReservedConPerCluster());
|
||||||
|
assertTrue(cluster.isEnableMinReservedConPerDatanode());
|
||||||
|
assertEquals(cluster.getMinReservedConPerCluster(), 40);
|
||||||
|
assertEquals(cluster.getMinReservedConPerDatanode(), 50);
|
||||||
|
pgConnection2.close();
|
||||||
|
ConnectionManager.getInstance().clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void setConnectionStateSuccessTest() throws SQLException {
|
||||||
|
HostSpec[] hostSpecs = initHostSpecs();
|
||||||
|
if (!checkHostSpecs(hostSpecs)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Properties properties = initPriority(hostSpecs);
|
||||||
|
String URLIdentifier = QueryCNListUtils.keyFromURL(properties);
|
||||||
|
Cluster cluster = new Cluster(URLIdentifier, properties);
|
||||||
|
assertFalse(cluster.isEnableMinReservedConPerCluster());
|
||||||
|
assertFalse(cluster.isEnableMinReservedConPerDatanode());
|
||||||
|
assertEquals(cluster.getMinReservedConPerCluster(), 0);
|
||||||
|
assertEquals(cluster.getMinReservedConPerDatanode(), 0);
|
||||||
|
|
||||||
|
String url2 = initURL(hostSpecs) + "?autoBalance=leastconn";
|
||||||
|
properties.setProperty("minReservedConPerCluster", "40");
|
||||||
|
properties.setProperty("minReservedConPerDatanode", "50");
|
||||||
|
PgConnection pgConnection1 = getConnection(url2, properties);
|
||||||
|
cluster.setConnection(pgConnection1, properties);
|
||||||
|
assertTrue(cluster.isEnableMinReservedConPerCluster());
|
||||||
|
assertTrue(cluster.isEnableMinReservedConPerDatanode());
|
||||||
|
assertEquals(cluster.getMinReservedConPerCluster(), 40);
|
||||||
|
assertEquals(cluster.getMinReservedConPerDatanode(), 50);
|
||||||
|
pgConnection1.close();
|
||||||
|
|
||||||
|
String url3 = initURL(hostSpecs) + "?autoBalance=leastconn";
|
||||||
|
properties.setProperty("minReservedConPerCluster", "20");
|
||||||
|
properties.setProperty("minReservedConPerDatanode", "30");
|
||||||
|
PgConnection pgConnection2 = getConnection(url3, properties);
|
||||||
|
cluster.setConnection(pgConnection2, properties);
|
||||||
|
assertTrue(cluster.isEnableMinReservedConPerCluster());
|
||||||
|
assertTrue(cluster.isEnableMinReservedConPerDatanode());
|
||||||
|
assertEquals(cluster.getMinReservedConPerCluster(), 20);
|
||||||
|
assertEquals(cluster.getMinReservedConPerDatanode(), 30);
|
||||||
|
pgConnection2.close();
|
||||||
|
ConnectionManager.getInstance().clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void setConnectionTest() throws PSQLException {
|
||||||
|
HostSpec[] hostSpecs = initHostSpecs();
|
||||||
|
if (!checkHostSpecs(hostSpecs)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Properties properties = initPriority(hostSpecs);
|
||||||
|
String URLIdentifier = QueryCNListUtils.keyFromURL(properties);
|
||||||
|
Cluster cluster = new Cluster(URLIdentifier, properties);
|
||||||
|
String url = initURL(hostSpecs) + "?autoBalance=leastconn";
|
||||||
|
PgConnection pgConnection;
|
||||||
|
try {
|
||||||
|
pgConnection = getConnection(url, properties);
|
||||||
|
cluster.setConnection(pgConnection, properties);
|
||||||
|
ConnectionInfo connectionInfo = cluster.getConnectionInfo(pgConnection);
|
||||||
|
assertEquals(pgConnection, connectionInfo.getPgConnection());
|
||||||
|
cluster.setConnection(null, properties);
|
||||||
|
cluster.setConnection(pgConnection, null);
|
||||||
|
ConnectionManager.getInstance().clear();
|
||||||
|
} catch (SQLException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
fail();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setConnection(Cluster cluster, String ip, int port, Properties properties) {
|
||||||
|
String url = "jdbc:postgresql://" + ip + ":" + port + "/" + TestUtil.getDatabase();
|
||||||
|
try {
|
||||||
|
final PgConnection pgConnection = getConnection(url, properties);
|
||||||
|
cluster.incrementCachedCreatingConnectionSize(new HostSpec(ip, port));
|
||||||
|
cluster.setConnection(pgConnection, properties);
|
||||||
|
} catch (SQLException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
fail();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void sortDnsByLeastConnTest() throws PSQLException {
|
||||||
|
HostSpec[] hostSpecs = initHostSpecs();
|
||||||
|
if (!checkHostSpecs(hostSpecs)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Properties properties = initPriority(hostSpecs);
|
||||||
|
String URLIdentifier = QueryCNListUtils.keyFromURL(properties);
|
||||||
|
Cluster cluster = new Cluster(URLIdentifier, properties);
|
||||||
|
|
||||||
|
int num1 = 4;
|
||||||
|
int num2 = 6;
|
||||||
|
int num3 = 5;
|
||||||
|
for (int i = 0; i < num1; i++) {
|
||||||
|
setConnection(cluster, TestUtil.getServer(), TestUtil.getPort(), properties);
|
||||||
|
}
|
||||||
|
for (int i = 0; i < num2; i++) {
|
||||||
|
setConnection(cluster, TestUtil.getSecondaryServer(), TestUtil.getSecondaryPort(), properties);
|
||||||
|
}
|
||||||
|
for (int i = 0; i < num3; i++) {
|
||||||
|
setConnection(cluster, TestUtil.getSecondaryServer2(), TestUtil.getSecondaryServerPort2(), properties);
|
||||||
|
}
|
||||||
|
List<HostSpec> result = cluster.sortDnsByLeastConn(Arrays.asList(hostSpecs));
|
||||||
|
LOGGER.info(GT.tr("after sort: {0}", result.toString()));
|
||||||
|
assertEquals(TestUtil.getServer(), result.get(0).getHost());
|
||||||
|
assertEquals(TestUtil.getPort(), result.get(0).getPort());
|
||||||
|
assertEquals(TestUtil.getSecondaryServer(), result.get(2).getHost());
|
||||||
|
assertEquals(TestUtil.getSecondaryPort(), result.get(2).getPort());
|
||||||
|
assertEquals(TestUtil.getSecondaryServer2(), result.get(1).getHost());
|
||||||
|
assertEquals(TestUtil.getSecondaryServerPort2(), result.get(1).getPort());
|
||||||
|
ConnectionManager.getInstance().clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void sortDnsByLeastConnWithOneNodeFailedTest() throws PSQLException {
|
||||||
|
HostSpec[] hostSpecs = initHostSpecs();
|
||||||
|
if (!checkHostSpecs(hostSpecs)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Properties properties = initPriority(hostSpecs);
|
||||||
|
String URLIdentifier = QueryCNListUtils.keyFromURL(properties);
|
||||||
|
Cluster cluster = new Cluster(URLIdentifier, properties);
|
||||||
|
|
||||||
|
int num1 = 4;
|
||||||
|
int num2 = 6;
|
||||||
|
int num3 = 5;
|
||||||
|
for (int i = 0; i < num1; i++) {
|
||||||
|
setConnection(cluster, TestUtil.getServer(), TestUtil.getPort(), properties);
|
||||||
|
}
|
||||||
|
for (int i = 0; i < num2; i++) {
|
||||||
|
setConnection(cluster, TestUtil.getSecondaryServer(), TestUtil.getSecondaryPort(), properties);
|
||||||
|
}
|
||||||
|
for (int i = 0; i < num3; i++) {
|
||||||
|
setConnection(cluster, TestUtil.getSecondaryServer2(), TestUtil.getSecondaryServerPort2(), properties);
|
||||||
|
}
|
||||||
|
Map<HostSpec, DataNode> cachedDnList = ReflectUtil.getField(Cluster.class, cluster, Map.class, "cachedDnList");
|
||||||
|
DataNode dataNode = cachedDnList.getOrDefault(hostSpecs[0], null);
|
||||||
|
ReflectUtil.setField(DataNode.class, dataNode, "dataNodeState", false);
|
||||||
|
List<HostSpec> result = cluster.sortDnsByLeastConn(Arrays.asList(hostSpecs));
|
||||||
|
LOGGER.info(GT.tr("after sort: {0}", result.toString()));
|
||||||
|
assertEquals(TestUtil.getServer(), result.get(2).getHost());
|
||||||
|
assertEquals(TestUtil.getPort(), result.get(2).getPort());
|
||||||
|
assertEquals(TestUtil.getSecondaryServer(), result.get(1).getHost());
|
||||||
|
assertEquals(TestUtil.getSecondaryPort(), result.get(1).getPort());
|
||||||
|
assertEquals(TestUtil.getSecondaryServer2(), result.get(0).getHost());
|
||||||
|
assertEquals(TestUtil.getSecondaryServerPort2(), result.get(0).getPort());
|
||||||
|
ConnectionManager.getInstance().clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void checkDnStateSpecSuccessTest() throws PSQLException {
|
||||||
|
HostSpec[] hostSpecs = initHostSpecs();
|
||||||
|
if (!checkHostSpecs(hostSpecs)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Arrays.sort(hostSpecs);
|
||||||
|
String URLIdentifier = String.valueOf(hostSpecs);
|
||||||
|
String pgHostUrl = hostSpecs[0].getHost() + "," + hostSpecs[1].getHost() + "," + hostSpecs[2].getHost();
|
||||||
|
String pgPortUrl = hostSpecs[0].getPort() + "," + hostSpecs[1].getPort() + "," + hostSpecs[2].getPort();
|
||||||
|
Properties clusterProperties = new Properties();
|
||||||
|
clusterProperties.setProperty("user", TestUtil.getUser());
|
||||||
|
clusterProperties.setProperty("password", TestUtil.getPassword());
|
||||||
|
clusterProperties.setProperty("PGDBNAME", TestUtil.getDatabase());
|
||||||
|
clusterProperties.setProperty("PGHOSTURL", pgHostUrl);
|
||||||
|
clusterProperties.setProperty("PGPORT", pgPortUrl);
|
||||||
|
clusterProperties.setProperty("PGPORTURL", pgPortUrl);
|
||||||
|
clusterProperties.setProperty("PGHOST", pgHostUrl);
|
||||||
|
Cluster cluster = new Cluster(URLIdentifier, clusterProperties);
|
||||||
|
for (int i = 0; i < DN_NUM; i++) {
|
||||||
|
assertTrue(cluster.checkDnState(hostSpecs[i]));
|
||||||
|
}
|
||||||
|
ConnectionManager.getInstance().clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void checkDnStateUserOrPasswordErrorTest() throws PSQLException {
|
||||||
|
HostSpec[] hostSpecs = initHostSpecs();
|
||||||
|
if (!checkHostSpecs(hostSpecs)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Arrays.sort(hostSpecs);
|
||||||
|
String URLIdentifier = String.valueOf(hostSpecs);
|
||||||
|
String pgHostUrl = hostSpecs[0].getHost() + "," + hostSpecs[1].getHost() + "," + hostSpecs[2].getHost();
|
||||||
|
String pgPortUrl = hostSpecs[0].getPort() + "," + hostSpecs[1].getPort() + "," + hostSpecs[2].getPort();
|
||||||
|
Properties invalidProperties = new Properties();
|
||||||
|
invalidProperties.setProperty("user", FAKE_USER);
|
||||||
|
invalidProperties.setProperty("password", FAKE_PASSWORD);
|
||||||
|
invalidProperties.setProperty("PGDBNAME", TestUtil.getDatabase());
|
||||||
|
invalidProperties.setProperty("PGHOSTURL", pgHostUrl);
|
||||||
|
invalidProperties.setProperty("PGPORT", pgPortUrl);
|
||||||
|
invalidProperties.setProperty("PGPORTURL", pgPortUrl);
|
||||||
|
invalidProperties.setProperty("PGHOST", pgHostUrl);
|
||||||
|
Cluster cluster = new Cluster(URLIdentifier, invalidProperties);
|
||||||
|
for (int i = 0; i < DN_NUM; i++) {
|
||||||
|
assertFalse(cluster.checkDnState(hostSpecs[i]));
|
||||||
|
}
|
||||||
|
|
||||||
|
Properties validProperties = new Properties();
|
||||||
|
validProperties.setProperty("user", TestUtil.getUser());
|
||||||
|
validProperties.setProperty("password", TestUtil.getPassword());
|
||||||
|
validProperties.setProperty("PGDBNAME", TestUtil.getDatabase());
|
||||||
|
validProperties.setProperty("PGHOSTURL", pgHostUrl);
|
||||||
|
validProperties.setProperty("PGPORT", pgPortUrl);
|
||||||
|
validProperties.setProperty("PGPORTURL", pgPortUrl);
|
||||||
|
validProperties.setProperty("PGHOST", pgHostUrl);
|
||||||
|
cluster.setProperties(invalidProperties);
|
||||||
|
cluster.setProperties(validProperties);
|
||||||
|
for (int i = 0; i < DN_NUM; i++) {
|
||||||
|
assertTrue(cluster.checkDnState(hostSpecs[i]));
|
||||||
|
}
|
||||||
|
ConnectionManager.getInstance().clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void checkDnStateConnectFailedTest() throws PSQLException {
|
||||||
|
HostSpec[] hostSpecs = new HostSpec[DN_NUM];
|
||||||
|
for (int i = 0; i < DN_NUM; i++) {
|
||||||
|
hostSpecs[i] = new HostSpec(FAKE_HOST, Integer.parseInt(FAKE_PORT));
|
||||||
|
}
|
||||||
|
if (!checkHostSpecs(hostSpecs)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Arrays.sort(hostSpecs);
|
||||||
|
String URLIdentifier = Arrays.toString(hostSpecs);
|
||||||
|
String pgHostUrl = hostSpecs[0].getHost() + "," + hostSpecs[1].getHost() + "," + hostSpecs[2].getHost();
|
||||||
|
String pgPortUrl = hostSpecs[0].getPort() + "," + hostSpecs[1].getPort() + "," + hostSpecs[2].getPort();
|
||||||
|
Properties invalidProperties = new Properties();
|
||||||
|
invalidProperties.setProperty("user", TestUtil.getUser());
|
||||||
|
invalidProperties.setProperty("password", TestUtil.getPassword());
|
||||||
|
invalidProperties.setProperty("PGDBNAME", TestUtil.getDatabase());
|
||||||
|
invalidProperties.setProperty("PGHOSTURL", pgHostUrl);
|
||||||
|
invalidProperties.setProperty("PGPORT", pgPortUrl);
|
||||||
|
invalidProperties.setProperty("PGPORTURL", pgPortUrl);
|
||||||
|
invalidProperties.setProperty("PGHOST", pgHostUrl);
|
||||||
|
Cluster cluster = new Cluster(URLIdentifier, invalidProperties);
|
||||||
|
for (int i = 0; i < DN_NUM; i++) {
|
||||||
|
assertFalse(cluster.checkDnState(hostSpecs[i]));
|
||||||
|
}
|
||||||
|
ConnectionManager.getInstance().clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void checkConnectionStateSuccessTest() throws PSQLException {
|
||||||
|
HostSpec[] hostSpecs = initHostSpecs();
|
||||||
|
if (!checkHostSpecs(hostSpecs)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Properties properties = initPriority(hostSpecs);
|
||||||
|
Arrays.sort(hostSpecs);
|
||||||
|
String URLIdentifier = QueryCNListUtils.keyFromURL(properties);
|
||||||
|
Cluster cluster = new Cluster(URLIdentifier, properties);
|
||||||
|
String url = initURL(hostSpecs) + "?autoBalance=leastconn";
|
||||||
|
int total = 10;
|
||||||
|
for (int i = 0; i < total; i++) {
|
||||||
|
try {
|
||||||
|
PgConnection pgConnection = getConnection(url, properties);
|
||||||
|
cluster.setConnection(pgConnection, properties);
|
||||||
|
} catch (SQLException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
fail();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int invalidDn = cluster.checkClusterState();
|
||||||
|
assertEquals(0, invalidDn);
|
||||||
|
ConnectionManager.getInstance().clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void checkConnectionStateOneNodeFailedTest() throws PSQLException {
|
||||||
|
HostSpec[] hostSpecs = initHostSpecsWithInvalidNode();
|
||||||
|
if (!checkHostSpecs(hostSpecs)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Properties properties = initPriority(hostSpecs);
|
||||||
|
Arrays.sort(hostSpecs);
|
||||||
|
String URLIdentifier = QueryCNListUtils.keyFromURL(properties);
|
||||||
|
Cluster cluster = new Cluster(URLIdentifier, properties);
|
||||||
|
String url = initURL(hostSpecs) + "?autoBalance=leastconn";
|
||||||
|
int total = 10;
|
||||||
|
for (int i = 0; i < total; i++) {
|
||||||
|
try {
|
||||||
|
PgConnection pgConnection = getConnection(url, properties);
|
||||||
|
cluster.setConnection(pgConnection, properties);
|
||||||
|
} catch (SQLException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
fail();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int invalidDn = cluster.checkClusterState();
|
||||||
|
assertEquals(1, invalidDn);
|
||||||
|
ConnectionManager.getInstance().clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void checkConnectionStateAndQuickLoadBalanceTest() throws PSQLException {
|
||||||
|
HostSpec[] hostSpecs = initHostSpecs();
|
||||||
|
if (!checkHostSpecs(hostSpecs)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Properties properties = initPriority(hostSpecs);
|
||||||
|
properties.setProperty("autoBalance", "leastconn");
|
||||||
|
properties.setProperty("maxIdleTimeBeforeTerminal", "1");
|
||||||
|
properties.setProperty("enableQuickAutoBalance", "true");
|
||||||
|
Arrays.sort(hostSpecs);
|
||||||
|
String URLIdentifier = QueryCNListUtils.keyFromURL(properties);
|
||||||
|
Cluster cluster = new Cluster(URLIdentifier, properties);
|
||||||
|
String url = initURL(hostSpecs) + "?autoBalance=leastconn";
|
||||||
|
int total = 9;
|
||||||
|
for (int i = 0; i < total; i++) {
|
||||||
|
try {
|
||||||
|
PgConnection pgConnection = getConnection(url, properties);
|
||||||
|
cluster.setConnection(pgConnection, properties);
|
||||||
|
} catch (SQLException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
fail();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
Thread.sleep(1100);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
fail();
|
||||||
|
}
|
||||||
|
// set dnState='false' of dn1
|
||||||
|
|
||||||
|
Map<HostSpec, DataNode> cachedDnList = ReflectUtil.getField(Cluster.class, cluster,
|
||||||
|
Map.class, "cachedDnList");
|
||||||
|
DataNode dataNode = cachedDnList.get(hostSpecs[0]);
|
||||||
|
dataNode.setDataNodeState(false);
|
||||||
|
int invalidDn = cluster.checkClusterState();
|
||||||
|
assertEquals(0, invalidDn);
|
||||||
|
// check cluster state: jdbc will find dnState change to true from false, and execute quickAutoBalance.
|
||||||
|
Queue<ConnectionInfo> abandonedConnectionList = ReflectUtil.getField(Cluster.class, cluster,
|
||||||
|
Queue.class, "abandonedConnectionList");
|
||||||
|
assertEquals(6, abandonedConnectionList.size());
|
||||||
|
ConnectionManager.getInstance().clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void checkConnectionStateAndQuickLoadBalanceWithParamsTest() throws PSQLException {
|
||||||
|
HostSpec[] hostSpecs = initHostSpecs();
|
||||||
|
if (!checkHostSpecs(hostSpecs)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// init properties with quickLoadBalance.
|
||||||
|
Properties properties = initPriority(hostSpecs);
|
||||||
|
properties.setProperty("autoBalance", "leastconn");
|
||||||
|
properties.setProperty("maxIdleTimeBeforeTerminal", "1");
|
||||||
|
properties.setProperty("enableQuickAutoBalance", "true");
|
||||||
|
properties.setProperty("minReservedConPerDatanode", "0");
|
||||||
|
properties.setProperty("minReservedConPerCluster", "75");
|
||||||
|
Arrays.sort(hostSpecs);
|
||||||
|
String URLIdentifier = QueryCNListUtils.keyFromURL(properties);
|
||||||
|
Cluster cluster = new Cluster(URLIdentifier, properties);
|
||||||
|
String url = initURL(hostSpecs) + "?autoBalance=leastconn";
|
||||||
|
int total = 12;
|
||||||
|
// set connection
|
||||||
|
for (int i = 0; i < total; i++) {
|
||||||
|
try {
|
||||||
|
PgConnection pgConnection = getConnection(url, properties);
|
||||||
|
cluster.setConnection(pgConnection, properties);
|
||||||
|
} catch (SQLException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
fail();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
Thread.sleep(1100);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
fail();
|
||||||
|
}
|
||||||
|
// set dnState='false' of dn1
|
||||||
|
Map<HostSpec, DataNode> cachedDnList = ReflectUtil.getField(Cluster.class, cluster, Map.class,
|
||||||
|
"cachedDnList");
|
||||||
|
DataNode dataNode = cachedDnList.get(hostSpecs[0]);
|
||||||
|
dataNode.setDataNodeState(false);
|
||||||
|
// check cluster state: jdbc will find dnState change to true from false, and execute quickAutoBalance.
|
||||||
|
int invalidDn = cluster.checkClusterState();
|
||||||
|
assertEquals(0, invalidDn);
|
||||||
|
Queue<ConnectionInfo> abandonedConnectionList = ReflectUtil.getField(Cluster.class, cluster,
|
||||||
|
Queue.class, "abandonedConnectionList");
|
||||||
|
assertEquals((int) (total / 3 * 2 * (100 - 75) / 100), abandonedConnectionList.size());
|
||||||
|
ConnectionManager.getInstance().clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void incrementCachedCreatingConnectionSizeTest() throws PSQLException {
|
||||||
|
HostSpec[] hostSpecs = initHostSpecs();
|
||||||
|
if (!checkHostSpecs(hostSpecs)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// init properties with quickLoadBalance.
|
||||||
|
Properties properties = initPriority(hostSpecs);
|
||||||
|
properties.setProperty("autoBalance", "leastconn");
|
||||||
|
Arrays.sort(hostSpecs);
|
||||||
|
String URLIdentifier = QueryCNListUtils.keyFromURL(properties);
|
||||||
|
Cluster cluster = new Cluster(URLIdentifier, properties);
|
||||||
|
Map<HostSpec, DataNode> cachedDnList = ReflectUtil.getField(Cluster.class, cluster, Map.class,
|
||||||
|
"cachedDnList");
|
||||||
|
cluster.incrementCachedCreatingConnectionSize(hostSpecs[0]);
|
||||||
|
assertEquals(1, cachedDnList.get(hostSpecs[0]).getCachedCreatingConnectionSize());
|
||||||
|
cluster.incrementCachedCreatingConnectionSize(hostSpecs[0]);
|
||||||
|
assertEquals(2, cachedDnList.get(hostSpecs[0]).getCachedCreatingConnectionSize());
|
||||||
|
cluster.incrementCachedCreatingConnectionSize(hostSpecs[1]);
|
||||||
|
assertEquals(1, cachedDnList.get(hostSpecs[1]).getCachedCreatingConnectionSize());
|
||||||
|
assertEquals(0, cluster.incrementCachedCreatingConnectionSize(new HostSpec(FAKE_HOST,
|
||||||
|
Integer.parseInt(FAKE_PORT))));
|
||||||
|
ConnectionManager.getInstance().clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void decrementCachedCreatingConnectionSizeTest() throws PSQLException {
|
||||||
|
HostSpec[] hostSpecs = initHostSpecs();
|
||||||
|
if (!checkHostSpecs(hostSpecs)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// init properties with quickLoadBalance.
|
||||||
|
Properties properties = initPriority(hostSpecs);
|
||||||
|
properties.setProperty("autoBalance", "leastconn");
|
||||||
|
Arrays.sort(hostSpecs);
|
||||||
|
String URLIdentifier = QueryCNListUtils.keyFromURL(properties);
|
||||||
|
Cluster cluster = new Cluster(URLIdentifier, properties);
|
||||||
|
Map<HostSpec, DataNode> cachedDnList = ReflectUtil.getField(Cluster.class, cluster,
|
||||||
|
Map.class, "cachedDnList");
|
||||||
|
cluster.incrementCachedCreatingConnectionSize(hostSpecs[0]);
|
||||||
|
cluster.incrementCachedCreatingConnectionSize(hostSpecs[0]);
|
||||||
|
assertEquals(2, cachedDnList.get(hostSpecs[0]).getCachedCreatingConnectionSize());
|
||||||
|
cluster.decrementCachedCreatingConnectionSize(hostSpecs[0]);
|
||||||
|
assertEquals(1, cachedDnList.get(hostSpecs[0]).getCachedCreatingConnectionSize());
|
||||||
|
cluster.decrementCachedCreatingConnectionSize(hostSpecs[0]);
|
||||||
|
assertEquals(0, cachedDnList.get(hostSpecs[0]).getCachedCreatingConnectionSize());
|
||||||
|
cluster.decrementCachedCreatingConnectionSize(hostSpecs[0]);
|
||||||
|
assertEquals(0, cachedDnList.get(hostSpecs[0]).getCachedCreatingConnectionSize());
|
||||||
|
assertEquals(0, cluster.incrementCachedCreatingConnectionSize(new HostSpec(FAKE_HOST,
|
||||||
|
Integer.parseInt(FAKE_PORT))));
|
||||||
|
ConnectionManager.getInstance().clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void setPropertiesTest() throws PSQLException {
|
||||||
|
HostSpec[] hostSpecs = initHostSpecs();
|
||||||
|
if (!checkHostSpecs(hostSpecs)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Properties properties1 = initPriority(hostSpecs);
|
||||||
|
properties1.setProperty("autoBalance", "leastconn");
|
||||||
|
String URLIdentifier = QueryCNListUtils.keyFromURL(properties1);
|
||||||
|
Cluster cluster = new Cluster(URLIdentifier, properties1);
|
||||||
|
List<Properties> cachedPropertiesList = ReflectUtil.getField(Cluster.class, cluster, List.class,
|
||||||
|
"cachedPropertiesList");
|
||||||
|
assertEquals(1, cachedPropertiesList.size());
|
||||||
|
assertEquals(TestUtil.getUser(), cachedPropertiesList.get(0).getProperty("user", ""));
|
||||||
|
assertEquals(TestUtil.getPassword(), cachedPropertiesList.get(0).getProperty("password", ""));
|
||||||
|
Properties properties2 = initPriority(hostSpecs);
|
||||||
|
properties2.setProperty("user", "fakeuser");
|
||||||
|
properties2.setProperty("password", "fakepassword");
|
||||||
|
cluster.setProperties(properties2);
|
||||||
|
assertEquals(2, cachedPropertiesList.size());
|
||||||
|
assertEquals("fakeuser", cachedPropertiesList.get(1).getProperty("user", ""));
|
||||||
|
assertEquals("fakepassword", cachedPropertiesList.get(1).getProperty("password", ""));
|
||||||
|
Properties properties3 = initPriority(hostSpecs);
|
||||||
|
properties3.setProperty("password", "fakepassword2");
|
||||||
|
cluster.setProperties(properties3);
|
||||||
|
assertEquals(2, cachedPropertiesList.size());
|
||||||
|
assertEquals(TestUtil.getUser(), cachedPropertiesList.get(0).getProperty("user", ""));
|
||||||
|
assertEquals("fakepassword2", cachedPropertiesList.get(0).getProperty("password", ""));
|
||||||
|
ConnectionManager.getInstance().clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void closeConnectionTest() throws PSQLException {
|
||||||
|
HostSpec[] hostSpecs = initHostSpecs();
|
||||||
|
if (!checkHostSpecs(hostSpecs)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Properties properties = initPriority(hostSpecs);
|
||||||
|
properties.setProperty("autoBalance", "leastconn");
|
||||||
|
properties.setProperty("enableQuickAutoBalance", "true");
|
||||||
|
properties.setProperty("maxIdleTimeBeforeTerminal", "1");
|
||||||
|
Arrays.sort(hostSpecs);
|
||||||
|
String URLIdentifier = QueryCNListUtils.keyFromURL(properties);
|
||||||
|
Cluster cluster = new Cluster(URLIdentifier, properties);
|
||||||
|
String url = initURL(hostSpecs);
|
||||||
|
int total = 50;
|
||||||
|
List<PgConnection> pgConnectionList = new ArrayList<>();
|
||||||
|
for (int i = 0; i < total; i++) {
|
||||||
|
try {
|
||||||
|
PgConnection pgConnection = getConnection(url, properties);
|
||||||
|
cluster.setConnection(pgConnection, properties);
|
||||||
|
pgConnectionList.add(pgConnection);
|
||||||
|
} catch (SQLException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
fail();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
Thread.sleep(1100);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
fail();
|
||||||
|
}
|
||||||
|
Queue<ConnectionInfo> abandonedConnectionList = ReflectUtil.getField(Cluster.class, cluster,
|
||||||
|
Queue.class, "abandonedConnectionList");
|
||||||
|
ReflectUtil.setField(Cluster.class, cluster, "totalAbandonedConnectionSize", total);
|
||||||
|
ReflectUtil.setField(Cluster.class, cluster, "quickAutoBalanceStartTime", Long.MAX_VALUE);
|
||||||
|
for (PgConnection pgConnection : pgConnectionList) {
|
||||||
|
ConnectionInfo connectionInfo = cluster.getConnectionInfo(pgConnection);
|
||||||
|
abandonedConnectionList.add(connectionInfo);
|
||||||
|
}
|
||||||
|
assertEquals((int) (Math.ceil((double) total * 0.2)), cluster.closeConnections());
|
||||||
|
assertEquals((int) (Math.ceil((double) total * 0.2)), cluster.closeConnections());
|
||||||
|
assertEquals((int) (Math.ceil((double) total * 0.2)), cluster.closeConnections());
|
||||||
|
assertEquals((int) (Math.ceil((double) total * 0.2)), cluster.closeConnections());
|
||||||
|
assertEquals(total - 4 * (int) (Math.ceil((double) total * 0.2)), cluster.closeConnections());
|
||||||
|
ConnectionManager.getInstance().clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ -0,0 +1,271 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) Huawei Technologies Co., Ltd. 2023. All rights reserved.
|
||||||
|
*
|
||||||
|
* openGauss is licensed under Mulan PSL v2.
|
||||||
|
* You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||||
|
* You may obtain a copy of Mulan PSL v2 at:
|
||||||
|
*
|
||||||
|
* http://license.coscl.org.cn/MulanPSL2
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||||
|
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||||
|
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||||
|
* See the Mulan PSL v2 for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.postgresql.test.quickautobalance;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.postgresql.jdbc.PgConnection;
|
||||||
|
import org.postgresql.jdbc.StatementCancelState;
|
||||||
|
import org.postgresql.quickautobalance.ConnectionInfo;
|
||||||
|
import org.postgresql.test.TestUtil;
|
||||||
|
import org.postgresql.util.HostSpec;
|
||||||
|
import org.postgresql.util.PSQLException;
|
||||||
|
import org.postgresql.util.PSQLState;
|
||||||
|
|
||||||
|
import java.sql.Connection;
|
||||||
|
import java.sql.DriverManager;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.util.Properties;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertFalse;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ConnectionInfo Test
|
||||||
|
*/
|
||||||
|
public class ConnectionInfoTest {
|
||||||
|
private HostSpec initHost() {
|
||||||
|
return new HostSpec(TestUtil.getServer(), TestUtil.getPort());
|
||||||
|
}
|
||||||
|
|
||||||
|
private Properties initProperties() {
|
||||||
|
Properties properties = new Properties();
|
||||||
|
properties.setProperty("PGDBNAME", TestUtil.getDatabase());
|
||||||
|
properties.setProperty("PGHOSTURL", TestUtil.getServer());
|
||||||
|
properties.setProperty("PGPORT", String.valueOf(TestUtil.getPort()));
|
||||||
|
properties.setProperty("PGPORTURL", String.valueOf(TestUtil.getPort()));
|
||||||
|
properties.setProperty("PGHOST", TestUtil.getServer());
|
||||||
|
properties.setProperty("user", TestUtil.getUser());
|
||||||
|
properties.setProperty("password", TestUtil.getPassword());
|
||||||
|
return properties;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void createConnectionInfoWithDefaultParamsTest() throws SQLException {
|
||||||
|
// ConnectionInfo without quickAutoBalance
|
||||||
|
HostSpec hostSpec = initHost();
|
||||||
|
Properties properties = initProperties();
|
||||||
|
PgConnection pgConnection = DriverManager.getConnection(TestUtil.getURL(), properties)
|
||||||
|
.unwrap(PgConnection.class);
|
||||||
|
ConnectionInfo connectionInfo = new ConnectionInfo(pgConnection, properties, hostSpec);
|
||||||
|
assertEquals(connectionInfo.getPgConnection(), pgConnection);
|
||||||
|
assertEquals(connectionInfo.getHostSpec(), hostSpec);
|
||||||
|
assertEquals(connectionInfo.getConnectionState(), StatementCancelState.IDLE);
|
||||||
|
assertEquals(connectionInfo.getMaxIdleTimeBeforeTerminal(), 30);
|
||||||
|
assertEquals(connectionInfo.getAutoBalance(), "");
|
||||||
|
assertFalse(connectionInfo.isEnableQuickAutoBalance());
|
||||||
|
pgConnection.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = SQLException.class)
|
||||||
|
public void createConnectionInfoEnableQuickAutoBalanceParsedFailedTest() throws SQLException {
|
||||||
|
Properties properties = initProperties();
|
||||||
|
properties.setProperty("autoBalance", "luanma");
|
||||||
|
properties.setProperty("enableQuickAutoBalance", "luanma");
|
||||||
|
HostSpec hostSpec = initHost();
|
||||||
|
try (PgConnection pgConnection = DriverManager.getConnection(TestUtil.getURL(), properties)
|
||||||
|
.unwrap(PgConnection.class)) {
|
||||||
|
ConnectionInfo connectionInfo = new ConnectionInfo(pgConnection, properties, hostSpec);
|
||||||
|
} catch (PSQLException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
assertEquals(PSQLState.INVALID_PARAMETER_VALUE.getState(), e.getSQLState());
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void createConnectionInfoEnableQuickAutoBalanceFalseTest() throws SQLException {
|
||||||
|
Properties properties = initProperties();
|
||||||
|
properties.setProperty("autoBalance", "luanma");
|
||||||
|
properties.setProperty("enableQuickAutoBalance", "false");
|
||||||
|
HostSpec hostSpec = initHost();
|
||||||
|
PgConnection pgConnection = DriverManager.getConnection(TestUtil.getURL(), properties)
|
||||||
|
.unwrap(PgConnection.class);
|
||||||
|
ConnectionInfo connectionInfo = new ConnectionInfo(pgConnection, properties, hostSpec);
|
||||||
|
assertEquals("luanma", connectionInfo.getAutoBalance());
|
||||||
|
assertFalse(connectionInfo.isEnableQuickAutoBalance());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void createConnectionInfoSuccessTest() throws SQLException {
|
||||||
|
Properties properties = initProperties();
|
||||||
|
HostSpec hostSpec = initHost();
|
||||||
|
properties.setProperty("maxIdleTimeBeforeTerminal", "66");
|
||||||
|
properties.setProperty("autoBalance", "leastconn");
|
||||||
|
properties.setProperty("enableQuickAutoBalance", "true");
|
||||||
|
PgConnection pgConnection = DriverManager.getConnection(TestUtil.getURL(), properties)
|
||||||
|
.unwrap(PgConnection.class);
|
||||||
|
ConnectionInfo connectionInfo = new ConnectionInfo(pgConnection, properties, hostSpec);
|
||||||
|
assertEquals(connectionInfo.getMaxIdleTimeBeforeTerminal(), 66);
|
||||||
|
assertTrue(connectionInfo.isEnableQuickAutoBalance());
|
||||||
|
assertEquals("leastconn", connectionInfo.getAutoBalance());
|
||||||
|
pgConnection.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = PSQLException.class)
|
||||||
|
public void createConnectionInfoMaxIdleTimeBeforeTerminalParsedFailedTest() throws SQLException {
|
||||||
|
Properties properties = initProperties();
|
||||||
|
properties.setProperty("maxIdleTimeBeforeTerminal", "111111@@");
|
||||||
|
HostSpec hostSpec = initHost();
|
||||||
|
try {
|
||||||
|
Connection connection = DriverManager.getConnection(TestUtil.getURL(), properties);
|
||||||
|
PgConnection pgConnection = connection.unwrap(PgConnection.class);
|
||||||
|
ConnectionInfo connectionInfo = new ConnectionInfo(pgConnection, properties, hostSpec);
|
||||||
|
} catch (SQLException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
assertEquals(PSQLState.INVALID_PARAMETER_TYPE.getState(), e.getSQLState());
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = SQLException.class)
|
||||||
|
public void createConnectionInfoMaxIdleTimeBeforeTerminalTooBigTest() throws SQLException {
|
||||||
|
Properties properties = initProperties();
|
||||||
|
HostSpec hostSpec = initHost();
|
||||||
|
properties.setProperty("maxIdleTimeBeforeTerminal", String.valueOf(Long.MAX_VALUE));
|
||||||
|
properties.setProperty("enableQuickAutoBalance", "false");
|
||||||
|
try (PgConnection pgConnection = DriverManager.getConnection(TestUtil.getURL(), properties)
|
||||||
|
.unwrap(PgConnection.class)) {
|
||||||
|
ConnectionInfo connectionInfo = new ConnectionInfo(pgConnection, properties, hostSpec);
|
||||||
|
} catch (SQLException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
assertEquals(PSQLState.INVALID_PARAMETER_VALUE.getState(), e.getSQLState());
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = SQLException.class)
|
||||||
|
public void createConnectionInfoMaxIdleTimeBeforeTerminalTooSmallTest() throws SQLException {
|
||||||
|
Properties properties = initProperties();
|
||||||
|
HostSpec hostSpec = initHost();
|
||||||
|
properties.setProperty("maxIdleTimeBeforeTerminal", String.valueOf(-100));
|
||||||
|
properties.setProperty("enableQuickAutoBalance", "false");
|
||||||
|
try (PgConnection pgConnection = DriverManager.getConnection(TestUtil.getURL(), properties)
|
||||||
|
.unwrap(PgConnection.class)) {
|
||||||
|
new ConnectionInfo(pgConnection, properties, hostSpec);
|
||||||
|
} catch (SQLException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
assertEquals(PSQLState.INVALID_PARAMETER_VALUE.getState(), e.getSQLState());
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void checkConnectionCanBeClosedTestPgConnectionNullTest() throws InterruptedException, PSQLException {
|
||||||
|
HostSpec hostSpec = initHost();
|
||||||
|
Properties properties = initProperties();
|
||||||
|
PgConnection pgConnection;
|
||||||
|
properties.setProperty("enableQuickAutoBalance", "true");
|
||||||
|
properties.setProperty("maxIdleTimeBeforeTerminal", "1");
|
||||||
|
Thread.sleep(1100);
|
||||||
|
ConnectionInfo connectionInfo = new ConnectionInfo(null, properties, hostSpec);
|
||||||
|
assertFalse(connectionInfo.checkConnectionCanBeClosed(System.currentTimeMillis()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void checkConnectionCanBeClosedTestPgConnectionUnableQuickAutoBalance() throws SQLException,
|
||||||
|
InterruptedException {
|
||||||
|
HostSpec hostSpec = initHost();
|
||||||
|
Properties properties = initProperties();
|
||||||
|
properties.setProperty("enableQuickAutoBalance", "false");
|
||||||
|
properties.setProperty("maxIdleTimeBeforeTerminal", "1");
|
||||||
|
Thread.sleep(1100);
|
||||||
|
PgConnection pgConnection = DriverManager.getConnection(TestUtil.getURL(), properties)
|
||||||
|
.unwrap(PgConnection.class);
|
||||||
|
ConnectionInfo connectionInfo = new ConnectionInfo(pgConnection, properties, hostSpec);
|
||||||
|
assertFalse(connectionInfo.checkConnectionCanBeClosed(System.currentTimeMillis()));
|
||||||
|
pgConnection.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void checkConnectionCanBeClosedTestStartTimeSmallerThanCreateTimeStamp() throws SQLException,
|
||||||
|
InterruptedException {
|
||||||
|
HostSpec hostSpec = initHost();
|
||||||
|
Properties properties = initProperties();
|
||||||
|
properties.setProperty("enableQuickAutoBalance", "true");
|
||||||
|
properties.setProperty("maxIdleTimeBeforeTerminal", "1");
|
||||||
|
Thread.sleep(1100);
|
||||||
|
PgConnection pgConnection = DriverManager.getConnection(TestUtil.getURL(), properties).unwrap(PgConnection.class);
|
||||||
|
ConnectionInfo connectionInfo = new ConnectionInfo(pgConnection, properties, hostSpec);
|
||||||
|
assertFalse(connectionInfo.checkConnectionCanBeClosed(System.currentTimeMillis() - 1000 * 10));
|
||||||
|
pgConnection.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void checkConnectionCanBeClosedTestConnectionStateNoEqualsToIDLE() throws SQLException, InterruptedException {
|
||||||
|
HostSpec hostSpec = initHost();
|
||||||
|
Properties properties = initProperties();
|
||||||
|
properties.setProperty("enableQuickAutoBalance", "true");
|
||||||
|
properties.setProperty("maxIdleTimeBeforeTerminal", "1");
|
||||||
|
PgConnection pgConnection = DriverManager.getConnection(TestUtil.getURL(), properties)
|
||||||
|
.unwrap(PgConnection.class);
|
||||||
|
ConnectionInfo connectionInfo = new ConnectionInfo(pgConnection, properties, hostSpec);
|
||||||
|
connectionInfo.setConnectionState(StatementCancelState.IN_QUERY);
|
||||||
|
Thread.sleep(1100);
|
||||||
|
assertFalse(connectionInfo.checkConnectionCanBeClosed(System.currentTimeMillis()));
|
||||||
|
pgConnection.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void checkConnectionCanBeCloseTestIDLETimeToShort() throws SQLException, InterruptedException {
|
||||||
|
HostSpec hostSpec = initHost();
|
||||||
|
Properties properties = initProperties();
|
||||||
|
properties.setProperty("enableQuickAutoBalance", "true");
|
||||||
|
properties.setProperty("maxIdleTimeBeforeTerminal", "10");
|
||||||
|
PgConnection pgConnection = DriverManager.getConnection(TestUtil.getURL(), properties).unwrap(PgConnection.class);
|
||||||
|
ConnectionInfo connectionInfo = new ConnectionInfo(pgConnection, properties, hostSpec);
|
||||||
|
Thread.sleep(1100);
|
||||||
|
assertFalse(connectionInfo.checkConnectionCanBeClosed(System.currentTimeMillis()));
|
||||||
|
pgConnection.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void checkConnectionCanBeCloseTestSuccessTest() throws SQLException, InterruptedException {
|
||||||
|
HostSpec hostSpec = initHost();
|
||||||
|
Properties properties = initProperties();
|
||||||
|
properties.setProperty("enableQuickAutoBalance", "true");
|
||||||
|
properties.setProperty("maxIdleTimeBeforeTerminal", "1");
|
||||||
|
PgConnection pgConnection = DriverManager.getConnection(TestUtil.getURL(), properties).unwrap(PgConnection.class);
|
||||||
|
ConnectionInfo connectionInfo = new ConnectionInfo(pgConnection, properties, hostSpec);
|
||||||
|
Thread.sleep(1100);
|
||||||
|
assertTrue(connectionInfo.checkConnectionCanBeClosed(System.currentTimeMillis()));
|
||||||
|
pgConnection.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void checkConnectionIsValidTest() throws SQLException {
|
||||||
|
HostSpec hostSpec = initHost();
|
||||||
|
Properties properties = initProperties();
|
||||||
|
PgConnection pgConnection = DriverManager.getConnection(TestUtil.getURL(), properties)
|
||||||
|
.unwrap(PgConnection.class);
|
||||||
|
ConnectionInfo connectionInfo = new ConnectionInfo(pgConnection, properties, hostSpec);
|
||||||
|
assertTrue(connectionInfo.checkConnectionIsValid());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void checkConnectionIsValidFailedTest() throws SQLException {
|
||||||
|
HostSpec hostSpec = initHost();
|
||||||
|
Properties properties = initProperties();
|
||||||
|
PgConnection pgConnection = DriverManager.getConnection(TestUtil.getURL(), properties)
|
||||||
|
.unwrap(PgConnection.class);
|
||||||
|
ConnectionInfo connectionInfo = new ConnectionInfo(pgConnection, properties, hostSpec);
|
||||||
|
pgConnection.getQueryExecutor().setAvailability(false);
|
||||||
|
pgConnection.close();
|
||||||
|
long before = System.currentTimeMillis();
|
||||||
|
assertFalse(connectionInfo.checkConnectionIsValid());
|
||||||
|
long after = System.currentTimeMillis();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,320 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) Huawei Technologies Co., Ltd. 2023. All rights reserved.
|
||||||
|
*
|
||||||
|
* openGauss is licensed under Mulan PSL v2.
|
||||||
|
* You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||||
|
* You may obtain a copy of Mulan PSL v2 at:
|
||||||
|
*
|
||||||
|
* http://license.coscl.org.cn/MulanPSL2
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||||
|
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||||
|
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||||
|
* See the Mulan PSL v2 for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.postgresql.test.quickautobalance;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.postgresql.jdbc.PgConnection;
|
||||||
|
import org.postgresql.jdbc.StatementCancelState;
|
||||||
|
import org.postgresql.log.Log;
|
||||||
|
import org.postgresql.log.Logger;
|
||||||
|
import org.postgresql.quickautobalance.Cluster;
|
||||||
|
import org.postgresql.quickautobalance.ConnectionInfo;
|
||||||
|
import org.postgresql.quickautobalance.ConnectionManager;
|
||||||
|
import org.postgresql.test.TestUtil;
|
||||||
|
import org.postgresql.util.GT;
|
||||||
|
import org.postgresql.util.HostSpec;
|
||||||
|
import org.postgresql.util.PSQLException;
|
||||||
|
|
||||||
|
import java.sql.DriverManager;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
import java.util.Properties;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertFalse;
|
||||||
|
import static org.junit.Assert.assertNotEquals;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
import static org.junit.Assert.fail;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class ConnectionManagerTest {
|
||||||
|
private static Log LOGGER = Logger.getLogger(ConnectionManagerTest.class.getName());
|
||||||
|
|
||||||
|
private static final String USER = TestUtil.getUser();
|
||||||
|
|
||||||
|
private static final String PASSWORD = TestUtil.getPassword();
|
||||||
|
|
||||||
|
private static final String MASTER_1 = TestUtil.getServer() + ":" + TestUtil.getPort();
|
||||||
|
|
||||||
|
private static final String SECONDARY_1 = TestUtil.getSecondaryServer() + ":" + TestUtil.getSecondaryPort();
|
||||||
|
|
||||||
|
private static final String SECONDARY_2 = TestUtil.getSecondaryServer2() + ":" + TestUtil.getSecondaryServerPort2();
|
||||||
|
|
||||||
|
private static final String DATABASE = TestUtil.getDatabase();
|
||||||
|
|
||||||
|
private static final int DN_NUM = 3;
|
||||||
|
|
||||||
|
private String initURLWithLeastConn() {
|
||||||
|
return "jdbc:postgresql://" + MASTER_1 + "," + SECONDARY_1
|
||||||
|
+ "," + SECONDARY_2 + "/" + DATABASE + "?autoBalance=leastconn&loggerLevel=OFF";
|
||||||
|
}
|
||||||
|
|
||||||
|
private PgConnection getConnection(String url, String user, String password) throws SQLException {
|
||||||
|
return DriverManager.getConnection(url, user, password).unwrap(PgConnection.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
private HostSpec[] initHostSpec() {
|
||||||
|
HostSpec[] hostSpecs = new HostSpec[DN_NUM];
|
||||||
|
hostSpecs[0] = new HostSpec(TestUtil.getServer(), TestUtil.getPort());
|
||||||
|
hostSpecs[1] = new HostSpec(TestUtil.getSecondaryServer(), TestUtil.getSecondaryPort());
|
||||||
|
hostSpecs[2] = new HostSpec(TestUtil.getSecondaryServer2(), TestUtil.getSecondaryServerPort2());
|
||||||
|
return hostSpecs;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Properties initPriority(HostSpec[] hostSpecs) {
|
||||||
|
String host = hostSpecs[0].getHost() + "," + hostSpecs[1].getHost() + "," + hostSpecs[2].getHost();
|
||||||
|
String port = hostSpecs[0].getPort() + "," + hostSpecs[1].getPort() + "," + hostSpecs[2].getPort();
|
||||||
|
Properties properties = new Properties();
|
||||||
|
properties.setProperty("PGDBNAME", TestUtil.getDatabase());
|
||||||
|
properties.setProperty("PGHOSTURL", host);
|
||||||
|
properties.setProperty("PGPORT", port);
|
||||||
|
properties.setProperty("PGPORTURL", port);
|
||||||
|
properties.setProperty("PGHOST", host);
|
||||||
|
properties.setProperty("user", TestUtil.getUser());
|
||||||
|
properties.setProperty("password", TestUtil.getPassword());
|
||||||
|
return properties;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void setConnectionTest() throws ClassNotFoundException, SQLException {
|
||||||
|
if (String.valueOf(TestUtil.getSecondaryPort()).equals(System.getProperty("def_pgport"))
|
||||||
|
|| String.valueOf(TestUtil.getSecondaryServerPort2()).equals(System.getProperty("def_pgport"))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Class.forName("org.postgresql.Driver");
|
||||||
|
String url = initURLWithLeastConn();
|
||||||
|
PgConnection connection1 = getConnection(url, USER, PASSWORD);
|
||||||
|
String urlIdentifier = ConnectionManager.getURLIdentifierFromUrl(url);
|
||||||
|
Cluster cluster = ConnectionManager.getInstance().getCluster(urlIdentifier);
|
||||||
|
assertNotEquals(cluster, null);
|
||||||
|
ConnectionInfo connectionInfo = cluster.getConnectionInfo(connection1);
|
||||||
|
assertNotEquals(connectionInfo, null);
|
||||||
|
assertEquals(connectionInfo.getPgConnection(), connection1);
|
||||||
|
|
||||||
|
PgConnection connection2 = getConnection(url, USER, PASSWORD);
|
||||||
|
urlIdentifier = ConnectionManager.getURLIdentifierFromUrl(url);
|
||||||
|
cluster = ConnectionManager.getInstance().getCluster(urlIdentifier);
|
||||||
|
assertNotEquals(cluster, null);
|
||||||
|
connectionInfo = cluster.getConnectionInfo(connection2);
|
||||||
|
assertNotEquals(connectionInfo, null);
|
||||||
|
assertEquals(connectionInfo.getPgConnection(), connection2);
|
||||||
|
ConnectionManager.getInstance().clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void setConnectionStateTest() throws ClassNotFoundException, SQLException {
|
||||||
|
if (String.valueOf(TestUtil.getSecondaryPort()).equals(System.getProperty("def_pgport"))
|
||||||
|
|| String.valueOf(TestUtil.getSecondaryServerPort2()).equals(System.getProperty("def_pgport"))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Class.forName("org.postgresql.Driver");
|
||||||
|
String url = initURLWithLeastConn();
|
||||||
|
// set connection state which exists.
|
||||||
|
PgConnection connection = getConnection(url, USER, PASSWORD);
|
||||||
|
ConnectionManager.getInstance().setConnectionState(connection, StatementCancelState.IDLE);
|
||||||
|
String urlIdentifier = ConnectionManager.getURLIdentifierFromUrl(url);
|
||||||
|
ConnectionInfo connectionInfo = ConnectionManager.getInstance()
|
||||||
|
.getCluster(urlIdentifier).getConnectionInfo(connection);
|
||||||
|
assertEquals(connectionInfo.getConnectionState(), StatementCancelState.IDLE);
|
||||||
|
|
||||||
|
ConnectionManager.getInstance().setConnectionState(connection, StatementCancelState.IN_QUERY);
|
||||||
|
urlIdentifier = ConnectionManager.getURLIdentifierFromUrl(url);
|
||||||
|
connectionInfo = ConnectionManager.getInstance().getCluster(urlIdentifier).getConnectionInfo(connection);
|
||||||
|
assertEquals(connectionInfo.getConnectionState(), StatementCancelState.IN_QUERY);
|
||||||
|
|
||||||
|
ConnectionManager.getInstance().setConnectionState(connection, StatementCancelState.CANCELING);
|
||||||
|
urlIdentifier = ConnectionManager.getURLIdentifierFromUrl(url);
|
||||||
|
connectionInfo = ConnectionManager.getInstance().getCluster(urlIdentifier).getConnectionInfo(connection);
|
||||||
|
assertEquals(connectionInfo.getConnectionState(), StatementCancelState.CANCELING);
|
||||||
|
|
||||||
|
ConnectionManager.getInstance().setConnectionState(connection, StatementCancelState.CANCELLED);
|
||||||
|
urlIdentifier = ConnectionManager.getURLIdentifierFromUrl(url);
|
||||||
|
connectionInfo = ConnectionManager.getInstance().getCluster(urlIdentifier).getConnectionInfo(connection);
|
||||||
|
assertEquals(connectionInfo.getConnectionState(), StatementCancelState.CANCELLED);
|
||||||
|
ConnectionManager.getInstance().clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void leastConnTest() throws SQLException, ClassNotFoundException {
|
||||||
|
if (String.valueOf(TestUtil.getSecondaryPort()).equals(System.getProperty("def_pgport"))
|
||||||
|
|| String.valueOf(TestUtil.getSecondaryServerPort2()).equals(System.getProperty("def_pgport"))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Class.forName("org.postgresql.Driver");
|
||||||
|
String url = initURLWithLeastConn();
|
||||||
|
int num = 100;
|
||||||
|
HashMap<String, Integer> dns = new HashMap<>();
|
||||||
|
for (int i = 0; i < num; i++) {
|
||||||
|
PgConnection connection = getConnection(url, USER, PASSWORD);
|
||||||
|
String socketAddress = connection.getSocketAddress().split("/")[1];
|
||||||
|
dns.put(socketAddress, dns.getOrDefault(socketAddress, 0) + 1);
|
||||||
|
}
|
||||||
|
List<Integer> connectionCounts = new ArrayList<>();
|
||||||
|
for (Entry<String, Integer> entry : dns.entrySet()) {
|
||||||
|
connectionCounts.add(entry.getValue());
|
||||||
|
}
|
||||||
|
for (Integer connectionCount : connectionCounts) {
|
||||||
|
LOGGER.info(GT.tr("{0}", connectionCount));
|
||||||
|
}
|
||||||
|
for (int i = 0; i < connectionCounts.size() - 1; i++) {
|
||||||
|
assertTrue(Math.abs(connectionCounts.get(i) - connectionCounts.get(i + 1)) <= 1);
|
||||||
|
}
|
||||||
|
ConnectionManager.getInstance().clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void leastConnIntoOneDnTest() throws SQLException, ClassNotFoundException {
|
||||||
|
if (String.valueOf(TestUtil.getSecondaryPort()).equals(System.getProperty("def_pgport"))
|
||||||
|
|| String.valueOf(TestUtil.getSecondaryServerPort2()).equals(System.getProperty("def_pgport"))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Class.forName("org.postgresql.Driver");
|
||||||
|
String url1 = initURLWithLeastConn();
|
||||||
|
url1 += "&targetServerType=slave";
|
||||||
|
int num1 = 100;
|
||||||
|
HashMap<String, Integer> dns = new HashMap<>();
|
||||||
|
for (int i = 0; i < num1; i++) {
|
||||||
|
PgConnection connection = getConnection(url1, USER, PASSWORD);
|
||||||
|
String socketAddress = connection.getSocketAddress().split("/")[1];
|
||||||
|
dns.put(socketAddress, dns.getOrDefault(socketAddress, 0) + 1);
|
||||||
|
}
|
||||||
|
LOGGER.info(GT.tr("create 100 connections to slave nodes."));
|
||||||
|
for (Entry<String, Integer> entry : dns.entrySet()) {
|
||||||
|
int num = entry.getValue();
|
||||||
|
LOGGER.info(GT.tr("{0} : {1}", entry.getKey(), entry.getValue()));
|
||||||
|
assertEquals(50, num);
|
||||||
|
}
|
||||||
|
|
||||||
|
int num2 = 20;
|
||||||
|
String url2 = initURLWithLeastConn();
|
||||||
|
String lastAddress = "";
|
||||||
|
for (int i = 0; i < num2; i++) {
|
||||||
|
PgConnection connection = getConnection(url2, TestUtil.getUser(), TestUtil.getPassword());
|
||||||
|
String socketAddress = connection.getSocketAddress().split("/")[1];
|
||||||
|
dns.put(socketAddress, dns.getOrDefault(socketAddress, 0) + 1);
|
||||||
|
if (i > 1) {
|
||||||
|
assertEquals(lastAddress, socketAddress);
|
||||||
|
}
|
||||||
|
lastAddress = socketAddress;
|
||||||
|
}
|
||||||
|
LOGGER.info(GT.tr("Create 20 connections to all nodes."));
|
||||||
|
for (Entry<String, Integer> entry : dns.entrySet()) {
|
||||||
|
LOGGER.info(GT.tr("{0} : {1}", entry.getKey(), entry.getValue()));
|
||||||
|
}
|
||||||
|
ConnectionManager.getInstance().clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void checkConnectionStateTest() {
|
||||||
|
// If this testcase fail, maybe heartBeatingThread remove invalid connections.
|
||||||
|
try {
|
||||||
|
if (String.valueOf(TestUtil.getSecondaryPort()).equals(System.getProperty("def_pgport"))
|
||||||
|
|| String.valueOf(TestUtil.getSecondaryServerPort2()).equals(System.getProperty("def_pgport"))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Class.forName("org.postgresql.Driver");
|
||||||
|
String url = initURLWithLeastConn();
|
||||||
|
List<PgConnection> pgConnections = new ArrayList<>();
|
||||||
|
int total = 100;
|
||||||
|
int remove = 10;
|
||||||
|
for (int i = 0; i < total; i++) {
|
||||||
|
PgConnection connection = getConnection(url, USER, PASSWORD);
|
||||||
|
pgConnections.add(connection);
|
||||||
|
}
|
||||||
|
for (int i = 0; i < remove; i++) {
|
||||||
|
pgConnections.get(i).close();
|
||||||
|
}
|
||||||
|
assertEquals(Integer.valueOf(remove), ConnectionManager.getInstance().checkConnectionsValidity().get(0));
|
||||||
|
} catch (ClassNotFoundException | SQLException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
fail();
|
||||||
|
}
|
||||||
|
ConnectionManager.getInstance().clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void setClusterTest() throws PSQLException {
|
||||||
|
HostSpec[] hostSpecs = initHostSpec();
|
||||||
|
Properties properties = initPriority(hostSpecs);
|
||||||
|
assertFalse(ConnectionManager.getInstance().setCluster(properties));
|
||||||
|
assertFalse(ConnectionManager.getInstance().setCluster(null));
|
||||||
|
|
||||||
|
properties = new Properties();
|
||||||
|
properties.setProperty("PGDBNAME", TestUtil.getDatabase());
|
||||||
|
properties.setProperty("PGHOSTURL", TestUtil.getServer());
|
||||||
|
properties.setProperty("PGPORT", String.valueOf(TestUtil.getPort()));
|
||||||
|
properties.setProperty("PGPORTURL", String.valueOf(TestUtil.getPort()));
|
||||||
|
properties.setProperty("PGHOST", TestUtil.getServer());
|
||||||
|
properties.setProperty("user", TestUtil.getUser());
|
||||||
|
properties.setProperty("password", TestUtil.getPassword());
|
||||||
|
assertFalse(ConnectionManager.getInstance().setCluster(properties));
|
||||||
|
|
||||||
|
properties = initPriority(hostSpecs);
|
||||||
|
properties.setProperty("autoBalance", "leastconn");
|
||||||
|
assertTrue(ConnectionManager.getInstance().setCluster(properties));
|
||||||
|
assertFalse(ConnectionManager.getInstance().setCluster(properties));
|
||||||
|
ConnectionManager.getInstance().clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void incrementCachedCreatingConnectionSize() throws PSQLException {
|
||||||
|
HostSpec[] hostSpecs = initHostSpec();
|
||||||
|
Properties properties = initPriority(hostSpecs);
|
||||||
|
properties.setProperty("autoBalance", "leastconn");
|
||||||
|
ConnectionManager.getInstance().setCluster(properties);
|
||||||
|
assertEquals(1, ConnectionManager.getInstance().incrementCachedCreatingConnectionSize(hostSpecs[0], properties));
|
||||||
|
assertEquals(2, ConnectionManager.getInstance().incrementCachedCreatingConnectionSize(hostSpecs[0], properties));
|
||||||
|
assertEquals(1, ConnectionManager.getInstance().decrementCachedCreatingConnectionSize(hostSpecs[0], properties));
|
||||||
|
assertEquals(0, ConnectionManager.getInstance().decrementCachedCreatingConnectionSize(hostSpecs[0], properties));
|
||||||
|
assertEquals(0, ConnectionManager.getInstance().decrementCachedCreatingConnectionSize(hostSpecs[0], properties));
|
||||||
|
assertEquals(0, ConnectionManager.getInstance().decrementCachedCreatingConnectionSize(hostSpecs[0], null));
|
||||||
|
assertEquals(1, ConnectionManager.getInstance().incrementCachedCreatingConnectionSize(hostSpecs[0], properties));
|
||||||
|
assertEquals(2, ConnectionManager.getInstance().incrementCachedCreatingConnectionSize(hostSpecs[0], properties));
|
||||||
|
properties.setProperty("autoBalance", "");
|
||||||
|
assertEquals(0, ConnectionManager.getInstance().incrementCachedCreatingConnectionSize(hostSpecs[0], properties));
|
||||||
|
properties.setProperty("autoBalance", "leastconn");
|
||||||
|
assertEquals(0, ConnectionManager.getInstance()
|
||||||
|
.incrementCachedCreatingConnectionSize(new HostSpec("127.127.127.127", 93589), properties));
|
||||||
|
ConnectionManager.getInstance().clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void decrementCachedCreatingConnectionSize() throws PSQLException {
|
||||||
|
HostSpec[] hostSpecs = initHostSpec();
|
||||||
|
Properties properties = initPriority(hostSpecs);
|
||||||
|
properties.setProperty("autoBalance", "leastconn");
|
||||||
|
ConnectionManager.getInstance().setCluster(properties);
|
||||||
|
assertEquals(1, ConnectionManager.getInstance().incrementCachedCreatingConnectionSize(hostSpecs[0], properties));
|
||||||
|
assertEquals(2, ConnectionManager.getInstance().incrementCachedCreatingConnectionSize(hostSpecs[0], properties));
|
||||||
|
assertEquals(1, ConnectionManager.getInstance().decrementCachedCreatingConnectionSize(hostSpecs[0], properties));
|
||||||
|
assertEquals(0, ConnectionManager.getInstance().decrementCachedCreatingConnectionSize(hostSpecs[0], properties));
|
||||||
|
assertEquals(0, ConnectionManager.getInstance().decrementCachedCreatingConnectionSize(hostSpecs[0], properties));
|
||||||
|
assertEquals(0, ConnectionManager.getInstance().decrementCachedCreatingConnectionSize(hostSpecs[0], null));
|
||||||
|
assertEquals(1, ConnectionManager.getInstance().incrementCachedCreatingConnectionSize(hostSpecs[0], properties));
|
||||||
|
assertEquals(2, ConnectionManager.getInstance().incrementCachedCreatingConnectionSize(hostSpecs[0], properties));
|
||||||
|
properties.setProperty("autoBalance", "");
|
||||||
|
assertEquals(0, ConnectionManager.getInstance().decrementCachedCreatingConnectionSize(hostSpecs[0], properties));
|
||||||
|
properties.setProperty("autoBalance", "leastconn");
|
||||||
|
assertEquals(0, ConnectionManager.getInstance()
|
||||||
|
.decrementCachedCreatingConnectionSize(new HostSpec("127.127.127.127", 93589), properties));
|
||||||
|
ConnectionManager.getInstance().clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,489 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) Huawei Technologies Co., Ltd. 2023. All rights reserved.
|
||||||
|
*
|
||||||
|
* openGauss is licensed under Mulan PSL v2.
|
||||||
|
* You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||||
|
* You may obtain a copy of Mulan PSL v2 at:
|
||||||
|
*
|
||||||
|
* http://license.coscl.org.cn/MulanPSL2
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||||
|
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||||
|
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||||
|
* See the Mulan PSL v2 for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.postgresql.test.quickautobalance;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.postgresql.jdbc.PgConnection;
|
||||||
|
import org.postgresql.jdbc.StatementCancelState;
|
||||||
|
import org.postgresql.quickautobalance.ConnectionInfo;
|
||||||
|
import org.postgresql.quickautobalance.DataNode;
|
||||||
|
import org.postgresql.quickautobalance.DataNode.CheckDnStateResult;
|
||||||
|
import org.postgresql.test.TestUtil;
|
||||||
|
import org.postgresql.util.HostSpec;
|
||||||
|
import org.postgresql.util.PSQLException;
|
||||||
|
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.sql.Connection;
|
||||||
|
import java.sql.DriverManager;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Properties;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertFalse;
|
||||||
|
import static org.junit.Assert.assertNotEquals;
|
||||||
|
import static org.junit.Assert.assertNotNull;
|
||||||
|
import static org.junit.Assert.assertNull;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
import static org.junit.Assert.fail;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class DataNodeTest {
|
||||||
|
private static final int DN_NUM = 3;
|
||||||
|
|
||||||
|
private static final String FAKE_HOST = "127.127.217.217";
|
||||||
|
|
||||||
|
private static final String FAKE_PORT = "1";
|
||||||
|
|
||||||
|
private static final String FAKE_USER = "fakeuser";
|
||||||
|
|
||||||
|
private static final String FAKE_DATABASE = "fakedatabase";
|
||||||
|
|
||||||
|
private static final String FAKE_PASSWORD = "fakepassword";
|
||||||
|
|
||||||
|
private HostSpec initHost() {
|
||||||
|
return new HostSpec(TestUtil.getServer(), TestUtil.getPort());
|
||||||
|
}
|
||||||
|
|
||||||
|
private PgConnection getConnection(String url, Properties properties) throws SQLException {
|
||||||
|
Connection connection = DriverManager.getConnection(url, properties);
|
||||||
|
assertTrue(connection instanceof PgConnection);
|
||||||
|
return (PgConnection) connection;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Properties initProperties() {
|
||||||
|
Properties properties = new Properties();
|
||||||
|
properties.setProperty("PGDBNAME", TestUtil.getDatabase());
|
||||||
|
properties.setProperty("PGHOSTURL", TestUtil.getServer());
|
||||||
|
properties.setProperty("PGPORT", String.valueOf(TestUtil.getPort()));
|
||||||
|
properties.setProperty("PGPORTURL", String.valueOf(TestUtil.getPort()));
|
||||||
|
properties.setProperty("PGHOST", TestUtil.getServer());
|
||||||
|
properties.setProperty("user", TestUtil.getUser());
|
||||||
|
properties.setProperty("password", TestUtil.getPassword());
|
||||||
|
properties.setProperty("autoBalance", "leastconn");
|
||||||
|
return properties;
|
||||||
|
}
|
||||||
|
|
||||||
|
private HostSpec[] initHostSpecs() {
|
||||||
|
HostSpec[] hostSpecs = new HostSpec[DN_NUM];
|
||||||
|
hostSpecs[0] = new HostSpec(TestUtil.getServer(), TestUtil.getPort());
|
||||||
|
hostSpecs[1] = new HostSpec(TestUtil.getSecondaryServer(), TestUtil.getSecondaryPort());
|
||||||
|
hostSpecs[2] = new HostSpec(TestUtil.getSecondaryServer2(), TestUtil.getSecondaryServerPort2());
|
||||||
|
return hostSpecs;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean checkHostSpecs(HostSpec[] hostSpecs) {
|
||||||
|
if (hostSpecs.length != DN_NUM) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < DN_NUM; i++) {
|
||||||
|
if ("localhost".equals(hostSpecs[i].getHost())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void setConnectionStateTest() throws SQLException {
|
||||||
|
HostSpec hostSpec = initHost();
|
||||||
|
DataNode dataNode = new DataNode(hostSpec);
|
||||||
|
Properties properties = initProperties();
|
||||||
|
PgConnection pgConnection = getConnection(TestUtil.getURL(), properties);
|
||||||
|
dataNode.setConnection(pgConnection, properties, hostSpec);
|
||||||
|
|
||||||
|
dataNode.setConnectionState(pgConnection, StatementCancelState.IDLE);
|
||||||
|
assertEquals(StatementCancelState.IDLE, dataNode.getConnectionInfo(pgConnection).getConnectionState());
|
||||||
|
|
||||||
|
dataNode.setConnectionState(pgConnection, StatementCancelState.IN_QUERY);
|
||||||
|
assertEquals(StatementCancelState.IN_QUERY, dataNode.getConnectionInfo(pgConnection).getConnectionState());
|
||||||
|
|
||||||
|
dataNode.setConnectionState(pgConnection, StatementCancelState.CANCELLED);
|
||||||
|
assertEquals(StatementCancelState.CANCELLED, dataNode.getConnectionInfo(pgConnection).getConnectionState());
|
||||||
|
|
||||||
|
dataNode.setConnectionState(pgConnection, StatementCancelState.CANCELING);
|
||||||
|
assertEquals(StatementCancelState.CANCELING, dataNode.getConnectionInfo(pgConnection).getConnectionState());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void setConnectionTest() throws SQLException {
|
||||||
|
HostSpec hostSpec = initHost();
|
||||||
|
DataNode dataNode = new DataNode(hostSpec);
|
||||||
|
Properties properties = initProperties();
|
||||||
|
PgConnection pgConnection = getConnection(TestUtil.getURL(), properties);
|
||||||
|
dataNode.setConnection(pgConnection, properties, hostSpec);
|
||||||
|
ConnectionInfo connectionInfo = dataNode.getConnectionInfo(pgConnection);
|
||||||
|
assertNotNull(connectionInfo);
|
||||||
|
assertEquals(pgConnection, connectionInfo.getPgConnection());
|
||||||
|
|
||||||
|
dataNode.setConnection(null, properties, hostSpec);
|
||||||
|
dataNode.setConnection(pgConnection, null, hostSpec);
|
||||||
|
dataNode.setConnection(pgConnection, properties, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getConnectionTest() throws SQLException {
|
||||||
|
HostSpec hostSpec = initHost();
|
||||||
|
DataNode dataNode = new DataNode(hostSpec);
|
||||||
|
Properties properties = initProperties();
|
||||||
|
PgConnection pgConnection = getConnection(TestUtil.getURL(), properties);
|
||||||
|
dataNode.setConnection(pgConnection, properties, hostSpec);
|
||||||
|
ConnectionInfo connectionInfo = dataNode.getConnectionInfo(pgConnection);
|
||||||
|
assertNotNull(connectionInfo);
|
||||||
|
assertEquals(pgConnection, connectionInfo.getPgConnection());
|
||||||
|
|
||||||
|
connectionInfo = dataNode.getConnectionInfo(null);
|
||||||
|
assertNull(connectionInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getCachedConnectionListSizeTest() throws SQLException {
|
||||||
|
int num = 10;
|
||||||
|
HostSpec hostSpec = initHost();
|
||||||
|
DataNode dataNode = new DataNode(hostSpec);
|
||||||
|
Properties properties = initProperties();
|
||||||
|
assertEquals(0, dataNode.getCachedConnectionListSize());
|
||||||
|
for (int i = 0; i < num; i++) {
|
||||||
|
PgConnection pgConnection = getConnection(TestUtil.getURL(), properties);
|
||||||
|
dataNode.setConnection(pgConnection, properties, hostSpec);
|
||||||
|
}
|
||||||
|
int result = dataNode.getCachedConnectionListSize();
|
||||||
|
assertEquals(num, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void checkDnStateWithPropertiesSuccessTest() {
|
||||||
|
HostSpec hostSpec = new HostSpec(TestUtil.getServer(), TestUtil.getPort());
|
||||||
|
Properties properties = new Properties();
|
||||||
|
properties.setProperty("PGDBNAME", TestUtil.getDatabase());
|
||||||
|
properties.setProperty("PGHOSTURL", TestUtil.getServer());
|
||||||
|
properties.setProperty("PGPORT", String.valueOf(TestUtil.getPort()));
|
||||||
|
properties.setProperty("PGPORTURL", String.valueOf(TestUtil.getPort()));
|
||||||
|
properties.setProperty("PGHOST", TestUtil.getServer());
|
||||||
|
properties.setProperty("user", TestUtil.getUser());
|
||||||
|
properties.setProperty("password", TestUtil.getPassword());
|
||||||
|
DataNode dataNode = new DataNode(hostSpec);
|
||||||
|
try {
|
||||||
|
assertTrue(dataNode.checkDnState(properties));
|
||||||
|
} catch (InvocationTargetException | PSQLException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
fail();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void checkDnStateWithPropertiesUsernameOrPasswordErrorTest() {
|
||||||
|
HostSpec hostSpec = new HostSpec(TestUtil.getServer(), TestUtil.getPort());
|
||||||
|
Properties properties = new Properties();
|
||||||
|
properties.setProperty("PGDBNAME", TestUtil.getDatabase());
|
||||||
|
properties.setProperty("PGHOSTURL", TestUtil.getServer());
|
||||||
|
properties.setProperty("PGPORT", String.valueOf(TestUtil.getPort()));
|
||||||
|
properties.setProperty("PGPORTURL", String.valueOf(TestUtil.getPort()));
|
||||||
|
properties.setProperty("PGHOST", TestUtil.getServer());
|
||||||
|
properties.setProperty("user", FAKE_USER);
|
||||||
|
properties.setProperty("password", FAKE_PASSWORD);
|
||||||
|
DataNode dataNode = new DataNode(hostSpec);
|
||||||
|
try {
|
||||||
|
dataNode.checkDnState(properties);
|
||||||
|
} catch (InvocationTargetException e) {
|
||||||
|
assertTrue(e.getCause() instanceof PSQLException);
|
||||||
|
if (e.getCause() instanceof PSQLException) {
|
||||||
|
PSQLException psqlException = (PSQLException) (e.getCause());
|
||||||
|
String sqlState = psqlException.getSQLState();
|
||||||
|
assertEquals("28P01", sqlState);
|
||||||
|
} else {
|
||||||
|
e.printStackTrace();
|
||||||
|
fail();
|
||||||
|
}
|
||||||
|
} catch (PSQLException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
fail();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void checkDnStateWithPropertiesDatabaseErrorTest() {
|
||||||
|
// Invalid parameter "PGDBNAME" doesn't affect to tryConnect().
|
||||||
|
HostSpec hostSpec = new HostSpec(TestUtil.getServer(), TestUtil.getPort());
|
||||||
|
Properties properties = new Properties();
|
||||||
|
properties.setProperty("PGDBNAME", FAKE_DATABASE);
|
||||||
|
properties.setProperty("PGHOSTURL", TestUtil.getServer());
|
||||||
|
properties.setProperty("PGPORT", String.valueOf(TestUtil.getPort()));
|
||||||
|
properties.setProperty("PGPORTURL", String.valueOf(TestUtil.getPort()));
|
||||||
|
properties.setProperty("PGHOST", TestUtil.getServer());
|
||||||
|
properties.setProperty("user", TestUtil.getUser());
|
||||||
|
properties.setProperty("password", TestUtil.getPassword());
|
||||||
|
DataNode dataNode = new DataNode(hostSpec);
|
||||||
|
try {
|
||||||
|
assertTrue(dataNode.checkDnState(properties));
|
||||||
|
} catch (InvocationTargetException | PSQLException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
fail();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void checkDnStateWithPropertiesConnectionFailedTest() {
|
||||||
|
HostSpec hostSpec = new HostSpec(FAKE_HOST, Integer.parseInt(FAKE_PORT));
|
||||||
|
Properties properties = new Properties();
|
||||||
|
properties.setProperty("PGDBNAME", TestUtil.getDatabase());
|
||||||
|
properties.setProperty("PGHOSTURL", FAKE_HOST);
|
||||||
|
properties.setProperty("PGPORT", FAKE_PORT);
|
||||||
|
properties.setProperty("PGPORTURL", FAKE_PORT);
|
||||||
|
properties.setProperty("PGHOST", FAKE_HOST);
|
||||||
|
properties.setProperty("user", TestUtil.getUser());
|
||||||
|
properties.setProperty("password", TestUtil.getPassword());
|
||||||
|
DataNode dataNode = new DataNode(hostSpec);
|
||||||
|
try {
|
||||||
|
dataNode.checkDnState(properties);
|
||||||
|
} catch (InvocationTargetException e) {
|
||||||
|
if (e.getCause() instanceof PSQLException) {
|
||||||
|
PSQLException psqlException = (PSQLException) (e.getCause());
|
||||||
|
String SQLState = psqlException.getSQLState();
|
||||||
|
assertNotEquals("28P01", SQLState);
|
||||||
|
} else {
|
||||||
|
assertTrue(true);
|
||||||
|
}
|
||||||
|
} catch (PSQLException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
fail();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void checkDnStateWithHostSpecSuccessTest() {
|
||||||
|
HostSpec[] hostSpecs = initHostSpecs();
|
||||||
|
if (!checkHostSpecs(hostSpecs)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Arrays.sort(hostSpecs);
|
||||||
|
String pgHostUrl = hostSpecs[0].getHost() + "," + hostSpecs[1].getHost() + "," + hostSpecs[2].getHost();
|
||||||
|
String pgPortUrl = hostSpecs[0].getPort() + "," + hostSpecs[1].getPort() + "," + hostSpecs[2].getPort();
|
||||||
|
Properties clusterProperties = new Properties();
|
||||||
|
clusterProperties.setProperty("user", TestUtil.getUser());
|
||||||
|
clusterProperties.setProperty("password", TestUtil.getPassword());
|
||||||
|
clusterProperties.setProperty("PGDBNAME", TestUtil.getDatabase());
|
||||||
|
clusterProperties.setProperty("PGHOSTURL", pgHostUrl);
|
||||||
|
clusterProperties.setProperty("PGPORT", pgPortUrl);
|
||||||
|
clusterProperties.setProperty("PGPORTURL", pgPortUrl);
|
||||||
|
clusterProperties.setProperty("PGHOST", pgHostUrl);
|
||||||
|
DataNode dataNode = new DataNode(new HostSpec(TestUtil.getServer(), TestUtil.getPort()));
|
||||||
|
CheckDnStateResult result = dataNode.checkDnStateAndProperties(clusterProperties);
|
||||||
|
assertEquals(CheckDnStateResult.DN_VALID, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void checkDnStateWithHostSpecUserOrPasswordExpiredTest() {
|
||||||
|
HostSpec[] hostSpecs = initHostSpecs();
|
||||||
|
if (!checkHostSpecs(hostSpecs)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Arrays.sort(hostSpecs);
|
||||||
|
String pgHostUrl = hostSpecs[0].getHost() + "," + hostSpecs[1].getHost() + "," + hostSpecs[2].getHost();
|
||||||
|
String pgPortUrl = hostSpecs[0].getPort() + "," + hostSpecs[1].getPort() + "," + hostSpecs[2].getPort();
|
||||||
|
Properties invalidProperties = new Properties();
|
||||||
|
invalidProperties.setProperty("user", FAKE_USER);
|
||||||
|
invalidProperties.setProperty("password", FAKE_PASSWORD);
|
||||||
|
invalidProperties.setProperty("PGDBNAME", TestUtil.getDatabase());
|
||||||
|
invalidProperties.setProperty("PGHOSTURL", pgHostUrl);
|
||||||
|
invalidProperties.setProperty("PGPORT", pgPortUrl);
|
||||||
|
invalidProperties.setProperty("PGPORTURL", pgPortUrl);
|
||||||
|
invalidProperties.setProperty("PGHOST", pgHostUrl);
|
||||||
|
DataNode dataNode = new DataNode(new HostSpec(TestUtil.getServer(), TestUtil.getPort()));
|
||||||
|
CheckDnStateResult result = dataNode.checkDnStateAndProperties(invalidProperties);
|
||||||
|
assertEquals(CheckDnStateResult.PROPERTIES_INVALID, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void checkDnStateWithHostSpecConnectFailedTest() {
|
||||||
|
HostSpec[] hostSpecs = initHostSpecs();
|
||||||
|
HostSpec hostSpec = new HostSpec(FAKE_HOST, Integer.parseInt(FAKE_PORT));
|
||||||
|
if (!checkHostSpecs(hostSpecs)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Arrays.sort(hostSpecs);
|
||||||
|
Properties clusterProperties = new Properties();
|
||||||
|
clusterProperties.setProperty("user", TestUtil.getUser());
|
||||||
|
clusterProperties.setProperty("password", TestUtil.getPassword());
|
||||||
|
clusterProperties.setProperty("PGDBNAME", TestUtil.getDatabase());
|
||||||
|
clusterProperties.setProperty("PGHOSTURL", FAKE_HOST);
|
||||||
|
clusterProperties.setProperty("PGPORT", FAKE_PORT);
|
||||||
|
clusterProperties.setProperty("PGPORTURL", FAKE_PORT);
|
||||||
|
clusterProperties.setProperty("PGHOST", FAKE_HOST);
|
||||||
|
DataNode dataNode = new DataNode(hostSpec);
|
||||||
|
CheckDnStateResult result = dataNode.checkDnStateAndProperties(clusterProperties);
|
||||||
|
assertEquals(CheckDnStateResult.DN_INVALID, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void checkConnectionValidityTest() {
|
||||||
|
HostSpec hostSpec = initHost();
|
||||||
|
DataNode dataNode = new DataNode(hostSpec);
|
||||||
|
int total = 10;
|
||||||
|
int remove = 2;
|
||||||
|
List<PgConnection> pgConnections = new ArrayList<>();
|
||||||
|
Properties properties = initProperties();
|
||||||
|
try {
|
||||||
|
for (int i = 0; i < total; i++) {
|
||||||
|
PgConnection pgConnection = getConnection(TestUtil.getURL(), properties);
|
||||||
|
dataNode.setConnection(pgConnection, properties, hostSpec);
|
||||||
|
pgConnections.add(pgConnection);
|
||||||
|
}
|
||||||
|
assertEquals(0, dataNode.checkConnectionsValidity());
|
||||||
|
assertEquals(10, dataNode.getCachedConnectionListSize());
|
||||||
|
for (int i = 0; i < remove; i++) {
|
||||||
|
pgConnections.get(i).close();
|
||||||
|
}
|
||||||
|
assertEquals(2, dataNode.checkConnectionsValidity());
|
||||||
|
assertEquals(10 - 2, dataNode.getCachedConnectionListSize());
|
||||||
|
} catch (SQLException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
fail();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void filterIdleConnectionsTest() {
|
||||||
|
HostSpec hostSpec = initHost();
|
||||||
|
DataNode dataNode = new DataNode(hostSpec);
|
||||||
|
int idleSize = 10;
|
||||||
|
int querySize = 20;
|
||||||
|
Properties properties = initProperties();
|
||||||
|
properties.setProperty("enableQuickAutoBalance", "true");
|
||||||
|
properties.setProperty("maxIdleTimeBeforeTerminal", "1");
|
||||||
|
for (int i = 0; i < idleSize; i++) {
|
||||||
|
try {
|
||||||
|
PgConnection pgConnection = getConnection(TestUtil.getURL(), properties);
|
||||||
|
dataNode.setConnection(pgConnection, properties, hostSpec);
|
||||||
|
ConnectionInfo connectionInfo = dataNode.getConnectionInfo(pgConnection);
|
||||||
|
} catch (SQLException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
fail();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (int i = 0; i < querySize; i++) {
|
||||||
|
try {
|
||||||
|
PgConnection pgConnection = getConnection(TestUtil.getURL(), properties);
|
||||||
|
dataNode.setConnection(pgConnection, properties, hostSpec);
|
||||||
|
ConnectionInfo connectionInfo = dataNode.getConnectionInfo(pgConnection);
|
||||||
|
connectionInfo.setConnectionState(StatementCancelState.IN_QUERY);
|
||||||
|
} catch (SQLException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
fail();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
Thread.sleep(1000);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
fail();
|
||||||
|
}
|
||||||
|
List<ConnectionInfo> connectionInfos = dataNode.filterIdleConnections(System.currentTimeMillis());
|
||||||
|
assertEquals(idleSize, connectionInfos.size());
|
||||||
|
for (ConnectionInfo connectionInfo : connectionInfos) {
|
||||||
|
assertEquals(StatementCancelState.IDLE, connectionInfo.getConnectionState());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getAndSetDNStateTest() {
|
||||||
|
HostSpec hostSpec = initHost();
|
||||||
|
DataNode dataNode = new DataNode(hostSpec);
|
||||||
|
assertTrue(dataNode.getDataNodeState());
|
||||||
|
dataNode.setDataNodeState(false);
|
||||||
|
assertFalse(dataNode.getDataNodeState());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void clearCachedConnectionsTest() {
|
||||||
|
HostSpec hostSpec = initHost();
|
||||||
|
DataNode dataNode = new DataNode(hostSpec);
|
||||||
|
int idleSize = 10;
|
||||||
|
Properties properties = initProperties();
|
||||||
|
properties.setProperty("enableQuickAutoBalance", "true");
|
||||||
|
properties.setProperty("maxIdleTimeBeforeTerminal", "1");
|
||||||
|
for (int i = 0; i < idleSize; i++) {
|
||||||
|
try {
|
||||||
|
PgConnection pgConnection = getConnection(TestUtil.getURL(), properties);
|
||||||
|
dataNode.setConnection(pgConnection, properties, hostSpec);
|
||||||
|
ConnectionInfo connectionInfo = dataNode.getConnectionInfo(pgConnection);
|
||||||
|
} catch (SQLException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
fail();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assertEquals(idleSize, dataNode.getCachedConnectionListSize());
|
||||||
|
dataNode.clearCachedConnections();
|
||||||
|
assertEquals(0, dataNode.getCachedConnectionListSize());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void closeConnectionsTest() {
|
||||||
|
HostSpec hostSpec = initHost();
|
||||||
|
DataNode dataNode = new DataNode(hostSpec);
|
||||||
|
int total = 10;
|
||||||
|
int closed = 5;
|
||||||
|
Properties properties = initProperties();
|
||||||
|
List<PgConnection> connectionList = new ArrayList<>();
|
||||||
|
for (int i = 0; i < total; i++) {
|
||||||
|
try {
|
||||||
|
PgConnection pgConnection = getConnection(TestUtil.getURL(), properties);
|
||||||
|
dataNode.setConnection(pgConnection, properties, hostSpec);
|
||||||
|
connectionList.add(pgConnection);
|
||||||
|
} catch (SQLException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
fail();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assertEquals(total, dataNode.getCachedConnectionListSize());
|
||||||
|
for (int i = 0; i < total; i++) {
|
||||||
|
try {
|
||||||
|
assertTrue(connectionList.get(i).isValid(4));
|
||||||
|
} catch (SQLException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
fail();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (int i = 0; i < closed; i++) {
|
||||||
|
assertTrue(dataNode.closeConnection(connectionList.get(i)));
|
||||||
|
}
|
||||||
|
assertEquals(total - closed, dataNode.getCachedConnectionListSize());
|
||||||
|
for (int i = 0; i < closed; i++) {
|
||||||
|
try {
|
||||||
|
assertFalse(connectionList.get(i).isValid(4));
|
||||||
|
} catch (SQLException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
fail();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getAndSetCachedCreatingConnectionSizeTest() {
|
||||||
|
HostSpec hostSpec = initHost();
|
||||||
|
DataNode dataNode = new DataNode(hostSpec);
|
||||||
|
assertEquals(0, dataNode.getCachedCreatingConnectionSize());
|
||||||
|
assertEquals(1, dataNode.incrementCachedCreatingConnectionSize());
|
||||||
|
assertEquals(1, dataNode.getCachedCreatingConnectionSize());
|
||||||
|
assertEquals(0, dataNode.decrementCachedCreatingConnectionSize());
|
||||||
|
assertEquals(0, dataNode.getCachedCreatingConnectionSize());
|
||||||
|
assertEquals(0, dataNode.decrementCachedCreatingConnectionSize());
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,105 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) Huawei Technologies Co., Ltd. 2023. All rights reserved.
|
||||||
|
*
|
||||||
|
* openGauss is licensed under Mulan PSL v2.
|
||||||
|
* You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||||
|
* You may obtain a copy of Mulan PSL v2 at:
|
||||||
|
*
|
||||||
|
* http://license.coscl.org.cn/MulanPSL2
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||||
|
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||||
|
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||||
|
* See the Mulan PSL v2 for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.postgresql.test.quickautobalance;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.postgresql.quickautobalance.LoadBalanceHeartBeating;
|
||||||
|
import org.postgresql.test.TestUtil;
|
||||||
|
|
||||||
|
import java.util.Properties;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertFalse;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* LoadBalanceHeartBeatingTest
|
||||||
|
*/
|
||||||
|
public class LoadBalanceHeartBeatingTest {
|
||||||
|
private Properties initProperties() {
|
||||||
|
Properties properties = new Properties();
|
||||||
|
properties.setProperty("PGPORTURL", TestUtil.getPort() + ","
|
||||||
|
+ TestUtil.getSecondaryPort() + "," + TestUtil.getSecondaryServerPort2());
|
||||||
|
properties.setProperty("PGHOSTURL", TestUtil.getServer() + ","
|
||||||
|
+ TestUtil.getSecondaryServer() + "," + TestUtil.getSecondaryServer2());
|
||||||
|
return properties;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void startCheckConnectionScheduledExecutorServiceSuccessTest() {
|
||||||
|
Properties properties = initProperties();
|
||||||
|
properties.setProperty("autoBalance", "leastconn");
|
||||||
|
assertFalse(LoadBalanceHeartBeating.isLeastConnStarted());
|
||||||
|
assertFalse(LoadBalanceHeartBeating.isQuickAutoBalanceStarted());
|
||||||
|
LoadBalanceHeartBeating.startScheduledExecutorService(properties);
|
||||||
|
assertTrue(LoadBalanceHeartBeating.isLeastConnStarted());
|
||||||
|
assertFalse(LoadBalanceHeartBeating.isQuickAutoBalanceStarted());
|
||||||
|
LoadBalanceHeartBeating.startScheduledExecutorService(properties);
|
||||||
|
assertTrue(LoadBalanceHeartBeating.isLeastConnStarted());
|
||||||
|
assertFalse(LoadBalanceHeartBeating.isQuickAutoBalanceStarted());
|
||||||
|
LoadBalanceHeartBeating.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void startCloseConnectionExecutorServiceSuccessTest() {
|
||||||
|
Properties properties = initProperties();
|
||||||
|
properties.setProperty("autoBalance", "leastconn");
|
||||||
|
properties.setProperty("enableQuickAutoBalance", "true");
|
||||||
|
assertFalse(LoadBalanceHeartBeating.isLeastConnStarted());
|
||||||
|
assertFalse(LoadBalanceHeartBeating.isQuickAutoBalanceStarted());
|
||||||
|
LoadBalanceHeartBeating.startScheduledExecutorService(properties);
|
||||||
|
assertTrue(LoadBalanceHeartBeating.isLeastConnStarted());
|
||||||
|
assertTrue(LoadBalanceHeartBeating.isQuickAutoBalanceStarted());
|
||||||
|
LoadBalanceHeartBeating.startScheduledExecutorService(properties);
|
||||||
|
assertTrue(LoadBalanceHeartBeating.isLeastConnStarted());
|
||||||
|
assertTrue(LoadBalanceHeartBeating.isQuickAutoBalanceStarted());
|
||||||
|
LoadBalanceHeartBeating.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void startCloseConnectionExecutorServiceFailedTest() {
|
||||||
|
Properties properties = initProperties();
|
||||||
|
properties.setProperty("autoBalance", "leastconn");
|
||||||
|
LoadBalanceHeartBeating.startScheduledExecutorService(properties);
|
||||||
|
assertTrue(LoadBalanceHeartBeating.isLeastConnStarted());
|
||||||
|
assertFalse(LoadBalanceHeartBeating.isQuickAutoBalanceStarted());
|
||||||
|
properties.setProperty("enableQuickAutoBalance", "fsfsfs");
|
||||||
|
LoadBalanceHeartBeating.startScheduledExecutorService(properties);
|
||||||
|
assertTrue(LoadBalanceHeartBeating.isLeastConnStarted());
|
||||||
|
assertFalse(LoadBalanceHeartBeating.isQuickAutoBalanceStarted());
|
||||||
|
properties.setProperty("enableQuickAutoBalance", "false");
|
||||||
|
LoadBalanceHeartBeating.startScheduledExecutorService(properties);
|
||||||
|
assertTrue(LoadBalanceHeartBeating.isLeastConnStarted());
|
||||||
|
assertFalse(LoadBalanceHeartBeating.isQuickAutoBalanceStarted());
|
||||||
|
LoadBalanceHeartBeating.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void startExecutorServiceWithSingleHostTest() {
|
||||||
|
Properties properties = new Properties();
|
||||||
|
properties.setProperty("PGPORTURL", String.valueOf(TestUtil.getPort()));
|
||||||
|
properties.setProperty("PGHOSTURL", TestUtil.getServer());
|
||||||
|
properties.setProperty("autoBalance", "leastconn");
|
||||||
|
properties.setProperty("enableQuickAutoBalance", "true");
|
||||||
|
assertFalse(LoadBalanceHeartBeating.isLeastConnStarted());
|
||||||
|
assertFalse(LoadBalanceHeartBeating.isQuickAutoBalanceStarted());
|
||||||
|
LoadBalanceHeartBeating.startScheduledExecutorService(properties);
|
||||||
|
assertFalse(LoadBalanceHeartBeating.isLeastConnStarted());
|
||||||
|
assertFalse(LoadBalanceHeartBeating.isQuickAutoBalanceStarted());
|
||||||
|
LoadBalanceHeartBeating.startScheduledExecutorService(properties);
|
||||||
|
assertFalse(LoadBalanceHeartBeating.isLeastConnStarted());
|
||||||
|
assertFalse(LoadBalanceHeartBeating.isQuickAutoBalanceStarted());
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user