Fix MySQL/MariaDB round-stats SQL (GREATEST vs MAX) and throttle DB failure logging
Build / build (push) Successful in 1m14s
Build / build (push) Successful in 1m14s
This commit is contained in:
@@ -10,7 +10,10 @@ import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.logging.Level;
|
||||
|
||||
/** SQLite- or MySQL-backed statistics storage, selected via storage.type in config.yml.
|
||||
@@ -18,17 +21,39 @@ import java.util.logging.Level;
|
||||
* them off the main thread (see StatsManager). */
|
||||
public class StatsDatabase {
|
||||
|
||||
private static final long ERROR_LOG_INTERVAL_MS = TimeUnit.MINUTES.toMillis(5);
|
||||
|
||||
private final Plugin plugin;
|
||||
private final StorageSettings settings;
|
||||
private final String table;
|
||||
private Connection connection;
|
||||
|
||||
/** operation name -> last time we actually logged a failure for it, so a persistently
|
||||
* broken database (bad SQL, dropped connection, etc.) logs once immediately for
|
||||
* diagnosis and then at most once every {@link #ERROR_LOG_INTERVAL_MS} after that,
|
||||
* instead of spamming a full stack trace on every round/query. */
|
||||
private final Map<String, Long> lastLoggedAt = new ConcurrentHashMap<>();
|
||||
|
||||
public StatsDatabase(Plugin plugin, StorageSettings settings) {
|
||||
this.plugin = plugin;
|
||||
this.settings = settings;
|
||||
this.table = settings.isMysql() ? settings.tablePrefix() + "player_stats" : "player_stats";
|
||||
}
|
||||
|
||||
private void logFailure(String operation, UUID uuid, SQLException ex) {
|
||||
long now = System.currentTimeMillis();
|
||||
Long last = lastLoggedAt.get(operation);
|
||||
if (last == null) {
|
||||
plugin.getLogger().log(Level.WARNING, "Failed to " + operation + " for " + uuid
|
||||
+ " (further repeats of this error are throttled to once per 5 minutes)", ex);
|
||||
lastLoggedAt.put(operation, now);
|
||||
} else if (now - last >= ERROR_LOG_INTERVAL_MS) {
|
||||
plugin.getLogger().warning("Still failing to " + operation + ": "
|
||||
+ ex.getClass().getSimpleName() + ": " + ex.getMessage());
|
||||
lastLoggedAt.put(operation, now);
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void connect() {
|
||||
try {
|
||||
if (settings.isMysql()) {
|
||||
@@ -119,7 +144,7 @@ public class StatsDatabase {
|
||||
}
|
||||
}
|
||||
} catch (SQLException ex) {
|
||||
plugin.getLogger().log(Level.WARNING, "Failed to read stats for " + uuid, ex);
|
||||
logFailure("read stats", uuid, ex);
|
||||
}
|
||||
return PlayerStats.empty(uuid);
|
||||
}
|
||||
@@ -142,14 +167,17 @@ public class StatsDatabase {
|
||||
}
|
||||
try {
|
||||
ensureRow(uuid.toString());
|
||||
// MySQL/MariaDB's MAX() is aggregate-only; the two-argument "greatest of these
|
||||
// values" form used by SQLite must be GREATEST() on that dialect instead.
|
||||
String greatestFn = settings.isMysql() ? "GREATEST" : "MAX";
|
||||
try (PreparedStatement ps = connection.prepareStatement(
|
||||
"UPDATE " + table + " SET best_round = MAX(best_round, ?), total_rounds_survived = total_rounds_survived + 1 WHERE uuid=?")) {
|
||||
"UPDATE " + table + " SET best_round = " + greatestFn + "(best_round, ?), total_rounds_survived = total_rounds_survived + 1 WHERE uuid=?")) {
|
||||
ps.setInt(1, round);
|
||||
ps.setString(2, uuid.toString());
|
||||
ps.executeUpdate();
|
||||
}
|
||||
} catch (SQLException ex) {
|
||||
plugin.getLogger().log(Level.WARNING, "Failed to update round stats for " + uuid, ex);
|
||||
logFailure("update round stats", uuid, ex);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -165,7 +193,7 @@ public class StatsDatabase {
|
||||
ps.executeUpdate();
|
||||
}
|
||||
} catch (SQLException ex) {
|
||||
plugin.getLogger().log(Level.WARNING, "Failed to update stats for " + uuid, ex);
|
||||
logFailure("update stats (" + setClause + ")", uuid, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user