Set up connection pool and enforce usage of it

Attempt to initialize connection pool on startup until a timeout.
This commit is contained in:
P0nk
2021-04-02 14:20:45 +02:00
parent 05bd668cc2
commit 45331d17b0
4 changed files with 59 additions and 80 deletions

View File

@@ -165,7 +165,6 @@ server:
DB_URL: "jdbc:mysql://localhost:3306/cosmic"
DB_USER: "cosmic_server"
DB_PASS: "snailshell"
DB_CONNECTION_POOL: true #Installs a connection pool to hub DB connections. Set false to default.
#Login Configuration
WORLDS: 1 #Initial number of worlds on the server.

View File

@@ -8,7 +8,6 @@ public class ServerConfig {
public String DB_URL;
public String DB_USER;
public String DB_PASS;
public boolean DB_CONNECTION_POOL;
//Login Configuration
public int WORLDS;

View File

@@ -866,8 +866,13 @@ public class Server {
public void init() {
System.out.println("Cosmic v" + ServerConstants.VERSION + " starting up.\r\n");
if(YamlConfig.config.server.SHUTDOWNHOOK)
if(YamlConfig.config.server.SHUTDOWNHOOK) {
Runtime.getRuntime().addShutdownHook(new Thread(shutdown(false)));
}
if (!DatabaseConnection.initializeConnectionPool()) {
throw new IllegalStateException("Failed to initiate a connection to the database");
}
TimeZone.setDefault(TimeZone.getTimeZone(YamlConfig.config.server.TIMEZONE));

View File

@@ -1,10 +1,10 @@
package tools;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.time.Instant;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
@@ -16,82 +16,58 @@ import config.YamlConfig;
* @author Ronan - some connection pool to this beautiful code
*/
public class DatabaseConnection {
private static HikariDataSource ds;
public static Connection getConnection() throws SQLException {
if(ds != null) {
try {
return ds.getConnection();
} catch (SQLException sqle) {
sqle.printStackTrace();
}
}
int denies = 0;
while(true) { // There is no way it can pass with a null out of here?
try {
return DriverManager.getConnection(YamlConfig.config.server.DB_URL, YamlConfig.config.server.DB_USER, YamlConfig.config.server.DB_PASS);
} catch (SQLException sqle) {
denies++;
if(denies == 3) {
// Give up, throw exception. Nothing good will come from this.
FilePrinter.printError(FilePrinter.SQL_EXCEPTION, "SQL Driver refused to give a connection after " + denies + " tries. Problem: " + sqle.getMessage());
throw sqle;
}
}
}
}
private static int getNumberOfAccounts() {
try {
Connection con = DriverManager.getConnection(YamlConfig.config.server.DB_URL, YamlConfig.config.server.DB_USER, YamlConfig.config.server.DB_PASS);
try (PreparedStatement ps = con.prepareStatement("SELECT count(*) FROM accounts")) {
try (ResultSet rs = ps.executeQuery()) {
rs.next();
return rs.getInt(1);
}
} finally {
con.close();
}
} catch(SQLException sqle) {
return 20;
}
}
public DatabaseConnection() {
try {
Class.forName("com.mysql.jdbc.Driver"); // touch the mysql driver
} catch (ClassNotFoundException e) {
System.out.println("[SEVERE] SQL Driver Not Found. Consider death by clams.");
e.printStackTrace();
}
ds = null;
if(YamlConfig.config.server.DB_CONNECTION_POOL) {
// Connection Pool on database ftw!
HikariConfig config = new HikariConfig();
config.setJdbcUrl(YamlConfig.config.server.DB_URL);
config.setUsername(YamlConfig.config.server.DB_USER);
config.setPassword(YamlConfig.config.server.DB_PASS);
// Make sure pool size is comfortable for the worst case scenario.
// Under 100 accounts? Make it 10. Over 10000 accounts? Make it 30.
int poolSize = (int)Math.ceil(0.00202020202 * getNumberOfAccounts() + 9.797979798);
if(poolSize < 10) poolSize = 10;
else if(poolSize > 30) poolSize = 30;
config.setConnectionTimeout(30 * 1000);
config.setMaximumPoolSize(poolSize);
config.addDataSourceProperty("cachePrepStmts", true);
config.addDataSourceProperty("prepStmtCacheSize", 25);
config.addDataSourceProperty("prepStmtCacheSqlLimit", 2048);
private static HikariDataSource dataSource;
ds = new HikariDataSource(config);
public static Connection getConnection() throws SQLException {
if (dataSource == null) {
throw new IllegalStateException("Unable to get connection from uninitialized connection pool");
}
return dataSource.getConnection();
}
private static HikariConfig getConfig() {
HikariConfig config = new HikariConfig();
config.setJdbcUrl(YamlConfig.config.server.DB_URL);
config.setUsername(YamlConfig.config.server.DB_USER);
config.setPassword(YamlConfig.config.server.DB_PASS);
config.setConnectionTimeout(30 * 1000); // Hikari default
config.setMaximumPoolSize(10); // Hikari default
config.addDataSourceProperty("cachePrepStmts", true);
config.addDataSourceProperty("prepStmtCacheSize", 25);
config.addDataSourceProperty("prepStmtCacheSqlLimit", 2048);
return config;
}
/**
* Initiate connection to the database
*
* @return true if connection to the database initiated successfully, false if not successful
*/
public static boolean initializeConnectionPool() {
final int timeoutSeconds = 60;
final Instant timeout = Instant.now().plusSeconds(timeoutSeconds);
System.out.println("Initializing connection pool...");
final HikariConfig config = getConfig();
HikariDataSource hikariDataSource;
int attempt = 1;
while (Instant.now().isBefore(timeout)) {
try {
hikariDataSource = new HikariDataSource(config);
} catch (Exception e) {
System.err.printf("Failed to initialize database connection pool after %d attempt(s)%n", attempt++);
continue;
}
dataSource = hikariDataSource;
return true;
}
// Timed out - failed to initialize
return false;
}
}