diff --git a/Emulator/pom.xml b/Emulator/pom.xml
index e5e2d4f7..005fdfa2 100644
--- a/Emulator/pom.xml
+++ b/Emulator/pom.xml
@@ -6,7 +6,7 @@
com.eu.habbo
Habbo
- 4.1.0
+ 4.0.5
UTF-8
@@ -86,11 +86,11 @@
2.11.0
-
+
- com.mysql
- mysql-connector-j
- 9.1.0
+ org.mariadb.jdbc
+ mariadb-java-client
+ 3.5.1
runtime
diff --git a/Emulator/src/main/java/com/eu/habbo/database/Database.java b/Emulator/src/main/java/com/eu/habbo/database/Database.java
index 00be0fee..9a7c2a4e 100644
--- a/Emulator/src/main/java/com/eu/habbo/database/Database.java
+++ b/Emulator/src/main/java/com/eu/habbo/database/Database.java
@@ -48,11 +48,11 @@ public class Database {
}
public void dispose() {
- if (this.databasePool != null) {
- this.databasePool.getDatabase().close();
+ // dataSource is the same instance returned by databasePool.getDatabase(),
+ // so close it exactly once.
+ if (this.dataSource != null && !this.dataSource.isClosed()) {
+ this.dataSource.close();
}
-
- this.dataSource.close();
}
public HikariDataSource getDataSource() {
diff --git a/Emulator/src/main/java/com/eu/habbo/database/DatabasePool.java b/Emulator/src/main/java/com/eu/habbo/database/DatabasePool.java
index 68a3e86c..acd50764 100644
--- a/Emulator/src/main/java/com/eu/habbo/database/DatabasePool.java
+++ b/Emulator/src/main/java/com/eu/habbo/database/DatabasePool.java
@@ -8,41 +8,85 @@ import org.slf4j.LoggerFactory;
class DatabasePool {
private final Logger log = LoggerFactory.getLogger(DatabasePool.class);
+
+ // Connection settings
+ private static final String DB_HOSTNAME_KEY = "db.hostname";
+ private static final String DB_PORT_KEY = "db.port";
+ private static final String DB_PASSWORD_KEY = "db.password";
+ private static final String DB_NAME_KEY = "db.database";
+ private static final String DB_USER_KEY = "db.username";
+ private static final String DB_PARAMS_KEY = "db.params";
+
+ // Pool sizing
private static final String DB_POOL_MAX_SIZE = "db.pool.maxsize";
private static final String DB_POOL_MIN_SIZE = "db.pool.minsize";
- private static final String DB_HOSTNAME_KEY = "db.hostname";
- private static final String DB_PORT_KEY = "db.port";
- private static final String DB_PASSWORD_KEY = "db.password";
- private static final String DB_NAME_KEY = "db.database";
- private static final String DB_USER_KEY = "db.username";
- private static final String DB_PARAMS_KEY = "db.params";
+
+ // Pool tuning (all overridable via config.ini; sensible MariaDB defaults apply otherwise)
+ private static final String DB_POOL_CONNECTION_TIMEOUT = "db.pool.connection_timeout_ms";
+ private static final String DB_POOL_IDLE_TIMEOUT = "db.pool.idle_timeout_ms";
+ private static final String DB_POOL_MAX_LIFETIME = "db.pool.max_lifetime_ms";
+ private static final String DB_POOL_LEAK_THRESHOLD = "db.pool.leak_detection_ms";
+ private static final String DB_POOL_VALIDATION_TIMEOUT = "db.pool.validation_timeout_ms";
+
private HikariDataSource database;
- private static DatabasePool instance;
DatabasePool() {
}
- public static synchronized DatabasePool getInstance() {
- if (instance == null) {
- instance = new DatabasePool();
- }
- return instance;
- }
-
public boolean getStoragePooling(ConfigurationManager config) {
try {
HikariConfig databaseConfiguration = new HikariConfig();
+
+ // Pool sizing
databaseConfiguration.setMaximumPoolSize(config.getInt(DB_POOL_MAX_SIZE, 50));
databaseConfiguration.setMinimumIdle(config.getInt(DB_POOL_MIN_SIZE, 10));
- databaseConfiguration.setJdbcUrl("jdbc:mysql://" + config.getValue(DB_HOSTNAME_KEY, "localhost") + ":" + config.getValue(DB_PORT_KEY, "3306") + "/" + config.getValue(DB_NAME_KEY) + config.getValue(DB_PARAMS_KEY));
- databaseConfiguration.addDataSourceProperty("serverName", config.getValue(DB_HOSTNAME_KEY, "localhost"));
- databaseConfiguration.addDataSourceProperty("port", config.getValue(DB_PORT_KEY, "3306"));
- databaseConfiguration.addDataSourceProperty("databaseName", config.getValue(DB_NAME_KEY));
- databaseConfiguration.addDataSourceProperty("user", config.getValue(DB_USER_KEY));
- databaseConfiguration.addDataSourceProperty("password", config.getValue(DB_PASSWORD_KEY));
+
+ // Pool timeouts (milliseconds)
+ databaseConfiguration.setConnectionTimeout(config.getInt(DB_POOL_CONNECTION_TIMEOUT, 10_000));
+ databaseConfiguration.setIdleTimeout(config.getInt(DB_POOL_IDLE_TIMEOUT, 600_000));
+ databaseConfiguration.setMaxLifetime(config.getInt(DB_POOL_MAX_LIFETIME, 1_800_000));
+ databaseConfiguration.setValidationTimeout(config.getInt(DB_POOL_VALIDATION_TIMEOUT, 5_000));
+
+ // Leak detection: 0 disables it. Default 20s helps locate connections
+ // that weren't closed in a try-with-resources block.
+ int leakThreshold = config.getInt(DB_POOL_LEAK_THRESHOLD, 20_000);
+ if (leakThreshold > 0) {
+ databaseConfiguration.setLeakDetectionThreshold(leakThreshold);
+ }
+
+ // Use the MariaDB Connector/J native protocol instead of the Oracle MySQL driver.
+ databaseConfiguration.setJdbcUrl("jdbc:mariadb://"
+ + config.getValue(DB_HOSTNAME_KEY, "localhost") + ":"
+ + config.getValue(DB_PORT_KEY, "3306") + "/"
+ + config.getValue(DB_NAME_KEY)
+ + config.getValue(DB_PARAMS_KEY));
+ databaseConfiguration.setUsername(config.getValue(DB_USER_KEY));
+ databaseConfiguration.setPassword(config.getValue(DB_PASSWORD_KEY));
+
+ // Prepared-statement caching. Without these, Hikari's cache is off entirely
+ // and every prepareStatement() call re-parses on the server side.
+ databaseConfiguration.addDataSourceProperty("cachePrepStmts", "true");
+ databaseConfiguration.addDataSourceProperty("prepStmtCacheSize", "500");
+ databaseConfiguration.addDataSourceProperty("prepStmtCacheSqlLimit", "2048");
+ databaseConfiguration.addDataSourceProperty("useServerPrepStmts", "true");
+
+ // Bulk write throughput: rewrites batched INSERTs into a single multi-value
+ // INSERT statement. Huge win for item/room/inventory persistence paths.
+ databaseConfiguration.addDataSourceProperty("rewriteBatchedStatements", "true");
+
+ // Cut per-connection round-trips.
+ databaseConfiguration.addDataSourceProperty("cacheServerConfiguration", "true");
+ databaseConfiguration.addDataSourceProperty("useLocalSessionState", "true");
+ databaseConfiguration.addDataSourceProperty("cacheResultSetMetadata", "true");
+ databaseConfiguration.addDataSourceProperty("elideSetAutoCommits", "true");
+ databaseConfiguration.addDataSourceProperty("maintainTimeStats", "false");
+
+ databaseConfiguration.setPoolName("HabboHikariPool");
+
log.info("INITIALIZING DATABASE SERVER: " + config.getValue(DB_HOSTNAME_KEY));
log.info("ON PORT: " + config.getValue(DB_PORT_KEY));
log.info("HABBO DATABASE: " + config.getValue(DB_NAME_KEY));
+ log.info("USING DRIVER: MariaDB Connector/J");
this.database = new HikariDataSource(databaseConfiguration);
} catch (Exception e) {
@@ -58,4 +102,4 @@ class DatabasePool {
}
return database;
}
-}
\ No newline at end of file
+}