Skip to content

Commit 67fc2f5

Browse files
Damodar ReddyAbhinandan Prateek
authored andcommitted
CLOUDSTACK-4916: Fixing the issue with DB HA when there are more than 2 db nodes (Issue was mysql DriverManager was returning salve db nodes on random basis which was causing issue)
Signed-off-by: Abhinandan Prateek <aprateek@apache.org>
1 parent 74153e4 commit 67fc2f5

4 files changed

Lines changed: 140 additions & 4 deletions

File tree

client/tomcatconf/db.properties.in

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ db.simulator.autoReconnect=true
8585

8686
# High Availability And Cluster Properties
8787
db.ha.enabled=false
88+
db.ha.loadBalanceStrategy=com.cloud.utils.db.StaticStrategy
8889
# cloud stack Database
8990
db.cloud.slaves=localhost,localhost
9091
db.cloud.autoReconnect=true

framework/db/pom.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,11 @@
4444
<artifactId>cloud-utils</artifactId>
4545
<version>${project.version}</version>
4646
</dependency>
47+
<dependency>
48+
<groupId>mysql</groupId>
49+
<artifactId>mysql-connector-java</artifactId>
50+
<scope>compile</scope>
51+
</dependency>
4752
</dependencies>
4853
<build>
4954
<plugins>
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
// Licensed to the Apache Software Foundation (ASF) under one
2+
// or more contributor license agreements. See the NOTICE file
3+
// distributed with this work for additional information
4+
// regarding copyright ownership. The ASF licenses this file
5+
// to you under the Apache License, Version 2.0 (the
6+
// "License"); you may not use this file except in compliance
7+
// the License. You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing,
12+
// software distributed under the License is distributed on an
13+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
// KIND, either express or implied. See the License for the
15+
// specific language governing permissions and limitations
16+
// under the License.
17+
package com.cloud.utils.db;
18+
19+
import java.sql.SQLException;
20+
import java.util.ArrayList;
21+
import java.util.HashMap;
22+
import java.util.List;
23+
import java.util.Map;
24+
import java.util.Properties;
25+
26+
import com.mysql.jdbc.BalanceStrategy;
27+
import com.mysql.jdbc.Connection;
28+
import com.mysql.jdbc.ConnectionImpl;
29+
import com.mysql.jdbc.LoadBalancingConnectionProxy;
30+
import com.mysql.jdbc.SQLError;
31+
32+
public class StaticStrategy implements BalanceStrategy {
33+
34+
public StaticStrategy() {
35+
}
36+
37+
public void destroy() {
38+
// we don't have anything to clean up
39+
}
40+
41+
public void init(Connection conn, Properties props) throws SQLException {
42+
// we don't have anything to initialize
43+
}
44+
45+
public ConnectionImpl pickConnection(LoadBalancingConnectionProxy proxy,
46+
List<String> configuredHosts, Map<String, ConnectionImpl> liveConnections, long[] responseTimes,
47+
int numRetries) throws SQLException {
48+
int numHosts = configuredHosts.size();
49+
50+
SQLException ex = null;
51+
52+
List<String> whiteList = new ArrayList<String>(numHosts);
53+
whiteList.addAll(configuredHosts);
54+
55+
Map<String, Long> blackList = proxy.getGlobalBlacklist();
56+
57+
whiteList.removeAll(blackList.keySet());
58+
59+
Map<String, Integer> whiteListMap = this.getArrayIndexMap(whiteList);
60+
61+
62+
for (int attempts = 0; attempts < numRetries;) {
63+
if(whiteList.size() == 0){
64+
throw SQLError.createSQLException("No hosts configured", null);
65+
}
66+
67+
String hostPortSpec = whiteList.get(0); //Always take the first host
68+
69+
ConnectionImpl conn = liveConnections.get(hostPortSpec);
70+
71+
if (conn == null) {
72+
try {
73+
conn = proxy.createConnectionForHost(hostPortSpec);
74+
} catch (SQLException sqlEx) {
75+
ex = sqlEx;
76+
77+
if (proxy.shouldExceptionTriggerFailover(sqlEx)) {
78+
79+
Integer whiteListIndex = whiteListMap.get(hostPortSpec);
80+
81+
// exclude this host from being picked again
82+
if (whiteListIndex != null) {
83+
whiteList.remove(whiteListIndex.intValue());
84+
whiteListMap = this.getArrayIndexMap(whiteList);
85+
}
86+
proxy.addToGlobalBlacklist( hostPortSpec );
87+
88+
if (whiteList.size() == 0) {
89+
attempts++;
90+
try {
91+
Thread.sleep(250);
92+
} catch (InterruptedException e) {
93+
}
94+
95+
// start fresh
96+
whiteListMap = new HashMap<String, Integer>(numHosts);
97+
whiteList.addAll(configuredHosts);
98+
blackList = proxy.getGlobalBlacklist();
99+
100+
whiteList.removeAll(blackList.keySet());
101+
whiteListMap = this.getArrayIndexMap(whiteList);
102+
}
103+
104+
continue;
105+
}
106+
107+
throw sqlEx;
108+
}
109+
}
110+
111+
return conn;
112+
}
113+
114+
if (ex != null) {
115+
throw ex;
116+
}
117+
118+
return null; // we won't get here, compiler can't tell
119+
}
120+
121+
private Map<String, Integer> getArrayIndexMap(List<String> l) {
122+
Map<String, Integer> m = new HashMap<String, Integer>(l.size());
123+
for (int i = 0; i < l.size(); i++) {
124+
m.put(l.get(i), Integer.valueOf(i));
125+
}
126+
return m;
127+
128+
}
129+
130+
}

framework/db/src/com/cloud/utils/db/TransactionLegacy.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1034,7 +1034,7 @@ public static void initDataSource(Properties dbProps) {
10341034

10351035
s_dbHAEnabled = Boolean.valueOf(dbProps.getProperty("db.ha.enabled"));
10361036
s_logger.info("Is Data Base High Availiability enabled? Ans : " + s_dbHAEnabled);
1037-
1037+
String loadBalanceStrategy = dbProps.getProperty("db.ha.loadBalanceStrategy");
10381038
// FIXME: If params are missing...default them????
10391039
final int cloudMaxActive = Integer.parseInt(dbProps.getProperty("db.cloud.maxActive"));
10401040
final int cloudMaxIdle = Integer.parseInt(dbProps.getProperty("db.cloud.maxIdle"));
@@ -1090,7 +1090,7 @@ public static void initDataSource(Properties dbProps) {
10901090
cloudMaxWait, cloudMaxIdle, cloudTestOnBorrow, false, cloudTimeBtwEvictionRunsMillis, 1, cloudMinEvcitableIdleTimeMillis, cloudTestWhileIdle);
10911091

10921092
final ConnectionFactory cloudConnectionFactory = new DriverManagerConnectionFactory("jdbc:mysql://" + cloudHost + (s_dbHAEnabled ? "," + cloudSlaves : "") + ":" + cloudPort + "/" + cloudDbName +
1093-
"?autoReconnect=" + cloudAutoReconnect + (url != null ? "&" + url : "") + (useSSL ? "&useSSL=true" : "") + (s_dbHAEnabled ? "&" + cloudDbHAParams : ""), cloudUsername, cloudPassword);
1093+
"?autoReconnect=" + cloudAutoReconnect + (url != null ? "&" + url : "") + (useSSL ? "&useSSL=true" : "") + (s_dbHAEnabled ? "&" + cloudDbHAParams : "") + (s_dbHAEnabled ? "&loadBalanceStrategy=" + loadBalanceStrategy : ""), cloudUsername, cloudPassword);
10941094

10951095
final KeyedObjectPoolFactory poolableObjFactory = (cloudPoolPreparedStatements ? new StackKeyedObjectPoolFactory() : null);
10961096

@@ -1116,7 +1116,7 @@ public static void initDataSource(Properties dbProps) {
11161116
usageMaxWait, usageMaxIdle);
11171117

11181118
final ConnectionFactory usageConnectionFactory = new DriverManagerConnectionFactory("jdbc:mysql://" + usageHost + (s_dbHAEnabled ? "," + dbProps.getProperty("db.cloud.slaves") : "") + ":" + usagePort + "/" + usageDbName +
1119-
"?autoReconnect=" + usageAutoReconnect + (usageUrl != null ? "&" + usageUrl : "") + (s_dbHAEnabled ? "&" + getDBHAParams("usage", dbProps) : ""), usageUsername, usagePassword);
1119+
"?autoReconnect=" + usageAutoReconnect + (usageUrl != null ? "&" + usageUrl : "") + (s_dbHAEnabled ? "&" + getDBHAParams("usage", dbProps) : "") + (s_dbHAEnabled ? "&loadBalanceStrategy=" + loadBalanceStrategy : ""), usageUsername, usagePassword);
11201120

11211121
final PoolableConnectionFactory usagePoolableConnectionFactory = new PoolableConnectionFactory(usageConnectionFactory, usageConnectionPool,
11221122
new StackKeyedObjectPoolFactory(), null, false, false);
@@ -1129,7 +1129,7 @@ public static void initDataSource(Properties dbProps) {
11291129
final GenericObjectPool awsapiConnectionPool = new GenericObjectPool(null, usageMaxActive, GenericObjectPool.DEFAULT_WHEN_EXHAUSTED_ACTION,
11301130
usageMaxWait, usageMaxIdle);
11311131
final ConnectionFactory awsapiConnectionFactory = new DriverManagerConnectionFactory("jdbc:mysql://" + cloudHost + (s_dbHAEnabled ? "," + cloudSlaves : "") + ":" + cloudPort + "/" + awsapiDbName +
1132-
"?autoReconnect=" + cloudAutoReconnect + (s_dbHAEnabled ? "&" + cloudDbHAParams : ""), cloudUsername, cloudPassword);
1132+
"?autoReconnect=" + cloudAutoReconnect + (s_dbHAEnabled ? "&" + cloudDbHAParams : "") + (s_dbHAEnabled ? "&loadBalanceStrategy=" + loadBalanceStrategy : ""), cloudUsername, cloudPassword);
11331133
final PoolableConnectionFactory awsapiPoolableConnectionFactory = new PoolableConnectionFactory(awsapiConnectionFactory, awsapiConnectionPool,
11341134
new StackKeyedObjectPoolFactory(), null, false, false);
11351135

0 commit comments

Comments
 (0)