Skip to content

Commit 1d2bfae

Browse files
server: make HikariCP leak detection configurable
1 parent 348ce95 commit 1d2bfae

3 files changed

Lines changed: 79 additions & 5 deletions

File tree

client/conf/db.properties.in

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,11 @@ db.cloud.maxWait=600000
4141
db.cloud.minIdleConnections=5
4242
db.cloud.connectionTimeout=30000
4343
db.cloud.keepAliveTime=600000
44+
# HikariCP leak detection threshold in milliseconds. A value of 0 (or unset) disables leak detection.
45+
# Useful for debugging borrowed DB connections that are not returned to the pool. HikariCP ignores values below 2000.
46+
# db.cloud.leakDetectionThreshold=0
47+
# Enable HikariCP JMX MBeans for observing pool counters. Disabled by default.
48+
# db.cloud.registerMbeans=false
4449
db.cloud.validationQuery=/* ping */ SELECT 1
4550
db.cloud.testOnBorrow=true
4651
db.cloud.testWhileIdle=true

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

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
import org.apache.commons.dbcp2.PoolableConnection;
3939
import org.apache.commons.dbcp2.PoolableConnectionFactory;
4040
import org.apache.commons.dbcp2.PoolingDataSource;
41+
import org.apache.commons.lang3.BooleanUtils;
4142
import org.apache.commons.lang3.ObjectUtils;
4243
import org.apache.commons.lang3.StringUtils;
4344
import org.apache.commons.pool2.ObjectPool;
@@ -1065,6 +1066,8 @@ public static void initDataSource(Properties dbProps) {
10651066
final Integer cloudMinIdleConnections = parseNumber(dbProps.getProperty("db.cloud.minIdleConnections"), Integer.class);
10661067
final Long cloudConnectionTimeout = parseNumber(dbProps.getProperty("db.cloud.connectionTimeout"), Long.class);
10671068
final Long cloudKeepAliveTimeout = parseNumber(dbProps.getProperty("db.cloud.keepAliveTime"), Long.class);
1069+
final Long cloudLeakDetectionThreshold = parseNumber(dbProps.getProperty("db.cloud.leakDetectionThreshold"), Long.class);
1070+
final Boolean cloudRegisterMbeans = BooleanUtils.toBooleanObject(dbProps.getProperty("db.cloud.registerMbeans"));
10681071
final String cloudUsername = dbProps.getProperty("db.cloud.username");
10691072
final String cloudPassword = dbProps.getProperty("db.cloud.password");
10701073
final String cloudValidationQuery = dbProps.getProperty("db.cloud.validationQuery");
@@ -1107,7 +1110,7 @@ public static void initDataSource(Properties dbProps) {
11071110
cloudUsername, cloudPassword, cloudMaxActive, cloudMaxIdle, cloudMaxWait,
11081111
cloudTimeBtwEvictionRunsMillis, cloudMinEvcitableIdleTimeMillis, cloudTestWhileIdle,
11091112
cloudTestOnBorrow, cloudValidationQuery, cloudMinIdleConnections, cloudConnectionTimeout,
1110-
cloudKeepAliveTimeout, isolationLevel, "cloud");
1113+
cloudKeepAliveTimeout, cloudLeakDetectionThreshold, cloudRegisterMbeans, isolationLevel, "cloud");
11111114

11121115
// Configure the usage db
11131116
final Integer usageMaxActive = parseNumber(dbProps.getProperty("db.usage.maxActive"), Integer.class);
@@ -1116,6 +1119,8 @@ public static void initDataSource(Properties dbProps) {
11161119
final Integer usageMinIdleConnections = parseNumber(dbProps.getProperty("db.usage.minIdleConnections"), Integer.class);
11171120
final Long usageConnectionTimeout = parseNumber(dbProps.getProperty("db.usage.connectionTimeout"), Long.class);
11181121
final Long usageKeepAliveTimeout = parseNumber(dbProps.getProperty("db.usage.keepAliveTime"), Long.class);
1122+
final Long usageLeakDetectionThreshold = parseNumber(dbProps.getProperty("db.usage.leakDetectionThreshold"), Long.class);
1123+
final Boolean usageRegisterMbeans = BooleanUtils.toBooleanObject(dbProps.getProperty("db.usage.registerMbeans"));
11191124
final String usageUsername = dbProps.getProperty("db.usage.username");
11201125
final String usagePassword = dbProps.getProperty("db.usage.password");
11211126

@@ -1127,7 +1132,8 @@ public static void initDataSource(Properties dbProps) {
11271132
s_usageDS = createDataSource(dbProps.getProperty("db.usage.connectionPoolLib"), usageUriAndDriver.first(),
11281133
usageUsername, usagePassword, usageMaxActive, usageMaxIdle, usageMaxWait, null,
11291134
null, null, null, null,
1130-
usageMinIdleConnections, usageConnectionTimeout, usageKeepAliveTimeout, isolationLevel, "usage");
1135+
usageMinIdleConnections, usageConnectionTimeout, usageKeepAliveTimeout, usageLeakDetectionThreshold,
1136+
usageRegisterMbeans, isolationLevel, "usage");
11311137

11321138
try {
11331139
// Configure the simulator db
@@ -1137,6 +1143,8 @@ public static void initDataSource(Properties dbProps) {
11371143
final Integer simulatorMinIdleConnections = parseNumber(dbProps.getProperty("db.simulator.minIdleConnections"), Integer.class);
11381144
final Long simulatorConnectionTimeout = parseNumber(dbProps.getProperty("db.simulator.connectionTimeout"), Long.class);
11391145
final Long simulatorKeepAliveTimeout = parseNumber(dbProps.getProperty("db.simulator.keepAliveTime"), Long.class);
1146+
final Long simulatorLeakDetectionThreshold = parseNumber(dbProps.getProperty("db.simulator.leakDetectionThreshold"), Long.class);
1147+
final Boolean simulatorRegisterMbeans = BooleanUtils.toBooleanObject(dbProps.getProperty("db.simulator.registerMbeans"));
11401148
final String simulatorUsername = dbProps.getProperty("db.simulator.username");
11411149
final String simulatorPassword = dbProps.getProperty("db.simulator.password");
11421150

@@ -1167,7 +1175,8 @@ public static void initDataSource(Properties dbProps) {
11671175
simulatorConnectionUri, simulatorUsername, simulatorPassword, simulatorMaxActive,
11681176
simulatorMaxIdle, simulatorMaxWait, null, null, null, null,
11691177
cloudValidationQuery, simulatorMinIdleConnections, simulatorConnectionTimeout,
1170-
simulatorKeepAliveTimeout, isolationLevel, "simulator");
1178+
simulatorKeepAliveTimeout, simulatorLeakDetectionThreshold, simulatorRegisterMbeans,
1179+
isolationLevel, "simulator");
11711180
} catch (Exception e) {
11721181
LOGGER.debug("Simulator DB properties are not available. Not initializing simulator DS");
11731182
}
@@ -1269,20 +1278,22 @@ protected static String buildConnectionUri(String loadBalanceStrategy, String dr
12691278
private static DataSource createDataSource(String connectionPoolLib, String uri, String username, String password,
12701279
Integer maxActive, Integer maxIdle, Long maxWait, Long timeBtwnEvictionRuns, Long minEvictableIdleTime,
12711280
Boolean testWhileIdle, Boolean testOnBorrow, String validationQuery, Integer minIdleConnections,
1272-
Long connectionTimeout, Long keepAliveTime, Integer isolationLevel, String dsName) {
1281+
Long connectionTimeout, Long keepAliveTime, Long leakDetectionThreshold, Boolean registerMbeans,
1282+
Integer isolationLevel, String dsName) {
12731283
LOGGER.debug("Creating datasource for database: {} with connection pool lib: {}", dsName,
12741284
connectionPoolLib);
12751285
if (CONNECTION_POOL_LIB_DBCP.equals(connectionPoolLib)) {
12761286
return createDbcpDataSource(uri, username, password, maxActive, maxIdle, maxWait, timeBtwnEvictionRuns,
12771287
minEvictableIdleTime, testWhileIdle, testOnBorrow, validationQuery, isolationLevel);
12781288
}
12791289
return createHikaricpDataSource(uri, username, password, maxActive, maxIdle, maxWait, minIdleConnections,
1280-
connectionTimeout, keepAliveTime, isolationLevel, dsName);
1290+
connectionTimeout, keepAliveTime, leakDetectionThreshold, registerMbeans, isolationLevel, dsName);
12811291
}
12821292

12831293
private static DataSource createHikaricpDataSource(String uri, String username, String password,
12841294
Integer maxActive, Integer maxIdle, Long maxWait,
12851295
Integer minIdleConnections, Long connectionTimeout, Long keepAliveTime,
1296+
Long leakDetectionThreshold, Boolean registerMbeans,
12861297
Integer isolationLevel, String dsName) {
12871298
HikariConfig config = new HikariConfig();
12881299
config.setJdbcUrl(uri);
@@ -1299,6 +1310,8 @@ private static DataSource createHikaricpDataSource(String uri, String username,
12991310
config.setConnectionTimeout(ObjectUtils.defaultIfNull(connectionTimeout, 30000L));
13001311
config.setKeepaliveTime(ObjectUtils.defaultIfNull(keepAliveTime, 600000L));
13011312

1313+
applyHikariDebugSettings(config, leakDetectionThreshold, registerMbeans, dsName);
1314+
13021315
String isolationLevelString = "TRANSACTION_READ_COMMITTED";
13031316
if (isolationLevel == Connection.TRANSACTION_SERIALIZABLE) {
13041317
isolationLevelString = "TRANSACTION_SERIALIZABLE";
@@ -1326,6 +1339,21 @@ private static DataSource createHikaricpDataSource(String uri, String username,
13261339
return dataSource;
13271340
}
13281341

1342+
/**
1343+
* Applies optional HikariCP debugging aids (leak detection and JMX MBeans). Both are disabled by
1344+
* default; leakDetectionThreshold is only applied when set to a positive value (HikariCP treats 0
1345+
* as disabled and ignores values below 2000ms). Package-private for unit testing.
1346+
*/
1347+
static void applyHikariDebugSettings(HikariConfig config, Long leakDetectionThreshold, Boolean registerMbeans, String dsName) {
1348+
if (leakDetectionThreshold != null && leakDetectionThreshold > 0) {
1349+
config.setLeakDetectionThreshold(leakDetectionThreshold);
1350+
}
1351+
config.setRegisterMbeans(ObjectUtils.defaultIfNull(registerMbeans, false));
1352+
LOGGER.debug("HikariCP pool {}: leakDetectionThreshold={} ms, registerMbeans={}", dsName,
1353+
ObjectUtils.defaultIfNull(leakDetectionThreshold, 0L),
1354+
ObjectUtils.defaultIfNull(registerMbeans, false));
1355+
}
1356+
13291357
private static DataSource createDbcpDataSource(String uri, String username, String password,
13301358
Integer maxActive, Integer maxIdle, Long maxWait,
13311359
Long timeBtwnEvictionRuns, Long minEvictableIdleTime,

framework/db/src/test/java/com/cloud/utils/db/TransactionLegacyTest.java

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package com.cloud.utils.db;
1818

1919
import com.cloud.utils.Pair;
20+
import com.zaxxer.hikari.HikariConfig;
2021
import org.junit.Assert;
2122
import org.junit.Before;
2223
import org.junit.Test;
@@ -114,4 +115,44 @@ public void buildConnectionUriTestUseSslTrue() {
114115

115116
Assert.assertEquals("driver://host:5555/cloud?autoReconnect=false&useSSL=true", result);
116117
}
118+
119+
@Test
120+
public void applyHikariDebugSettingsDisabledByDefault() {
121+
HikariConfig config = new HikariConfig();
122+
123+
TransactionLegacy.applyHikariDebugSettings(config, null, null, "cloud");
124+
125+
Assert.assertEquals(0L, config.getLeakDetectionThreshold());
126+
Assert.assertFalse(config.isRegisterMbeans());
127+
}
128+
129+
@Test
130+
public void applyHikariDebugSettingsZeroThresholdKeepsLeakDetectionDisabled() {
131+
HikariConfig config = new HikariConfig();
132+
133+
TransactionLegacy.applyHikariDebugSettings(config, 0L, false, "cloud");
134+
135+
Assert.assertEquals(0L, config.getLeakDetectionThreshold());
136+
Assert.assertFalse(config.isRegisterMbeans());
137+
}
138+
139+
@Test
140+
public void applyHikariDebugSettingsEnablesLeakDetection() {
141+
HikariConfig config = new HikariConfig();
142+
143+
TransactionLegacy.applyHikariDebugSettings(config, 60000L, null, "cloud");
144+
145+
Assert.assertEquals(60000L, config.getLeakDetectionThreshold());
146+
Assert.assertFalse(config.isRegisterMbeans());
147+
}
148+
149+
@Test
150+
public void applyHikariDebugSettingsEnablesRegisterMbeans() {
151+
HikariConfig config = new HikariConfig();
152+
153+
TransactionLegacy.applyHikariDebugSettings(config, null, true, "cloud");
154+
155+
Assert.assertEquals(0L, config.getLeakDetectionThreshold());
156+
Assert.assertTrue(config.isRegisterMbeans());
157+
}
117158
}

0 commit comments

Comments
 (0)