Add HikariCP connection pool for MySQL (#86)

* Add HikariCP connection pool for MySQL

* Make code more compatible with connection pooling

- Remove connection caching in Process/Consumer
- Use try-with-resources to make sure Connections always get closed, even in
  when an Exception occurs.

* Disable SSL for MySQL
This commit is contained in:
Jan Erik Petersen 2021-11-16 01:57:33 +01:00 committed by GitHub
parent 2d90f9cc08
commit fc99c24a0a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 157 additions and 200 deletions

View File

@ -37,6 +37,7 @@ dependencies {
}
compileOnly 'org.spigotmc:spigot-api:1.17-R0.1-SNAPSHOT'
implementation 'org.bstats:bstats-bukkit-lite:1.7'
implementation 'com.zaxxer:HikariCP:4.0.3'
}
jar {
@ -51,6 +52,7 @@ shadowJar {
dependencies {
// #toString because #getGroup technically returns an Object
relocate('org.bstats', project.group.toString())
relocate('com.zaxxer', project.group.toString())
}
archiveClassifier.set(null)
}

View File

@ -56,6 +56,10 @@
<pattern>org.bstats</pattern>
<shadedPattern>net.coreprotect</shadedPattern>
</relocation>
<relocation>
<pattern>com.zaxxer</pattern>
<shadedPattern>net.coreprotect</shadedPattern>
</relocation>
</relocations>
</configuration>
</execution>
@ -111,5 +115,10 @@
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
<version>4.0.3</version>
</dependency>
</dependencies>
</project>

View File

@ -445,8 +445,7 @@ public class CoreProtectAPI extends Queue {
return null;
}
try {
Connection connection = Database.getConnection(false, 1000);
try (Connection connection = Database.getConnection(false, 1000)) {
if (connection != null) {
Statement statement = connection.createStatement();
boolean restrictWorld = false;
@ -488,7 +487,6 @@ public class CoreProtectAPI extends Queue {
}
statement.close();
connection.close();
}
}
catch (Exception e) {

View File

@ -324,9 +324,8 @@ public class LookupCommand {
class BasicThread implements Runnable {
@Override
public void run() {
try {
try (Connection connection = Database.getConnection(true)) {
ConfigHandler.lookupThrottle.put(player2.getName(), new Object[] { true, System.currentTimeMillis() });
Connection connection = Database.getConnection(true);
if (connection != null) {
Statement statement = connection.createStatement();
String blockdata = ChestTransactionLookup.performLookup(command.getName(), statement, location, player2, p2, finalLimit, false);
@ -339,7 +338,6 @@ public class LookupCommand {
Chat.sendComponent(player2, blockdata);
}
statement.close();
connection.close();
}
else {
Chat.sendMessage(player2, Color.DARK_AQUA + "CoreProtect " + Color.WHITE + "- " + Phrase.build(Phrase.DATABASE_BUSY));
@ -422,9 +420,8 @@ public class LookupCommand {
class BasicThread implements Runnable {
@Override
public void run() {
try {
try (Connection connection = Database.getConnection(true)) {
ConfigHandler.lookupThrottle.put(player2.getName(), new Object[] { true, System.currentTimeMillis() });
Connection connection = Database.getConnection(true);
if (connection != null) {
Statement statement = connection.createStatement();
if (t == 8) {
@ -461,7 +458,6 @@ public class LookupCommand {
}
}
statement.close();
connection.close();
}
else {
Chat.sendMessage(player2, Color.DARK_AQUA + "CoreProtect " + Color.WHITE + "- " + Phrase.build(Phrase.DATABASE_BUSY));
@ -681,7 +677,7 @@ public class LookupCommand {
class BasicThread2 implements Runnable {
@Override
public void run() {
try {
try (Connection connection = Database.getConnection(true)) {
ConfigHandler.lookupThrottle.put(player2.getName(), new Object[] { true, System.currentTimeMillis() });
List<String> uuidList = new ArrayList<>();
@ -699,7 +695,6 @@ public class LookupCommand {
ConfigHandler.lookupAlist.put(player2.getName(), finalArgAction);
ConfigHandler.lookupRadius.put(player2.getName(), radius);
Connection connection = Database.getConnection(true);
if (connection != null) {
Statement statement = connection.createStatement();
String baduser = "";
@ -950,7 +945,6 @@ public class LookupCommand {
Chat.sendMessage(player2, Color.DARK_AQUA + "CoreProtect " + Color.WHITE + "- " + Phrase.build(Phrase.USER_NOT_FOUND, baduser));
}
statement.close();
connection.close();
}
else {
Chat.sendMessage(player2, Color.DARK_AQUA + "CoreProtect " + Color.WHITE + "- " + Phrase.build(Phrase.DATABASE_BUSY));

View File

@ -1,10 +1,7 @@
package net.coreprotect.command;
import java.io.File;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.*;
import java.text.NumberFormat;
import java.util.Arrays;
import java.util.List;
@ -94,14 +91,12 @@ public class PurgeCommand extends Consumer {
@Override
public void run() {
try {
try (Connection connection = Database.getConnection(false, 500)) {
int timestamp = (int) (System.currentTimeMillis() / 1000L);
int ptime = timestamp - seconds;
long removed = 0;
Connection connection = null;
for (int i = 0; i <= 5; i++) {
connection = Database.getConnection(false, 500);
if (connection != null) {
break;
}
@ -344,8 +339,6 @@ public class PurgeCommand extends Consumer {
}
}
connection.close();
if (abort) {
if (!Config.getGlobal().MYSQL) {
(new File(ConfigHandler.path + ConfigHandler.sqlite + ".tmp")).delete();

View File

@ -310,11 +310,10 @@ public class RollbackRestoreCommand {
class BasicThread2 implements Runnable {
@Override
public void run() {
try {
try (Connection connection = Database.getConnection(false, 1000)) {
ConfigHandler.lookupThrottle.put(player.getName(), new Object[] { true, System.currentTimeMillis() });
int action = finalAction;
Location location = locationFinal;
Connection connection = Database.getConnection(false, 1000);
if (connection != null) {
Statement statement = connection.createStatement();
String baduser = "";
@ -407,7 +406,6 @@ public class RollbackRestoreCommand {
Chat.sendMessage(player2, Color.DARK_AQUA + "CoreProtect " + Color.WHITE + "- " + Phrase.build(Phrase.USER_NOT_FOUND, baduser));
}
statement.close();
connection.close();
}
else {
Chat.sendMessage(player2, Color.DARK_AQUA + "CoreProtect " + Color.WHITE + "- " + Phrase.build(Phrase.DATABASE_BUSY));

View File

@ -14,6 +14,8 @@ import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import org.bukkit.Bukkit;
import org.bukkit.World;
import org.bukkit.entity.Player;
@ -50,6 +52,7 @@ public class ConfigHandler extends Queue {
public static String username = "root";
public static String password = "";
public static String prefix = "co_";
public static HikariDataSource hikariDataSource = null;
public static final boolean isSpigot = Util.isSpigot();
public static final boolean isPaper = Util.isPaper();
public static volatile boolean serverRunning = false;
@ -171,6 +174,12 @@ public class ConfigHandler extends Queue {
}
public static void loadDatabase() {
// close old pool when we reload the database, e.g. in purge command
if (ConfigHandler.hikariDataSource != null) {
ConfigHandler.hikariDataSource.close();
ConfigHandler.hikariDataSource = null;
}
if (!Config.getGlobal().MYSQL) {
try {
File tempFile = File.createTempFile("CoreProtect_" + System.currentTimeMillis(), ".tmp");
@ -198,11 +207,29 @@ public class ConfigHandler extends Queue {
catch (Exception e) {
e.printStackTrace();
}
} else {
HikariConfig config = new HikariConfig();
config.setJdbcUrl("jdbc:mysql://" + ConfigHandler.host + ":" + ConfigHandler.port + "/" + ConfigHandler.database);
config.setUsername(ConfigHandler.username);
config.setPassword(ConfigHandler.password);
config.addDataSourceProperty("characterEncoding", "UTF-8");
config.addDataSourceProperty("connectionTimeout", "10000");
/* https://github.com/brettwooldridge/HikariCP/wiki/MySQL-Configuration */
/* https://cdn.oreillystatic.com/en/assets/1/event/21/Connector_J%20Performance%20Gems%20Presentation.pdf */
config.addDataSourceProperty("cachePrepStmts", "true");
config.addDataSourceProperty("prepStmtCacheSize", "250");
config.addDataSourceProperty("prepStmtCacheSqlLimit", "2048");
config.addDataSourceProperty("useServerPrepStmts", "true");
config.addDataSourceProperty("useLocalSessionState", "true");
config.addDataSourceProperty("rewriteBatchedStatements", "true");
config.addDataSourceProperty("cacheServerConfiguration", "true");
config.addDataSourceProperty("maintainTimeStats", "false");
/* Disable SSL to suppress the unverified server identity warning */
config.addDataSourceProperty("useSSL", "false");
ConfigHandler.hikariDataSource = new HikariDataSource(config);
}
if (ConfigHandler.serverRunning) {
Consumer.resetConnection = true;
}
Database.createDatabaseTables(ConfigHandler.prefix, false);
}
@ -368,7 +395,11 @@ public class ConfigHandler extends Queue {
ConfigHandler.loadConfig(); // Load (or create) the configuration file.
ConfigHandler.loadDatabase(); // Initialize MySQL and create tables if necessary.
Connection connection = Database.getConnection(true, 0);
} catch (Exception e) {
e.printStackTrace();
}
try (Connection connection = Database.getConnection(true, 0)) {
Statement statement = connection.createStatement();
ConfigHandler.checkPlayers(connection);
@ -396,7 +427,6 @@ public class ConfigHandler extends Queue {
}
statement.close();
connection.close();
return validVersion && databaseLock;
}

View File

@ -18,7 +18,6 @@ import net.coreprotect.consumer.process.Process;
public class Consumer extends Process implements Runnable, Thread.UncaughtExceptionHandler {
private static Thread consumerThread = null;
public static volatile boolean resetConnection = false;
public static volatile int currentConsumer = 0;
public static volatile boolean isPaused = false;
public static volatile boolean transacting = false;
@ -103,12 +102,6 @@ public class Consumer extends Process implements Runnable, Thread.UncaughtExcept
try {
while ((ConfigHandler.serverRunning || ConfigHandler.converterRunning) && (Consumer.isPaused || ConfigHandler.purgeRunning || Consumer.consumer_id.get(process_id)[1] == 1)) {
pausedSuccess = true;
if (Consumer.isPaused || ConfigHandler.purgeRunning) {
if (connection != null) {
connection.close();
connection = null;
}
}
Thread.sleep(100);
}
}

View File

@ -2,6 +2,7 @@ package net.coreprotect.consumer.process;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Locale;
@ -45,36 +46,13 @@ public class Process {
public static final int BLOCKDATA_INSERT = 25;
public static final int ITEM_TRANSACTION = 26;
protected static Connection connection = null;
public static int lastLockUpdate = 0;
private static int lastConnection = 0;
private static volatile int currentConsumerSize = 0;
public static int getCurrentConsumerSize() {
return currentConsumerSize;
}
private static void validateConnection(boolean lastRun) {
try {
if (connection != null) {
int timeSinceLastConnection = ((int) (System.currentTimeMillis() / 1000L)) - lastConnection;
if ((!lastRun && timeSinceLastConnection > 900) || !connection.isValid(5) || Consumer.resetConnection) {
connection.close();
connection = null;
Consumer.resetConnection = false;
}
}
if (connection == null && ConfigHandler.serverRunning) {
connection = Database.getConnection(false, 500);
lastConnection = (int) (System.currentTimeMillis() / 1000L);
}
}
catch (Exception e) {
e.printStackTrace();
}
}
protected static void updateLockTable(Statement statement, int locked) {
try {
int unixTimestamp = (int) (System.currentTimeMillis() / 1000L);
@ -90,9 +68,7 @@ public class Process {
}
protected static void processConsumer(int processId, boolean lastRun) {
try {
// Connection
validateConnection(lastRun);
try (Connection connection = Database.getConnection(false, 500)) {
if (connection == null) {
return;
}
@ -243,7 +219,6 @@ public class Process {
for (int index = (i - 1); index >= 0; index--) {
consumerData.remove(index);
}
connection = null;
currentConsumerSize = 0;
Consumer.isPaused = false;
return;
@ -288,11 +263,6 @@ public class Process {
users.clear();
consumerObject.clear();
consumerData.clear();
if (lastRun) {
connection.close();
connection = null;
}
}
catch (Exception e) {
e.printStackTrace();

View File

@ -138,15 +138,7 @@ public class Database extends Queue {
}
if (Config.getGlobal().MYSQL) {
try {
/* Using useServerPrepStmts, cachePrepStmts, and rewriteBatchedStatements per https://github.com/brettwooldridge/HikariCP/wiki/MySQL-Configuration */
String database = "jdbc:mysql://" + ConfigHandler.host + ":" + ConfigHandler.port + "/" + ConfigHandler.database + "?useUnicode=true&characterEncoding=utf-8&connectTimeout=10000&useSSL=false&allowPublicKeyRetrieval=true&useCursorFetch=true&useLocalSessionState=true&rewriteBatchedStatements=true&maintainTimeStats=false";
connection = DriverManager.getConnection(database, ConfigHandler.username, ConfigHandler.password);
/* Recommended implementation per https://dev.mysql.com/doc/refman/5.0/en/charset-applications.html & https://dev.mysql.com/doc/refman/5.0/en/charset-syntax.html */
Statement statement = connection.createStatement();
statement.executeUpdate("SET NAMES 'utf8mb4'"); // COLLATE 'utf8mb4mb4_general_ci'
statement.close();
connection = ConfigHandler.hikariDataSource.getConnection();
ConfigHandler.databaseReachable = true;
}
catch (Exception e) {
@ -316,8 +308,7 @@ public class Database extends Queue {
if (Config.getGlobal().MYSQL) {
boolean success = false;
try {
Connection connection = Database.getConnection(true, true, true, 0);
try (Connection connection = Database.getConnection(true, true, true, 0)) {
if (connection != null) {
String index = "";
Statement statement = connection.createStatement();
@ -357,7 +348,6 @@ public class Database extends Queue {
initializeTables(prefix, statement);
}
statement.close();
connection.close();
success = true;
}
}
@ -369,8 +359,7 @@ public class Database extends Queue {
}
}
if (!Config.getGlobal().MYSQL) {
try {
Connection connection = Database.getConnection(true, 0);
try (Connection connection = Database.getConnection(true, 0)) {
Statement statement = connection.createStatement();
List<String> tableData = new ArrayList<>();
List<String> indexData = new ArrayList<>();
@ -553,7 +542,6 @@ public class Database extends Queue {
initializeTables(prefix, statement);
}
statement.close();
connection.close();
}
catch (Exception e) {
e.printStackTrace();

View File

@ -18,7 +18,7 @@ public class BlockLookupAPI {
public static List<String[]> performLookup(Block block, int offset) {
List<String[]> result = new ArrayList<>();
try {
try (Connection connection = Database.getConnection(false, 1000)) {
if (block == null) {
return result;
}
@ -33,7 +33,6 @@ public class BlockLookupAPI {
checkTime = time - offset;
}
Connection connection = Database.getConnection(false, 1000);
if (connection == null) {
return result;
}
@ -62,7 +61,6 @@ public class BlockLookupAPI {
}
results.close();
statement.close();
connection.close();
}
catch (Exception e) {
e.printStackTrace();

View File

@ -33,8 +33,7 @@ public final class HangingBreakByEntityListener extends Queue implements Listene
class BasicThread implements Runnable {
@Override
public void run() {
try {
Connection connection = Database.getConnection(true);
try (Connection connection = Database.getConnection(true)) {
if (connection != null) {
Statement statement = connection.createStatement();
String blockData = BlockLookup.performLookup(null, statement, block, player, 0, 1, 7);
@ -49,7 +48,6 @@ public final class HangingBreakByEntityListener extends Queue implements Listene
}
statement.close();
connection.close();
}
else {
Chat.sendMessage(player, Color.DARK_AQUA + "CoreProtect " + Color.WHITE + "- " + Phrase.build(Phrase.DATABASE_BUSY));

View File

@ -53,7 +53,6 @@ public final class ArmorStandManipulateListener extends Queue implements Listene
class BasicThread implements Runnable {
@Override
public void run() {
try {
if (ConfigHandler.converterRunning) {
Chat.sendMessage(finalPlayer, Color.DARK_AQUA + "CoreProtect " + Color.WHITE + "- " + Phrase.build(Phrase.UPGRADE_IN_PROGRESS));
return;
@ -71,7 +70,7 @@ public final class ArmorStandManipulateListener extends Queue implements Listene
}
ConfigHandler.lookupThrottle.put(finalPlayer.getName(), new Object[] { true, System.currentTimeMillis() });
Connection connection = Database.getConnection(true);
try (Connection connection = Database.getConnection(true)) {
if (connection != null) {
Statement statement = connection.createStatement();
Location standLocation = armorStand.getLocation();
@ -86,7 +85,6 @@ public final class ArmorStandManipulateListener extends Queue implements Listene
Chat.sendComponent(finalPlayer, blockData);
}
statement.close();
connection.close();
}
else {
Chat.sendMessage(finalPlayer, Color.DARK_AQUA + "CoreProtect " + Color.WHITE + "- " + Phrase.build(Phrase.DATABASE_BUSY));

View File

@ -87,7 +87,6 @@ public final class PlayerInteractListener extends Queue implements Listener {
class BasicThread implements Runnable {
@Override
public void run() {
try {
if (ConfigHandler.converterRunning) {
player.sendMessage(Color.DARK_AQUA + "CoreProtect " + Color.WHITE + "- " + Phrase.build(Phrase.UPGRADE_IN_PROGRESS));
return;
@ -104,7 +103,7 @@ public final class PlayerInteractListener extends Queue implements Listener {
}
}
Connection connection = Database.getConnection(true);
try (Connection connection = Database.getConnection(true)) {
if (connection != null) {
ConfigHandler.lookupThrottle.put(player.getName(), new Object[] { true, System.currentTimeMillis() });
Statement statement = connection.createStatement();
@ -120,7 +119,6 @@ public final class PlayerInteractListener extends Queue implements Listener {
}
statement.close();
connection.close();
ConfigHandler.lookupThrottle.put(player.getName(), new Object[] { false, System.currentTimeMillis() });
if (blockFinal instanceof Sign && player.getGameMode() != GameMode.CREATIVE) {
@ -185,7 +183,6 @@ public final class PlayerInteractListener extends Queue implements Listener {
class BasicThread implements Runnable {
@Override
public void run() {
try {
if (ConfigHandler.converterRunning) {
Chat.sendMessage(player, Color.DARK_AQUA + "CoreProtect " + Color.WHITE + "- " + Phrase.build(Phrase.UPGRADE_IN_PROGRESS));
return;
@ -206,7 +203,7 @@ public final class PlayerInteractListener extends Queue implements Listener {
ConfigHandler.lookupThrottle.put(player.getName(), new Object[] { true, System.currentTimeMillis() });
Connection connection = Database.getConnection(true);
try (Connection connection = Database.getConnection(true)) {
if (connection != null) {
Statement statement = connection.createStatement();
List<String> signData = SignMessageLookup.performLookup(null, statement, location, player, 1, 7);
@ -225,7 +222,6 @@ public final class PlayerInteractListener extends Queue implements Listener {
}
statement.close();
connection.close();
}
else {
Chat.sendMessage(player, Color.DARK_AQUA + "CoreProtect " + Color.WHITE + "- " + Phrase.build(Phrase.DATABASE_BUSY));
@ -269,7 +265,6 @@ public final class PlayerInteractListener extends Queue implements Listener {
class BasicThread implements Runnable {
@Override
public void run() {
try {
if (ConfigHandler.converterRunning) {
Chat.sendMessage(player, Color.DARK_AQUA + "CoreProtect " + Color.WHITE + "- " + Phrase.build(Phrase.UPGRADE_IN_PROGRESS));
return;
@ -290,7 +285,7 @@ public final class PlayerInteractListener extends Queue implements Listener {
ConfigHandler.lookupThrottle.put(player.getName(), new Object[] { true, System.currentTimeMillis() });
Connection connection = Database.getConnection(true);
try (Connection connection = Database.getConnection(true)) {
if (connection != null) {
Statement statement = connection.createStatement();
String blockData = ChestTransactionLookup.performLookup(null, statement, finalLocation, player, 1, 7, false);
@ -305,7 +300,6 @@ public final class PlayerInteractListener extends Queue implements Listener {
}
statement.close();
connection.close();
}
else {
Chat.sendMessage(player, Color.DARK_AQUA + "CoreProtect " + Color.WHITE + "- " + Phrase.build(Phrase.DATABASE_BUSY));
@ -340,7 +334,6 @@ public final class PlayerInteractListener extends Queue implements Listener {
class BasicThread implements Runnable {
@Override
public void run() {
try {
if (ConfigHandler.converterRunning) {
Chat.sendMessage(player, Color.DARK_AQUA + "CoreProtect " + Color.WHITE + "- " + Phrase.build(Phrase.UPGRADE_IN_PROGRESS));
return;
@ -359,7 +352,7 @@ public final class PlayerInteractListener extends Queue implements Listener {
ConfigHandler.lookupThrottle.put(player.getName(), new Object[] { true, System.currentTimeMillis() });
Connection connection = Database.getConnection(true);
try (Connection connection = Database.getConnection(true)) {
if (connection != null) {
Statement statement = connection.createStatement();
String blockData = InteractionLookup.performLookup(null, statement, finalInteractBlock, player, 0, 1, 7);
@ -374,7 +367,6 @@ public final class PlayerInteractListener extends Queue implements Listener {
}
statement.close();
connection.close();
}
else {
Chat.sendMessage(player, Color.DARK_AQUA + "CoreProtect " + Color.WHITE + "- " + Phrase.build(Phrase.DATABASE_BUSY));
@ -421,7 +413,6 @@ public final class PlayerInteractListener extends Queue implements Listener {
class BasicThread implements Runnable {
@Override
public void run() {
try {
if (ConfigHandler.converterRunning) {
Chat.sendMessage(finalPlayer, Color.DARK_AQUA + "CoreProtect " + Color.WHITE + "- " + Phrase.build(Phrase.UPGRADE_IN_PROGRESS));
return;
@ -442,7 +433,7 @@ public final class PlayerInteractListener extends Queue implements Listener {
ConfigHandler.lookupThrottle.put(finalPlayer.getName(), new Object[] { true, System.currentTimeMillis() });
Connection connection = Database.getConnection(true);
try (Connection connection = Database.getConnection(true)) {
if (connection != null) {
Statement statement = connection.createStatement();
if (finalBlock.getType().equals(Material.AIR) || finalBlock.getType().equals(Material.CAVE_AIR)) {
@ -470,7 +461,6 @@ public final class PlayerInteractListener extends Queue implements Listener {
}
statement.close();
connection.close();
}
else {
Chat.sendMessage(finalPlayer, Color.DARK_AQUA + "CoreProtect " + Color.WHITE + "- " + Phrase.build(Phrase.DATABASE_BUSY));

View File

@ -151,10 +151,9 @@ public class Patch {
int result = -1;
patching = true;
try {
try (Connection connection = Database.getConnection(true, 0)) {
boolean patched = false;
boolean allPatches = true;
Connection connection = Database.getConnection(true, 0);
Statement statement = connection.createStatement();
Integer[] newVersion = lastVersion;
@ -221,7 +220,6 @@ public class Patch {
}
statement.close();
connection.close();
}
catch (Exception e) {
e.printStackTrace();