Fixed MySQL transaction timeout caused by CleanTask

This commit is contained in:
Rsl1122 2019-04-01 20:47:39 +03:00
parent fb7e752300
commit 52841f3724
3 changed files with 70 additions and 75 deletions

View File

@ -19,25 +19,14 @@ package com.djrapitops.plan.db.access.transactions.init;
import com.djrapitops.plan.data.store.objects.DateObj; import com.djrapitops.plan.data.store.objects.DateObj;
import com.djrapitops.plan.db.access.ExecStatement; import com.djrapitops.plan.db.access.ExecStatement;
import com.djrapitops.plan.db.access.Executable; import com.djrapitops.plan.db.access.Executable;
import com.djrapitops.plan.db.access.Query;
import com.djrapitops.plan.db.access.QueryStatement;
import com.djrapitops.plan.db.access.queries.objects.TPSQueries; import com.djrapitops.plan.db.access.queries.objects.TPSQueries;
import com.djrapitops.plan.db.access.transactions.Transaction; import com.djrapitops.plan.db.access.transactions.Transaction;
import com.djrapitops.plan.db.access.transactions.commands.RemovePlayerTransaction;
import com.djrapitops.plan.db.sql.tables.PingTable; import com.djrapitops.plan.db.sql.tables.PingTable;
import com.djrapitops.plan.db.sql.tables.SessionsTable;
import com.djrapitops.plan.db.sql.tables.TPSTable; import com.djrapitops.plan.db.sql.tables.TPSTable;
import com.djrapitops.plan.extension.implementation.storage.transactions.results.RemoveUnsatisfiedConditionalResultsTransaction;
import com.djrapitops.plan.system.locale.Locale;
import com.djrapitops.plan.system.locale.lang.PluginLang;
import com.djrapitops.plugin.api.TimeAmount; import com.djrapitops.plugin.api.TimeAmount;
import com.djrapitops.plugin.logging.console.PluginLogger;
import java.sql.PreparedStatement; import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.UUID; import java.util.UUID;
@ -46,25 +35,14 @@ import java.util.UUID;
* *
* @author Rsl1122 * @author Rsl1122
*/ */
public class CleanTransaction extends Transaction { public class RemoveOldSampledDataTransaction extends Transaction {
private final UUID serverUUID; private final UUID serverUUID;
private final long keepInactiveForMs; public RemoveOldSampledDataTransaction(
UUID serverUUID
private final PluginLogger logger;
private final Locale locale;
public CleanTransaction(
UUID serverUUID,
long keepInactiveForMs,
PluginLogger logger,
Locale locale
) { ) {
this.serverUUID = serverUUID; this.serverUUID = serverUUID;
this.keepInactiveForMs = keepInactiveForMs;
this.logger = logger;
this.locale = locale;
} }
@Override @Override
@ -73,25 +51,6 @@ public class CleanTransaction extends Transaction {
execute(cleanTPSTable(allTimePeak.orElse(-1))); execute(cleanTPSTable(allTimePeak.orElse(-1)));
execute(cleanPingTable()); execute(cleanPingTable());
// Clean DataExtension data
executeOther(new RemoveUnsatisfiedConditionalResultsTransaction());
int removed = cleanOldPlayers();
if (removed > 0) {
logger.info(locale.getString(PluginLang.DB_NOTIFY_CLEAN, removed));
}
}
private int cleanOldPlayers() {
long now = System.currentTimeMillis();
long keepActiveAfter = now - keepInactiveForMs;
List<UUID> inactivePlayers = query(fetchInactivePlayerUUIDs(keepActiveAfter));
for (UUID uuid : inactivePlayers) {
executeOther(new RemovePlayerTransaction(uuid));
}
return inactivePlayers.size();
} }
private Executable cleanTPSTable(int allTimePlayerPeak) { private Executable cleanTPSTable(int allTimePlayerPeak) {
@ -123,28 +82,4 @@ public class CleanTransaction extends Transaction {
} }
}; };
} }
private Query<List<UUID>> fetchInactivePlayerUUIDs(long keepActiveAfter) {
String sql = "SELECT uuid, last_seen FROM (SELECT" +
" MAX(" + SessionsTable.SESSION_END + ") as last_seen, " + SessionsTable.USER_UUID +
" FROM " + SessionsTable.TABLE_NAME +
" GROUP BY " + SessionsTable.USER_UUID + ") as q1" +
" WHERE last_seen < ?";
return new QueryStatement<List<UUID>>(sql, 20000) {
@Override
public void prepare(PreparedStatement statement) throws SQLException {
statement.setLong(1, keepActiveAfter);
}
@Override
public List<UUID> processResults(ResultSet set) throws SQLException {
List<UUID> inactiveUUIDs = new ArrayList<>();
while (set.next()) {
inactiveUUIDs.add(UUID.fromString(set.getString("uuid")));
}
return inactiveUUIDs;
}
};
}
} }

View File

@ -18,19 +18,32 @@ package com.djrapitops.plan.db.tasks;
import com.djrapitops.plan.api.exceptions.database.DBOpException; import com.djrapitops.plan.api.exceptions.database.DBOpException;
import com.djrapitops.plan.db.Database; import com.djrapitops.plan.db.Database;
import com.djrapitops.plan.db.access.transactions.init.CleanTransaction; import com.djrapitops.plan.db.access.Query;
import com.djrapitops.plan.db.access.QueryStatement;
import com.djrapitops.plan.db.access.transactions.commands.RemovePlayerTransaction;
import com.djrapitops.plan.db.access.transactions.init.RemoveOldSampledDataTransaction;
import com.djrapitops.plan.db.sql.tables.SessionsTable;
import com.djrapitops.plan.extension.implementation.storage.transactions.results.RemoveUnsatisfiedConditionalResultsTransaction;
import com.djrapitops.plan.system.database.DBSystem; import com.djrapitops.plan.system.database.DBSystem;
import com.djrapitops.plan.system.info.server.ServerInfo; import com.djrapitops.plan.system.info.server.ServerInfo;
import com.djrapitops.plan.system.locale.Locale; import com.djrapitops.plan.system.locale.Locale;
import com.djrapitops.plan.system.locale.lang.PluginLang;
import com.djrapitops.plan.system.settings.config.PlanConfig; import com.djrapitops.plan.system.settings.config.PlanConfig;
import com.djrapitops.plan.system.settings.paths.TimeSettings; import com.djrapitops.plan.system.settings.paths.TimeSettings;
import com.djrapitops.plugin.logging.L; import com.djrapitops.plugin.logging.L;
import com.djrapitops.plugin.logging.console.PluginLogger; import com.djrapitops.plugin.logging.console.PluginLogger;
import com.djrapitops.plugin.logging.error.ErrorHandler; import com.djrapitops.plugin.logging.error.ErrorHandler;
import com.djrapitops.plugin.task.AbsRunnable; import com.djrapitops.plugin.task.AbsRunnable;
import com.google.common.annotations.VisibleForTesting;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Singleton; import javax.inject.Singleton;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
/** /**
* Task for cleaning the active database. * Task for cleaning the active database.
@ -70,13 +83,52 @@ public class DBCleanTask extends AbsRunnable {
Database database = dbSystem.getDatabase(); Database database = dbSystem.getDatabase();
try { try {
if (database.getState() != Database.State.CLOSED) { if (database.getState() != Database.State.CLOSED) {
database.executeTransaction(new CleanTransaction(serverInfo.getServerUUID(), database.executeTransaction(new RemoveOldSampledDataTransaction(serverInfo.getServerUUID()));
config.get(TimeSettings.KEEP_INACTIVE_PLAYERS), logger, locale) database.executeTransaction(new RemoveUnsatisfiedConditionalResultsTransaction());
); int removed = cleanOldPlayers(database);
if (removed > 0) {
logger.info(locale.getString(PluginLang.DB_NOTIFY_CLEAN, removed));
}
} }
} catch (DBOpException e) { } catch (DBOpException e) {
errorHandler.log(L.ERROR, this.getClass(), e); errorHandler.log(L.ERROR, this.getClass(), e);
cancel(); cancel();
} }
} }
@VisibleForTesting
public int cleanOldPlayers(Database database) {
long now = System.currentTimeMillis();
long keepActiveAfter = now - config.get(TimeSettings.KEEP_INACTIVE_PLAYERS);
List<UUID> inactivePlayers = database.query(fetchInactivePlayerUUIDs(keepActiveAfter));
for (UUID uuid : inactivePlayers) {
database.executeTransaction(new RemovePlayerTransaction(uuid));
}
return inactivePlayers.size();
}
private Query<List<UUID>> fetchInactivePlayerUUIDs(long keepActiveAfter) {
String sql = "SELECT uuid, last_seen FROM (SELECT" +
" MAX(" + SessionsTable.SESSION_END + ") as last_seen, " + SessionsTable.USER_UUID +
" FROM " + SessionsTable.TABLE_NAME +
" GROUP BY " + SessionsTable.USER_UUID + ") as q1" +
" WHERE last_seen < ?";
return new QueryStatement<List<UUID>>(sql, 20000) {
@Override
public void prepare(PreparedStatement statement) throws SQLException {
statement.setLong(1, keepActiveAfter);
}
@Override
public List<UUID> processResults(ResultSet set) throws SQLException {
List<UUID> inactiveUUIDs = new ArrayList<>();
while (set.next()) {
inactiveUUIDs.add(UUID.fromString(set.getString("uuid")));
}
return inactiveUUIDs;
}
};
}
} }

View File

@ -42,10 +42,10 @@ import com.djrapitops.plan.db.access.transactions.StoreServerInformationTransact
import com.djrapitops.plan.db.access.transactions.Transaction; import com.djrapitops.plan.db.access.transactions.Transaction;
import com.djrapitops.plan.db.access.transactions.commands.*; import com.djrapitops.plan.db.access.transactions.commands.*;
import com.djrapitops.plan.db.access.transactions.events.*; import com.djrapitops.plan.db.access.transactions.events.*;
import com.djrapitops.plan.db.access.transactions.init.CleanTransaction;
import com.djrapitops.plan.db.access.transactions.init.CreateIndexTransaction; import com.djrapitops.plan.db.access.transactions.init.CreateIndexTransaction;
import com.djrapitops.plan.db.access.transactions.init.CreateTablesTransaction; import com.djrapitops.plan.db.access.transactions.init.CreateTablesTransaction;
import com.djrapitops.plan.db.patches.Patch; import com.djrapitops.plan.db.patches.Patch;
import com.djrapitops.plan.db.tasks.DBCleanTask;
import com.djrapitops.plan.extension.CallEvents; import com.djrapitops.plan.extension.CallEvents;
import com.djrapitops.plan.extension.DataExtension; import com.djrapitops.plan.extension.DataExtension;
import com.djrapitops.plan.extension.ExtensionServiceImplementation; import com.djrapitops.plan.extension.ExtensionServiceImplementation;
@ -69,6 +69,7 @@ import com.djrapitops.plan.system.settings.paths.WebserverSettings;
import com.djrapitops.plan.utilities.SHA256Hash; import com.djrapitops.plan.utilities.SHA256Hash;
import com.djrapitops.plan.utilities.comparators.DateHolderRecentComparator; import com.djrapitops.plan.utilities.comparators.DateHolderRecentComparator;
import com.djrapitops.plugin.logging.console.TestPluginLogger; import com.djrapitops.plugin.logging.console.TestPluginLogger;
import com.djrapitops.plugin.logging.error.ConsoleErrorLogger;
import com.google.common.util.concurrent.MoreExecutors; import com.google.common.util.concurrent.MoreExecutors;
import org.junit.*; import org.junit.*;
import org.junit.rules.TemporaryFolder; import org.junit.rules.TemporaryFolder;
@ -608,7 +609,7 @@ public abstract class CommonDBTest {
} }
@Test @Test
public void cleanTransactionDoesNotCleanActivePlayers() { public void cleanDoesNotCleanActivePlayers() {
saveUserOne(); saveUserOne();
saveTwoWorlds(); saveTwoWorlds();
@ -617,7 +618,14 @@ public abstract class CommonDBTest {
session.endSession(sessionStart + 22345L); session.endSession(sessionStart + 22345L);
execute(DataStoreQueries.storeSession(session)); execute(DataStoreQueries.storeSession(session));
db.executeTransaction(new CleanTransaction(serverUUID, TimeUnit.DAYS.toMillis(1L), new TestPluginLogger(), new Locale())); new DBCleanTask(
system.getConfigSystem().getConfig(),
new Locale(),
system.getDatabaseSystem(),
system.getServerInfo(),
new TestPluginLogger(),
new ConsoleErrorLogger(new TestPluginLogger())
).cleanOldPlayers(db);
Collection<BaseUser> found = db.query(BaseUserQueries.fetchServerBaseUsers(serverUUID)); Collection<BaseUser> found = db.query(BaseUserQueries.fetchServerBaseUsers(serverUUID));
assertFalse("All users were deleted!! D:", found.isEmpty()); assertFalse("All users were deleted!! D:", found.isEmpty());