This entirely untested and made on a whim patch may or may not fix database lockups due to seperate threads accessing the db connection at the same time

This commit is contained in:
t00thpick1 2014-07-03 00:46:09 -04:00
parent 2b9b5df1ee
commit 4c4def2a23

View File

@ -13,6 +13,8 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Properties; import java.util.Properties;
import org.bukkit.Bukkit;
import com.gmail.nossr50.mcMMO; import com.gmail.nossr50.mcMMO;
import com.gmail.nossr50.config.Config; import com.gmail.nossr50.config.Config;
import com.gmail.nossr50.datatypes.MobHealthbarType; import com.gmail.nossr50.datatypes.MobHealthbarType;
@ -29,7 +31,8 @@ import com.gmail.nossr50.util.Misc;
public final class SQLDatabaseManager implements DatabaseManager { public final class SQLDatabaseManager implements DatabaseManager {
private String connectionString; private String connectionString;
private String tablePrefix = Config.getInstance().getMySQLTablePrefix(); private String tablePrefix = Config.getInstance().getMySQLTablePrefix();
private Connection connection = null; private Connection primaryConnection = null;
private Connection secondaryConnection = null;
// Scale waiting time by this much per failed attempt // Scale waiting time by this much per failed attempt
private final double SCALING_FACTOR = 40.0; private final double SCALING_FACTOR = 40.0;
@ -199,7 +202,7 @@ public final class SQLDatabaseManager implements DatabaseManager {
PreparedStatement statement = null; PreparedStatement statement = null;
try { try {
statement = connection.prepareStatement("SELECT " + query + ", user, NOW() FROM " + tablePrefix + "users JOIN " + tablePrefix + "skills ON (user_id = id) WHERE " + query + " > 0 ORDER BY " + query + " DESC, user LIMIT ?, ?"); statement = getConnection().prepareStatement("SELECT " + query + ", user, NOW() FROM " + tablePrefix + "users JOIN " + tablePrefix + "skills ON (user_id = id) WHERE " + query + " > 0 ORDER BY " + query + " DESC, user LIMIT ?, ?");
statement.setInt(1, (pageNumber * statsPerPage) - statsPerPage); statement.setInt(1, (pageNumber * statsPerPage) - statsPerPage);
statement.setInt(2, statsPerPage); statement.setInt(2, statsPerPage);
resultSet = statement.executeQuery(); resultSet = statement.executeQuery();
@ -245,7 +248,7 @@ public final class SQLDatabaseManager implements DatabaseManager {
"AND " + skillName + " > (SELECT " + skillName + " FROM " + tablePrefix + "users JOIN " + tablePrefix + "skills ON user_id = id " + "AND " + skillName + " > (SELECT " + skillName + " FROM " + tablePrefix + "users JOIN " + tablePrefix + "skills ON user_id = id " +
"WHERE user = ?)"; "WHERE user = ?)";
PreparedStatement statement = connection.prepareStatement(sql); PreparedStatement statement = getConnection().prepareStatement(sql);
statement.setString(1, playerName); statement.setString(1, playerName);
resultSet = statement.executeQuery(); resultSet = statement.executeQuery();
@ -259,7 +262,7 @@ public final class SQLDatabaseManager implements DatabaseManager {
statement.close(); statement.close();
statement = connection.prepareStatement(sql); statement = getConnection().prepareStatement(sql);
resultSet = statement.executeQuery(); resultSet = statement.executeQuery();
while (resultSet.next()) { while (resultSet.next()) {
@ -278,7 +281,7 @@ public final class SQLDatabaseManager implements DatabaseManager {
"(SELECT taming+mining+woodcutting+repair+unarmed+herbalism+excavation+archery+swords+axes+acrobatics+fishing+alchemy " + "(SELECT taming+mining+woodcutting+repair+unarmed+herbalism+excavation+archery+swords+axes+acrobatics+fishing+alchemy " +
"FROM " + tablePrefix + "users JOIN " + tablePrefix + "skills ON user_id = id WHERE user = ?)"; "FROM " + tablePrefix + "users JOIN " + tablePrefix + "skills ON user_id = id WHERE user = ?)";
PreparedStatement statement = connection.prepareStatement(sql); PreparedStatement statement = getConnection().prepareStatement(sql);
statement.setString(1, playerName); statement.setString(1, playerName);
resultSet = statement.executeQuery(); resultSet = statement.executeQuery();
@ -295,7 +298,7 @@ public final class SQLDatabaseManager implements DatabaseManager {
"(SELECT taming+mining+woodcutting+repair+unarmed+herbalism+excavation+archery+swords+axes+acrobatics+fishing+alchemy " + "(SELECT taming+mining+woodcutting+repair+unarmed+herbalism+excavation+archery+swords+axes+acrobatics+fishing+alchemy " +
"FROM " + tablePrefix + "users JOIN " + tablePrefix + "skills ON user_id = id WHERE user = ?) ORDER BY user"; "FROM " + tablePrefix + "users JOIN " + tablePrefix + "skills ON user_id = id WHERE user = ?) ORDER BY user";
statement = connection.prepareStatement(sql); statement = getConnection().prepareStatement(sql);
statement.setString(1, playerName); statement.setString(1, playerName);
resultSet = statement.executeQuery(); resultSet = statement.executeQuery();
@ -324,7 +327,7 @@ public final class SQLDatabaseManager implements DatabaseManager {
PreparedStatement statement = null; PreparedStatement statement = null;
try { try {
statement = connection.prepareStatement("INSERT INTO " + tablePrefix + "users (user, lastlogin) VALUES (?, ?)", Statement.RETURN_GENERATED_KEYS); statement = getConnection().prepareStatement("INSERT INTO " + tablePrefix + "users (user, lastlogin) VALUES (?, ?)", Statement.RETURN_GENERATED_KEYS);
statement.setString(1, playerName); statement.setString(1, playerName);
statement.setLong(2, System.currentTimeMillis() / Misc.TIME_CONVERSION_FACTOR); statement.setLong(2, System.currentTimeMillis() / Misc.TIME_CONVERSION_FACTOR);
statement.execute(); statement.execute();
@ -359,7 +362,7 @@ public final class SQLDatabaseManager implements DatabaseManager {
PreparedStatement statement = null; PreparedStatement statement = null;
try { try {
statement = connection.prepareStatement( statement = getConnection().prepareStatement(
"SELECT " "SELECT "
+ "s.taming, s.mining, s.repair, s.woodcutting, s.unarmed, s.herbalism, s.excavation, s.archery, s.swords, s.axes, s.acrobatics, s.fishing, s.alchemy, " + "s.taming, s.mining, s.repair, s.woodcutting, s.unarmed, s.herbalism, s.excavation, s.archery, s.swords, s.axes, s.acrobatics, s.fishing, s.alchemy, "
+ "e.taming, e.mining, e.repair, e.woodcutting, e.unarmed, e.herbalism, e.excavation, e.archery, e.swords, e.axes, e.acrobatics, e.fishing, e.alchemy, " + "e.taming, e.mining, e.repair, e.woodcutting, e.unarmed, e.herbalism, e.excavation, e.archery, e.swords, e.axes, e.acrobatics, e.fishing, e.alchemy, "
@ -435,7 +438,7 @@ public final class SQLDatabaseManager implements DatabaseManager {
PreparedStatement statement = null; PreparedStatement statement = null;
try { try {
statement = connection.prepareStatement( statement = getConnection().prepareStatement(
"SELECT " "SELECT "
+ "s.taming, s.mining, s.repair, s.woodcutting, s.unarmed, s.herbalism, s.excavation, s.archery, s.swords, s.axes, s.acrobatics, s.fishing, s.alchemy, " + "s.taming, s.mining, s.repair, s.woodcutting, s.unarmed, s.herbalism, s.excavation, s.archery, s.swords, s.axes, s.acrobatics, s.fishing, s.alchemy, "
+ "e.taming, e.mining, e.repair, e.woodcutting, e.unarmed, e.herbalism, e.excavation, e.archery, e.swords, e.axes, e.acrobatics, e.fishing, e.alchemy, " + "e.taming, e.mining, e.repair, e.woodcutting, e.unarmed, e.herbalism, e.excavation, e.archery, e.swords, e.axes, e.acrobatics, e.fishing, e.alchemy, "
@ -500,6 +503,7 @@ public final class SQLDatabaseManager implements DatabaseManager {
public boolean checkConnected() { public boolean checkConnected() {
boolean isClosed = true; boolean isClosed = true;
boolean isValid = false; boolean isValid = false;
Connection connection = getConnection();
boolean exists = (connection != null); boolean exists = (connection != null);
// If we're waiting for server to recover then leave early // If we're waiting for server to recover then leave early
@ -547,7 +551,11 @@ public final class SQLDatabaseManager implements DatabaseManager {
} }
// Try to connect again // Try to connect again
connect(); if (Bukkit.isPrimaryThread()) {
connectPrimary();
} else {
connectSecondary();
}
// Leave if connection is good // Leave if connection is good
try { try {
@ -572,13 +580,47 @@ public final class SQLDatabaseManager implements DatabaseManager {
return false; return false;
} }
private void connectSecondary() {
connectionString = "jdbc:mysql://" + Config.getInstance().getMySQLServerName() + ":" + Config.getInstance().getMySQLServerPort() + "/" + Config.getInstance().getMySQLDatabaseName();
try {
mcMMO.p.getLogger().info("Attempting connection to MySQL...");
// Force driver to load if not yet loaded
Class.forName("com.mysql.jdbc.Driver");
Properties connectionProperties = new Properties();
connectionProperties.put("user", Config.getInstance().getMySQLUserName());
connectionProperties.put("password", Config.getInstance().getMySQLUserPassword());
connectionProperties.put("autoReconnect", "false");
connectionProperties.put("maxReconnects", "0");
secondaryConnection = DriverManager.getConnection(connectionString, connectionProperties);
mcMMO.p.getLogger().info("Connection to MySQL was a success!");
}
catch (SQLException ex) {
secondaryConnection = null;
if (reconnectAttempt == 0 || reconnectAttempt >= 11) {
mcMMO.p.getLogger().severe("Connection to MySQL failed!");
printErrors(ex);
}
}
catch (ClassNotFoundException ex) {
secondaryConnection = null;
if (reconnectAttempt == 0 || reconnectAttempt >= 11) {
mcMMO.p.getLogger().severe("MySQL database driver not found!");
}
}
}
public List<String> getStoredUsers() { public List<String> getStoredUsers() {
ArrayList<String> users = new ArrayList<String>(); ArrayList<String> users = new ArrayList<String>();
if (checkConnected()) { if (checkConnected()) {
Statement stmt = null; Statement stmt = null;
try { try {
stmt = connection.createStatement(); stmt = getConnection().createStatement();
ResultSet result = stmt.executeQuery("SELECT user FROM " + tablePrefix + "users"); ResultSet result = stmt.executeQuery("SELECT user FROM " + tablePrefix + "users");
while (result.next()) { while (result.next()) {
users.add(result.getString("user")); users.add(result.getString("user"));
@ -603,10 +645,18 @@ public final class SQLDatabaseManager implements DatabaseManager {
return users; return users;
} }
private Connection getConnection() {
if (Bukkit.isPrimaryThread()) {
return primaryConnection;
} else {
return secondaryConnection;
}
}
/** /**
* Attempt to connect to the mySQL database. * Attempt to connect to the mySQL database.
*/ */
private void connect() { private void connectPrimary() {
connectionString = "jdbc:mysql://" + Config.getInstance().getMySQLServerName() + ":" + Config.getInstance().getMySQLServerPort() + "/" + Config.getInstance().getMySQLDatabaseName(); connectionString = "jdbc:mysql://" + Config.getInstance().getMySQLServerName() + ":" + Config.getInstance().getMySQLServerPort() + "/" + Config.getInstance().getMySQLDatabaseName();
try { try {
@ -619,12 +669,12 @@ public final class SQLDatabaseManager implements DatabaseManager {
connectionProperties.put("password", Config.getInstance().getMySQLUserPassword()); connectionProperties.put("password", Config.getInstance().getMySQLUserPassword());
connectionProperties.put("autoReconnect", "false"); connectionProperties.put("autoReconnect", "false");
connectionProperties.put("maxReconnects", "0"); connectionProperties.put("maxReconnects", "0");
connection = DriverManager.getConnection(connectionString, connectionProperties); primaryConnection = DriverManager.getConnection(connectionString, connectionProperties);
mcMMO.p.getLogger().info("Connection to MySQL was a success!"); mcMMO.p.getLogger().info("Connection to MySQL was a success!");
} }
catch (SQLException ex) { catch (SQLException ex) {
connection = null; primaryConnection = null;
if (reconnectAttempt == 0 || reconnectAttempt >= 11) { if (reconnectAttempt == 0 || reconnectAttempt >= 11) {
mcMMO.p.getLogger().severe("Connection to MySQL failed!"); mcMMO.p.getLogger().severe("Connection to MySQL failed!");
@ -632,7 +682,7 @@ public final class SQLDatabaseManager implements DatabaseManager {
} }
} }
catch (ClassNotFoundException ex) { catch (ClassNotFoundException ex) {
connection = null; primaryConnection = null;
if (reconnectAttempt == 0 || reconnectAttempt >= 11) { if (reconnectAttempt == 0 || reconnectAttempt >= 11) {
mcMMO.p.getLogger().severe("MySQL database driver not found!"); mcMMO.p.getLogger().severe("MySQL database driver not found!");
@ -806,7 +856,7 @@ public final class SQLDatabaseManager implements DatabaseManager {
return; return;
} }
statement = connection.prepareStatement(sql); statement = getConnection().prepareStatement(sql);
resultSet = statement.executeQuery(); resultSet = statement.executeQuery();
while (resultSet.next()) { while (resultSet.next()) {
@ -866,7 +916,7 @@ public final class SQLDatabaseManager implements DatabaseManager {
PreparedStatement statement = null; PreparedStatement statement = null;
try { try {
statement = connection.prepareStatement(sql); statement = getConnection().prepareStatement(sql);
statement.executeUpdate(); statement.executeUpdate();
return true; return true;
} }
@ -901,7 +951,7 @@ public final class SQLDatabaseManager implements DatabaseManager {
PreparedStatement statement = null; PreparedStatement statement = null;
try { try {
statement = connection.prepareStatement(sql); statement = getConnection().prepareStatement(sql);
rows = statement.executeUpdate(); rows = statement.executeUpdate();
} }
catch (SQLException ex) { catch (SQLException ex) {
@ -936,7 +986,7 @@ public final class SQLDatabaseManager implements DatabaseManager {
ResultSet resultSet; ResultSet resultSet;
try { try {
statement = connection.prepareStatement(sql); statement = getConnection().prepareStatement(sql);
resultSet = statement.executeQuery(); resultSet = statement.executeQuery();
while (resultSet.next()) { while (resultSet.next()) {
@ -1008,22 +1058,22 @@ public final class SQLDatabaseManager implements DatabaseManager {
PreparedStatement statement = null; PreparedStatement statement = null;
try { try {
statement = connection.prepareStatement("INSERT IGNORE INTO " + tablePrefix + "experience (user_id) VALUES (?)"); statement = getConnection().prepareStatement("INSERT IGNORE INTO " + tablePrefix + "experience (user_id) VALUES (?)");
statement.setInt(1, id); statement.setInt(1, id);
statement.execute(); statement.execute();
statement.close(); statement.close();
statement = connection.prepareStatement("INSERT IGNORE INTO " + tablePrefix + "skills (user_id) VALUES (?)"); statement = getConnection().prepareStatement("INSERT IGNORE INTO " + tablePrefix + "skills (user_id) VALUES (?)");
statement.setInt(1, id); statement.setInt(1, id);
statement.execute(); statement.execute();
statement.close(); statement.close();
statement = connection.prepareStatement("INSERT IGNORE INTO " + tablePrefix + "cooldowns (user_id) VALUES (?)"); statement = getConnection().prepareStatement("INSERT IGNORE INTO " + tablePrefix + "cooldowns (user_id) VALUES (?)");
statement.setInt(1, id); statement.setInt(1, id);
statement.execute(); statement.execute();
statement.close(); statement.close();
statement = connection.prepareStatement("INSERT IGNORE INTO " + tablePrefix + "huds (user_id, mobhealthbar) VALUES (? ,'" + Config.getInstance().getMobHealthbarDefault().name() + "')"); statement = getConnection().prepareStatement("INSERT IGNORE INTO " + tablePrefix + "huds (user_id, mobhealthbar) VALUES (? ,'" + Config.getInstance().getMobHealthbarDefault().name() + "')");
statement.setInt(1, id); statement.setInt(1, id);
statement.execute(); statement.execute();
statement.close(); statement.close();
@ -1053,7 +1103,7 @@ public final class SQLDatabaseManager implements DatabaseManager {
PreparedStatement statement = null; PreparedStatement statement = null;
try { try {
statement = connection.prepareStatement(sql); statement = getConnection().prepareStatement(sql);
int i = 1; int i = 1;
for (int arg : args) { for (int arg : args) {
@ -1083,7 +1133,7 @@ public final class SQLDatabaseManager implements DatabaseManager {
PreparedStatement statement = null; PreparedStatement statement = null;
try { try {
statement = connection.prepareStatement(sql); statement = getConnection().prepareStatement(sql);
int i = 1; int i = 1;
for (long arg : args) { for (long arg : args) {
@ -1120,7 +1170,7 @@ public final class SQLDatabaseManager implements DatabaseManager {
int id = -1; int id = -1;
try { try {
PreparedStatement statement = connection.prepareStatement("SELECT id FROM " + tablePrefix + "users WHERE user = ?"); PreparedStatement statement = getConnection().prepareStatement("SELECT id FROM " + tablePrefix + "users WHERE user = ?");
statement.setString(1, playerName); statement.setString(1, playerName);
id = readInt(statement); id = readInt(statement);
} }
@ -1135,7 +1185,7 @@ public final class SQLDatabaseManager implements DatabaseManager {
PreparedStatement statement = null; PreparedStatement statement = null;
try { try {
statement = connection.prepareStatement("UPDATE " + tablePrefix + "users SET lastlogin = ? WHERE id = ?"); statement = getConnection().prepareStatement("UPDATE " + tablePrefix + "users SET lastlogin = ? WHERE id = ?");
statement.setLong(1, login); statement.setLong(1, login);
statement.setInt(2, id); statement.setInt(2, id);
statement.execute(); statement.execute();
@ -1161,7 +1211,7 @@ public final class SQLDatabaseManager implements DatabaseManager {
PreparedStatement statement = null; PreparedStatement statement = null;
try { try {
statement = connection.prepareStatement("UPDATE " + tablePrefix + "huds SET mobhealthbar = ? WHERE user_id = ?"); statement = getConnection().prepareStatement("UPDATE " + tablePrefix + "huds SET mobhealthbar = ? WHERE user_id = ?");
statement.setString(1, mobHealthBar); statement.setString(1, mobHealthBar);
statement.setInt(2, userId); statement.setInt(2, userId);
statement.execute(); statement.execute();