Clean up MySQL a little bit

This commit is contained in:
AppleDash 2016-09-19 02:22:30 -04:00
parent 9803b795fd
commit 6642c5b703
3 changed files with 103 additions and 57 deletions

View File

@ -3,6 +3,7 @@ package org.appledash.saneeconomy.economy.backend.type;
import org.appledash.saneeconomy.SaneEconomy;
import org.appledash.saneeconomy.economy.economable.Economable;
import org.appledash.saneeconomy.utils.DatabaseCredentials;
import org.appledash.saneeconomy.utils.MySQLConnection;
import org.bukkit.Bukkit;
import java.sql.*;
@ -18,39 +19,14 @@ import java.util.concurrent.ConcurrentHashMap;
* Blackjack is still best pony.
*/
public class EconomyStorageBackendMySQL extends EconomyStorageBackendCaching {
private final Set<String> writingUsers = Collections.newSetFromMap(new ConcurrentHashMap<>());
private final DatabaseCredentials dbCredentials;
private final MySQLConnection dbConn;
public EconomyStorageBackendMySQL(DatabaseCredentials dbCredentials) {
this.dbCredentials = dbCredentials;
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e) {
throw new RuntimeException("No MySQL driver found.", e);
}
}
private Connection openConnection() {
try {
return DriverManager.getConnection(dbCredentials.getJDBCURL(), dbCredentials.getUsername(), dbCredentials.getPassword());
} catch (SQLException e) {
throw new RuntimeException("Database unavailable.", e);
}
}
public boolean testConnection() {
try (Connection ignored = openConnection()) {
createTables();
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
this.dbConn = new MySQLConnection(dbCredentials);
}
private void createTables() {
try (Connection conn = openConnection()) {
try (Connection conn = dbConn.openConnection()) {
int schemaVersion;
if (!checkTableExists("saneeconomy_schema")) {
if (checkTableExists("player_balances")) {
@ -108,9 +84,9 @@ public class EconomyStorageBackendMySQL extends EconomyStorageBackendCaching {
}
private boolean checkTableExists(String tableName) {
try (Connection conn = openConnection()) {
try (Connection conn = dbConn.openConnection()) {
PreparedStatement ps = conn.prepareStatement("SELECT * FROM information_schema.tables WHERE table_schema = ? AND table_name = ? LIMIT 1");
ps.setString(1, dbCredentials.getDatabaseName());
ps.setString(1, dbConn.getCredentials().getDatabaseName());
ps.setString(2, tableName);
ps.executeQuery();
ResultSet rs = ps.getResultSet();
@ -123,7 +99,8 @@ public class EconomyStorageBackendMySQL extends EconomyStorageBackendCaching {
@Override
public void reloadDatabase() {
try (Connection conn = openConnection()) {
createTables();
try (Connection conn = dbConn.openConnection()) {
PreparedStatement ps = conn.prepareStatement("SELECT * FROM `saneeconomy_balances`");
ResultSet rs = ps.executeQuery();
@ -142,21 +119,16 @@ public class EconomyStorageBackendMySQL extends EconomyStorageBackendCaching {
final double oldBalance = getBalance(economable);
balances.put(economable.getUniqueIdentifier(), newBalance);
Bukkit.getServer().getScheduler().scheduleAsyncDelayedTask(SaneEconomy.getInstance(), () -> {
waitForSlot(); // Don't run too many database operations at once.
writingUsers.add(economable.getUniqueIdentifier());
try (Connection conn = openConnection()) {
dbConn.executeAsyncOperation((conn) -> {
try {
ensureAccountExists(economable, conn);
PreparedStatement statement = conn.prepareStatement("UPDATE `saneeconomy_balances` SET balance = ? WHERE `unique_identifier` = ?");
statement.setDouble(1, newBalance);
statement.setString(2, economable.getUniqueIdentifier());
statement.executeUpdate();
} catch (SQLException e) {
/* Roll it back */
} catch (Exception e) {
balances.put(economable.getUniqueIdentifier(), oldBalance);
throw new RuntimeException("SQL error has occurred.", e);
} finally {
writingUsers.remove(economable.getUniqueIdentifier());
}
});
}
@ -178,24 +150,12 @@ public class EconomyStorageBackendMySQL extends EconomyStorageBackendCaching {
return rs.next();
}
private void waitForSlot() {
while (writingUsers.size() >= 5) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
@Override
public void waitUntilFlushed() {
while (!writingUsers.isEmpty()) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
dbConn.waitUntilFlushed();
}
public MySQLConnection getConnection() {
return dbConn;
}
}

View File

@ -0,0 +1,86 @@
package org.appledash.saneeconomy.utils;
import org.appledash.saneeconomy.SaneEconomy;
import org.bukkit.Bukkit;
import sun.misc.Unsafe;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
/**
* Created by appledash on 9/19/16.
* Blackjack is best pony.
*/
public class MySQLConnection {
private static final int MAX_OPEN_TRANSACTIONS = 5;
private final DatabaseCredentials dbCredentials;
private final AtomicInteger openTransactions = new AtomicInteger(0);
public MySQLConnection(DatabaseCredentials dbCredentials) {
this.dbCredentials = dbCredentials;
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e) {
throw new RuntimeException("No MySQL driver found.", e);
}
}
public Connection openConnection() {
try {
return DriverManager.getConnection(dbCredentials.getJDBCURL(), dbCredentials.getUsername(), dbCredentials.getPassword());
} catch (SQLException e) {
throw new RuntimeException("Database unavailable.", e);
}
}
public boolean testConnection() {
try (Connection ignored = openConnection()) {
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
public void executeAsyncOperation(Consumer<Connection> callback) {
Bukkit.getServer().getScheduler().scheduleAsyncDelayedTask(SaneEconomy.getInstance(), () -> {
waitForSlot();
openTransactions.incrementAndGet();
try (Connection conn = openConnection()) {
callback.accept(conn);
} catch (Exception e) {
throw new RuntimeException("This shouldn't happen (database error)", e);
} finally {
openTransactions.decrementAndGet();
}
});
}
public DatabaseCredentials getCredentials() {
return dbCredentials;
}
private void waitForSlot() {
while (openTransactions.get() >= MAX_OPEN_TRANSACTIONS) {
try {
Thread.sleep(50);
} catch (InterruptedException ignored) {
}
}
}
public void waitUntilFlushed() {
while (openTransactions.get() > 0) {
try {
Thread.sleep(50);
} catch (InterruptedException ignored) {
}
}
}
}

View File

@ -85,7 +85,7 @@ public class SaneEconomyConfiguration {
logger.info("Initialized MySQL backend.");
logger.info("Testing connection...");
if (!mySQLBackend.testConnection()) {
if (!mySQLBackend.getConnection().testConnection()) {
logger.severe("MySQL connection failed - cannot continue!");
return null;
}