org.postgresql.clusterchooser.GlobalClusterStatusTracker Maven / Gradle / Ivy
Show all versions of mogdb-jdbc Show documentation
package io.mogdb.clusterchooser;
import io.mogdb.Driver;
import io.mogdb.PGProperty;
import io.mogdb.QueryCNListUtils;
import io.mogdb.hostchooser.MultiHostChooser;
import io.mogdb.log.Log;
import io.mogdb.log.Logger;
import io.mogdb.util.ClusterSpec;
import io.mogdb.util.HostSpec;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
/**
* Keeps track of HostSpec targets in a global map.
*/
public class GlobalClusterStatusTracker {
private static final Map clusterStatusMap = new HashMap<>();
private static final Map firstConnectionMap = new ConcurrentHashMap<>();
private static Map masterClusterList = new HashMap<>();
private static Log LOGGER = Logger.getLogger(GlobalClusterStatusTracker.class.getName());
/**
* Store the actual observed cluster status.
*
* @param clusterSpec The cluster whose status is known.
* @param clusterStatus Latest known status for the cluster.
*/
public static void reportClusterStatus(ClusterSpec clusterSpec, ClusterStatus clusterStatus) {
String key = keyFromClusterSpec(clusterSpec);
synchronized (clusterStatusMap) {
ClusterSpecStatus clusterSpecStatus = clusterStatusMap.get(key);
if (clusterSpecStatus == null) {
clusterSpecStatus = new ClusterSpecStatus(clusterSpec);
clusterStatusMap.put(key, clusterSpecStatus);
}
clusterSpecStatus.status = clusterStatus;
}
}
/**
* The actual primary cluster in the storage cluster.
*
* @param props
* @param clusterSpec
*/
public static void reportMasterCluster(Properties props, ClusterSpec clusterSpec) {
String urlKey = QueryCNListUtils.keyFromURL(props);
String masterClusterKey = keyFromClusterSpec(clusterSpec);
synchronized (masterClusterList) {
masterClusterList.put(urlKey, masterClusterKey);
}
}
/**
* @param hostSpecs
* @return
*/
public static ClusterStatus getClusterStatus(HostSpec[] hostSpecs) {
HostSpec[] cloneHostSpecs = hostSpecs.clone();
Arrays.sort(cloneHostSpecs);
String clusterKey = Arrays.toString(cloneHostSpecs);
synchronized (clusterStatusMap) {
ClusterSpecStatus clusterSpecStatus = clusterStatusMap.get(clusterKey);
if (clusterSpecStatus != null && clusterSpecStatus.status != null) {
return clusterSpecStatus.status;
}
}
return ClusterStatus.Unknown;
}
/**
* Submit the cluster information in props.
* And try to get the information of the master cluster from the refreshed results.
*
* @param props Connection properties.
*/
public static void refreshProperties(Properties props) {
String key = QueryCNListUtils.keyFromURL(props);
// The value is false only when it is obtained for the first time.
boolean block;
synchronized (firstConnectionMap) {
block = firstConnectionMap.getOrDefault(key, false);
firstConnectionMap.put(key, true);
}
String masterClusterkey = getMasterClusterkey(key, block);
if ("".equals(masterClusterkey)) {
return;
}
LOGGER.info("[PRIORITYSERVERS] Find the main cluster in dual clusters." +
" | DualCluster: " + key +
" | MasterCluster:" + masterClusterkey
);
// The result is updated to props.
props.setProperty("MASTERCLUSTER", masterClusterkey);
}
/**
* If block is set to true, waiting will be prevented if the master cluster cannot be found.
*
* @param key Key in the connection string.
* @param block Concurrency lock.
* @return
*/
public static String getMasterClusterkey(String key, boolean block) {
int intervalWaitHasRefreshedCNList = 10;
int timesWaitHasRefreshedCNList = 200;
for (int i = 0; i <= timesWaitHasRefreshedCNList; i++) {
synchronized (masterClusterList) {
String masterClusterKey = masterClusterList.get(key);
if (masterClusterKey != null && !"".equals(masterClusterKey)) {
return masterClusterKey;
}
}
if (!block) break;
try {
Thread.sleep(intervalWaitHasRefreshedCNList);
} catch (InterruptedException e) {
LOGGER.info("[PRIORITYSERVERS] InterruptedException. This caused by: \"Thread.sleep\", waiting for refreshing master cluster from connection.");
}
}
if (block) {
LOGGER.info("[PRIORITYSERVERS] Blocking time extends 2 seconds need to pay attention.");
}
return "";
}
/**
* Determine whether the configuration to support disaster recovery switching is effective.
* if the disaster tolerance switch function is used, the value of this parameter should be a number,
* and the value should be greater than 0 and less than the number of nodes in the connection string.
*
* Return true if the above conditions are met, otherwise, return false.
*
* @param props Connection properties
* @return
*/
public static boolean isVaildPriorityServers(Properties props) {
String priorityServers = PGProperty.PRIORITY_SERVERS.get(props);
try {
int priorityServersNumber = Integer.parseInt(priorityServers);
int lengthPGPORTURL = props.getProperty("PGPORTURL").split(",").length;
if (lengthPGPORTURL <= priorityServersNumber || priorityServersNumber <= 0) {
LOGGER.warn("When configuring priority servers, The number of priority nodes should be less than the number of nodes on the URL and greater than 0.");
return false;
}
} catch (NumberFormatException e) {
LOGGER.warn("When configuring priority servers, \"priorityServers\" should be number.");
return false;
}
return true;
}
/**
* Split the clusters in the url, get the master cluster first when there are dual clusters.
*
* @param hostSpecs Specification information of all hosts in the cluster.
* @param info the parsed/defaulted connection properties
* @return
*/
public static Iterator getClusterFromHostSpecs(HostSpec[] hostSpecs, Properties info) {
String priorityServers = PGProperty.PRIORITY_SERVERS.get(info);
ClusterSpec[] clusterSpecs;
if (priorityServers != null) {
clusterSpecs = new ClusterSpec[2];
//cluster demarcation index.
Integer index = Integer.valueOf(priorityServers);
//known master cluster.
String masterCluster = info.getProperty("MASTERCLUSTER");
//When load balancing, use the real queried cluster information.
if (MultiHostChooser.isUsingAutoLoadBalance(info) && masterCluster != null) {
clusterSpecs[0] = new ClusterSpec(hostSpecs);
HostSpec[] urlHostSpecs = Driver.getURLHostSpecs(info);
HostSpec[] slaveHostSpecs = Arrays.copyOfRange(urlHostSpecs, index, urlHostSpecs.length);
if(!masterCluster.contains(slaveHostSpecs[0].toString())){
clusterSpecs[1] = new ClusterSpec(slaveHostSpecs);
}else{
clusterSpecs[1] = new ClusterSpec(Arrays.copyOfRange(urlHostSpecs, 0, index));
}
} else {
HostSpec[] masterHostSpecs = Arrays.copyOfRange(hostSpecs, 0, index);
HostSpec[] slaveHostSpecs = Arrays.copyOfRange(hostSpecs, index, hostSpecs.length);
//Prioritize the known master cluster.
if (masterCluster != null && masterCluster.contains(slaveHostSpecs[0].toString())) {
clusterSpecs[0] = new ClusterSpec(slaveHostSpecs);
clusterSpecs[1] = new ClusterSpec(masterHostSpecs);
} else {
clusterSpecs[0] = new ClusterSpec(masterHostSpecs);
clusterSpecs[1] = new ClusterSpec(slaveHostSpecs);
}
}
} else {
clusterSpecs = new ClusterSpec[1];
clusterSpecs[0] = new ClusterSpec(hostSpecs);
}
return new ArrayList<>(Arrays.asList(clusterSpecs)).iterator();
}
/**
* Returns a key representing the cluster based on clusterSpec.
*
* @param clusterSpec Nodes in the cluster.
* @return Key representing the cluster.
*/
public static String keyFromClusterSpec(ClusterSpec clusterSpec) {
HostSpec[] hostSpecs = clusterSpec.getHostSpecs();
Arrays.sort(hostSpecs);
return Arrays.toString(hostSpecs);
}
static class ClusterSpecStatus {
final ClusterSpec cluster;
ClusterStatus status;
ClusterSpecStatus(ClusterSpec cluster) {
this.cluster = cluster;
}
@Override
public String toString() {
return cluster.toString() + '=' + status;
}
}
}