diff --git a/config.yaml b/config.yaml index 4bc57105bd..e23faaa268 100644 --- a/config.yaml +++ b/config.yaml @@ -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. diff --git a/src/main/java/config/ServerConfig.java b/src/main/java/config/ServerConfig.java index fbef750367..2feeec8b4d 100644 --- a/src/main/java/config/ServerConfig.java +++ b/src/main/java/config/ServerConfig.java @@ -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; diff --git a/src/main/java/net/server/Server.java b/src/main/java/net/server/Server.java index 2673df652d..ea99fd3c1f 100644 --- a/src/main/java/net/server/Server.java +++ b/src/main/java/net/server/Server.java @@ -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)); diff --git a/src/main/java/tools/DatabaseConnection.java b/src/main/java/tools/DatabaseConnection.java index e408be455f..821b30bb1b 100644 --- a/src/main/java/tools/DatabaseConnection.java +++ b/src/main/java/tools/DatabaseConnection.java @@ -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; } }