framework/db: use HikariCP instead of dbcp2

Replaces dbcp2 connection pool library with more performant HikariCP.
With this unit tests are failing but build is passing.

Signed-off-by: Rohit Yadav <rohit.yadav@shapeblue.com>
This commit is contained in:
Rohit Yadav 2024-05-09 22:36:33 +05:30
parent f21a00b2de
commit 696927455f
5 changed files with 112 additions and 70 deletions

View File

@ -32,7 +32,7 @@ db.cloud.name=cloud
# CloudStack database tuning parameters
db.cloud.maxActive=250
db.cloud.maxIdle=30
db.cloud.maxWait=10000
db.cloud.maxWait=600000
db.cloud.validationQuery=/* ping */ SELECT 1
db.cloud.testOnBorrow=true
db.cloud.testWhileIdle=true
@ -64,7 +64,7 @@ db.usage.name=cloud_usage
# usage database tuning parameters
db.usage.maxActive=100
db.usage.maxIdle=30
db.usage.maxWait=10000
db.usage.maxWait=600000
db.usage.url.params=serverTimezone=UTC
# Simulator database settings
@ -76,7 +76,7 @@ db.simulator.port=3306
db.simulator.name=simulator
db.simulator.maxActive=250
db.simulator.maxIdle=30
db.simulator.maxWait=10000
db.simulator.maxWait=600000
db.simulator.autoReconnect=true

View File

@ -29,13 +29,17 @@
</parent>
<dependencies>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-dbcp2</artifactId>
<groupId>org.eclipse.persistence</groupId>
<artifactId>javax.persistence</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
</dependency>
<dependency>
<groupId>org.jasypt</groupId>
<artifactId>jasypt</artifactId>

View File

@ -37,8 +37,8 @@
<artifactId>javax.persistence</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-dbcp2</artifactId>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
</dependency>
<dependency>
<groupId>commons-io</groupId>

View File

@ -33,14 +33,8 @@ import java.util.concurrent.atomic.AtomicLong;
import javax.sql.DataSource;
import org.apache.commons.dbcp2.ConnectionFactory;
import org.apache.commons.dbcp2.DriverManagerConnectionFactory;
import org.apache.commons.dbcp2.PoolableConnection;
import org.apache.commons.dbcp2.PoolableConnectionFactory;
import org.apache.commons.dbcp2.PoolingDataSource;
import org.apache.commons.pool2.ObjectPool;
import org.apache.commons.pool2.impl.GenericObjectPool;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import org.apache.log4j.Logger;
import com.cloud.utils.Pair;
@ -1088,7 +1082,7 @@ public class TransactionLegacy implements Closeable {
// Default Data Source for CloudStack
s_ds = createDataSource(cloudConnectionUri, cloudUsername, cloudPassword, cloudMaxActive, cloudMaxIdle, cloudMaxWait,
cloudTimeBtwEvictionRunsMillis, cloudMinEvcitableIdleTimeMillis, cloudTestWhileIdle, cloudTestOnBorrow,
cloudValidationQuery, isolationLevel);
cloudValidationQuery, isolationLevel, "cloud");
// Configure the usage db
final int usageMaxActive = Integer.parseInt(dbProps.getProperty("db.usage.maxActive"));
@ -1111,7 +1105,7 @@ public class TransactionLegacy implements Closeable {
// Data Source for usage server
s_usageDS = createDataSource(usageConnectionUri, usageUsername, usagePassword,
usageMaxActive, usageMaxIdle, usageMaxWait, null, null, null, null,
null, isolationLevel);
null, isolationLevel, "usage");
try {
// Configure the simulator db
@ -1131,14 +1125,14 @@ public class TransactionLegacy implements Closeable {
DriverLoader.loadDriver(simulatorDriver);
s_simulatorDS = createDataSource(simulatorConnectionUri, simulatorUsername, simulatorPassword,
simulatorMaxActive, simulatorMaxIdle, simulatorMaxWait, null, null, null, null, cloudValidationQuery, isolationLevel);
simulatorMaxActive, simulatorMaxIdle, simulatorMaxWait, null, null, null, null, cloudValidationQuery, isolationLevel, "simulator");
} catch (Exception e) {
s_logger.debug("Simulator DB properties are not available. Not initializing simulator DS");
}
} catch (final Exception e) {
s_ds = getDefaultDataSource("cloud");
s_usageDS = getDefaultDataSource("cloud_usage");
s_simulatorDS = getDefaultDataSource("cloud_simulator");
s_simulatorDS = getDefaultDataSource("simulator");
s_logger.warn(
"Unable to load db configuration, using defaults with 5 connections. Falling back on assumed datasource on localhost:3306 using username:password=cloud:cloud. Please check your configuration",
e);
@ -1152,42 +1146,100 @@ public class TransactionLegacy implements Closeable {
Integer maxActive, Integer maxIdle, Long maxWait,
Long timeBtwnEvictionRuns, Long minEvictableIdleTime,
Boolean testWhileIdle, Boolean testOnBorrow,
String validationQuery, Integer isolationLevel) {
ConnectionFactory connectionFactory = new DriverManagerConnectionFactory(uri, username, password);
PoolableConnectionFactory poolableConnectionFactory = new PoolableConnectionFactory(connectionFactory, null);
GenericObjectPoolConfig config = createPoolConfig(maxActive, maxIdle, maxWait, timeBtwnEvictionRuns, minEvictableIdleTime, testWhileIdle, testOnBorrow);
ObjectPool<PoolableConnection> connectionPool = new GenericObjectPool<>(poolableConnectionFactory, config);
poolableConnectionFactory.setPool(connectionPool);
if (isolationLevel != null) {
poolableConnectionFactory.setDefaultTransactionIsolation(isolationLevel);
}
return new PoolingDataSource<>(connectionPool);
}
String validationQuery, Integer isolationLevel,
String dsName) {
HikariConfig config = new HikariConfig();
config.setJdbcUrl(uri);
config.setUsername(username);
config.setPassword(password);
/**
* Return a GenericObjectPoolConfig configuration usable on connection pool creation
*/
private static GenericObjectPoolConfig createPoolConfig(Integer maxActive, Integer maxIdle, Long maxWait,
Long timeBtwnEvictionRuns, Long minEvictableIdleTime,
Boolean testWhileIdle, Boolean testOnBorrow) {
GenericObjectPoolConfig config = new GenericObjectPoolConfig();
config.setMaxTotal(maxActive);
config.setMaxIdle(maxIdle);
config.setMaxWaitMillis(maxWait);
config.setPoolName(dsName);
if (maxActive != null) {
config.setMaximumPoolSize(maxActive);
} else {
config.setMaximumPoolSize(250); // 250 connections
}
if (maxIdle != null) {
config.setIdleTimeout(maxIdle * 1000);
} else {
config.setIdleTimeout(30000); // 30 seconds
}
if (maxWait != null) {
config.setMaxLifetime(maxWait);
} else {
config.setMaxLifetime(600000); // 10 minutes
}
if (timeBtwnEvictionRuns != null) {
config.setTimeBetweenEvictionRunsMillis(timeBtwnEvictionRuns);
// Connection pool properties
config.setMinimumIdle(5); // Minimum number of idle connections in the pool
config.setConnectionTimeout(30000); // 30 seconds in milliseconds
config.setKeepaliveTime(600000); // Keepalive time in milliseconds (10 minutes)
config.setIdleTimeout(300000); // 5 minutes
//config.setMinimumIdle(maxIdle);
//config.setConnectionTestQuery("/* ping */ SELECT 1"); // Connection test query
String isolationLevelString = "TRANSACTION_READ_COMMITTED";
if (isolationLevel == Connection.TRANSACTION_SERIALIZABLE) {
isolationLevelString = "TRANSACTION_SERIALIZABLE";
} else if (isolationLevel == Connection.TRANSACTION_READ_UNCOMMITTED) {
isolationLevelString = "TRANSACTION_READ_UNCOMMITTED";
} else if (isolationLevel == Connection.TRANSACTION_REPEATABLE_READ) {
isolationLevelString = "TRANSACTION_REPEATABLE_READ";
}
if (minEvictableIdleTime != null) {
config.setMinEvictableIdleTimeMillis(minEvictableIdleTime);
}
if (testWhileIdle != null) {
config.setTestWhileIdle(testWhileIdle);
}
if (testOnBorrow != null) {
config.setTestOnBorrow(testOnBorrow);
}
return config;
config.setTransactionIsolation(isolationLevelString);
// Standard datasource config for MySQL
config.addDataSourceProperty("cachePrepStmts", "true");
config.addDataSourceProperty("prepStmtCacheSize", "250");
config.addDataSourceProperty("prepStmtCacheSqlLimit", "2048");
// Additional config for MySQL
config.addDataSourceProperty("useServerPrepStmts", "true");
config.addDataSourceProperty("useLocalSessionState", "true");
config.addDataSourceProperty("rewriteBatchedStatements", "true");
config.addDataSourceProperty("cacheResultSetMetadata", "true");
config.addDataSourceProperty("cacheServerConfiguration", "true");
config.addDataSourceProperty("elideSetAutoCommits", "true");
config.addDataSourceProperty("maintainTimeStats", "false");
HikariDataSource dataSource = new HikariDataSource(config);
return dataSource;
}
@SuppressWarnings({"unchecked", "rawtypes"})
private static DataSource getDefaultDataSource(final String database) {
HikariConfig config = new HikariConfig();
config.setJdbcUrl("jdbc:mysql://localhost:3306/" + database + "?" + CONNECTION_PARAMS);
config.setUsername("cloud");
config.setPassword("cloud");
config.setPoolName(database);
config.setMaximumPoolSize(250); // 250 connections
config.setIdleTimeout(30000); // 30 seconds
config.setMaxLifetime(600000); // 10 minutes
// Connection pool properties
config.setConnectionTimeout(20000); // 20 seconds in milliseconds
config.setMinimumIdle(5); // Minimum number of idle connections in the pool
config.setKeepaliveTime(600000); // Keepalive time in milliseconds (10 minutes)
//config.setConnectionTestQuery("/* ping */ SELECT 1"); // Connection test query
config.setIdleTimeout(300000); // 5 minutes
config.setTransactionIsolation("TRANSACTION_READ_COMMITTED");
// Standard datasource config for MySQL
config.addDataSourceProperty("cachePrepStmts", "true");
config.addDataSourceProperty("prepStmtCacheSize", "250");
config.addDataSourceProperty("prepStmtCacheSqlLimit", "2048");
// Additional config for MySQL
config.addDataSourceProperty("useServerPrepStmts", "true");
config.addDataSourceProperty("useLocalSessionState", "true");
config.addDataSourceProperty("rewriteBatchedStatements", "true");
config.addDataSourceProperty("cacheResultSetMetadata", "true");
config.addDataSourceProperty("cacheServerConfiguration", "true");
config.addDataSourceProperty("elideSetAutoCommits", "true");
config.addDataSourceProperty("maintainTimeStats", "false");
HikariDataSource dataSource = new HikariDataSource(config);
return dataSource;
}
private static String getDBHAParams(String dbName, Properties dbProps) {
@ -1201,14 +1253,6 @@ public class TransactionLegacy implements Closeable {
return sb.toString();
}
@SuppressWarnings({"unchecked", "rawtypes"})
private static DataSource getDefaultDataSource(final String database) {
final ConnectionFactory connectionFactory = new DriverManagerConnectionFactory("jdbc:mysql://localhost:3306/" + database + "?" + CONNECTION_PARAMS, "cloud", "cloud");
final PoolableConnectionFactory poolableConnectionFactory = new PoolableConnectionFactory(connectionFactory, null);
final GenericObjectPool connectionPool = new GenericObjectPool(poolableConnectionFactory);
return new PoolingDataSource(connectionPool);
}
/**
* Used for unit testing primarily
*

14
pom.xml
View File

@ -100,7 +100,7 @@
<cs.commons-validator.version>1.6</cs.commons-validator.version>
<cs.configuration.version>1.10</cs.configuration.version>
<cs.daemon.version>1.3.3</cs.daemon.version>
<cs.dbcp.version>2.12.0</cs.dbcp.version>
<cs.hikaricp.version>5.1.0</cs.hikaricp.version>
<cs.discovery.version>0.5</cs.discovery.version>
<cs.lang.version>2.6</cs.lang.version>
<cs.pool.version>2.9.0</cs.pool.version>
@ -361,15 +361,9 @@
<version>${cs.daemon.version}</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-dbcp2</artifactId>
<version>${cs.dbcp.version}</version>
<exclusions>
<exclusion>
<artifactId>org.apache.commons</artifactId>
<groupId>commons-pool2</groupId>
</exclusion>
</exclusions>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
<version>${cs.hikaricp.version}</version>
</dependency>
<dependency>
<groupId>commons-discovery</groupId>