diff --git a/Plan/common/src/main/java/com/djrapitops/plan/settings/config/paths/TimeSettings.java b/Plan/common/src/main/java/com/djrapitops/plan/settings/config/paths/TimeSettings.java index a2f265f72..d0f41aad7 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/settings/config/paths/TimeSettings.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/settings/config/paths/TimeSettings.java @@ -36,6 +36,7 @@ public class TimeSettings { public static final Setting DELETE_INACTIVE_PLAYERS_AFTER = new TimeSetting("Time.Thresholds.Remove_inactive_player_data_after"); public static final Setting DELETE_TPS_DATA_AFTER = new TimeSetting("Time.Thresholds.Remove_time_series_data_after"); public static final Setting DELETE_PING_DATA_AFTER = new TimeSetting("Time.Thresholds.Remove_ping_data_after"); + public static final Setting DELETE_EXTENSION_DATA_AFTER = new TimeSetting("Time.Thresholds.Remove_disabled_extension_data_after"); public static final Setting EXTENSION_DATA_REFRESH_PERIOD = new TimeSetting("Time.Periodic_tasks.Extension_data_refresh_every"); public static final Setting CLEAN_DATABASE_PERIOD = new TimeSetting("Time.Periodic_tasks.Clean_Database_every"); public static final Setting CONFIG_UPDATE_INTERVAL = new TimeSetting("Time.Periodic_tasks.Check_DB_for_server_config_files_every"); diff --git a/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/init/RemoveOldExtensionsTransaction.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/init/RemoveOldExtensionsTransaction.java new file mode 100644 index 000000000..7071482be --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/init/RemoveOldExtensionsTransaction.java @@ -0,0 +1,169 @@ +/* + * This file is part of Player Analytics (Plan). + * + * Plan is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License v3 as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Plan is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Plan. If not, see . + */ +package com.djrapitops.plan.storage.database.transactions.init; + +import com.djrapitops.plan.storage.database.queries.Query; +import com.djrapitops.plan.storage.database.queries.QueryStatement; +import com.djrapitops.plan.storage.database.sql.tables.*; +import com.djrapitops.plan.storage.database.transactions.ExecStatement; +import com.djrapitops.plan.storage.database.transactions.Transaction; + +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.Collection; +import java.util.HashSet; +import java.util.UUID; + +import static com.djrapitops.plan.storage.database.sql.parsing.Sql.*; + +/** + * Transaction that removes outdated plugin's data after configurable threshold. + * + * @author Rsl1122 + */ +public class RemoveOldExtensionsTransaction extends Transaction { + + private final long deleteOlder; + private final UUID serverUUID; + + public RemoveOldExtensionsTransaction(long deleteAfterMs, UUID serverUUID) { + deleteOlder = System.currentTimeMillis() - deleteAfterMs; + this.serverUUID = serverUUID; + } + + @Override + protected void performOperations() { + for (Integer providerID : query(inactiveProviderIDsQuery())) { + removeValues(providerID); + } + for (Integer providerID : query(inactiveTableProviderIDsQuery())) { + removeTableValues(providerID); + } + removeProviders(); + } + + private void removeValues(int providerID) { + for (String table : new String[]{ + ExtensionPlayerValueTable.TABLE_NAME, + ExtensionServerValueTable.TABLE_NAME, + ExtensionGroupsTable.TABLE_NAME + }) { + execute(new ExecStatement(DELETE_FROM + table + WHERE + "provider_id=?") { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + statement.setInt(1, providerID); + } + }); + } + } + + private void removeTableValues(Integer providerID) { + for (String table : new String[]{ + ExtensionPlayerTableValueTable.TABLE_NAME, + ExtensionServerTableValueTable.TABLE_NAME + }) { + execute(new ExecStatement(DELETE_FROM + table + WHERE + "table_id=?") { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + statement.setInt(1, providerID); + } + }); + } + } + + private void removeProviders() { + execute(new ExecStatement( + DELETE_FROM + ExtensionProviderTable.TABLE_NAME + + WHERE + ExtensionProviderTable.PLUGIN_ID + + " IN (" + + SELECT + ExtensionPluginTable.ID + + FROM + ExtensionPluginTable.TABLE_NAME + + WHERE + ExtensionPluginTable.LAST_UPDATED + "> inactiveProviderIDsQuery() { + String sql = SELECT + "pr." + ExtensionProviderTable.ID + + FROM + ExtensionProviderTable.TABLE_NAME + " pr" + + INNER_JOIN + ExtensionPluginTable.TABLE_NAME + " pl on pl." + ExtensionPluginTable.ID + "=pr." + ExtensionProviderTable.PLUGIN_ID + + WHERE + ExtensionPluginTable.LAST_UPDATED + ">(sql, 100) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + statement.setLong(1, deleteOlder); + statement.setString(2, serverUUID.toString()); + } + + @Override + public Collection processResults(ResultSet set) throws SQLException { + Collection providerIds = new HashSet<>(); + while (set.next()) { + providerIds.add(set.getInt(ExtensionProviderTable.ID)); + } + return providerIds; + } + }; + } + + private Query> inactiveTableProviderIDsQuery() { + String sql = SELECT + "pr." + ExtensionTableProviderTable.ID + + FROM + ExtensionTableProviderTable.TABLE_NAME + " pr" + + INNER_JOIN + ExtensionPluginTable.TABLE_NAME + " pl on pl." + ExtensionPluginTable.ID + "=pr." + ExtensionTableProviderTable.PLUGIN_ID + + WHERE + ExtensionPluginTable.LAST_UPDATED + ">(sql, 100) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + statement.setLong(1, deleteOlder); + statement.setString(2, serverUUID.toString()); + } + + @Override + public Collection processResults(ResultSet set) throws SQLException { + Collection providerIds = new HashSet<>(); + while (set.next()) { + providerIds.add(set.getInt(ExtensionProviderTable.ID)); + } + return providerIds; + } + }; + } +} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/storage/upkeep/DBCleanTask.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/upkeep/DBCleanTask.java index c1dd29ccf..a5ba420a7 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/storage/upkeep/DBCleanTask.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/upkeep/DBCleanTask.java @@ -32,6 +32,7 @@ import com.djrapitops.plan.storage.database.queries.QueryStatement; import com.djrapitops.plan.storage.database.sql.tables.SessionsTable; import com.djrapitops.plan.storage.database.transactions.commands.RemovePlayerTransaction; import com.djrapitops.plan.storage.database.transactions.init.RemoveDuplicateUserInfoTransaction; +import com.djrapitops.plan.storage.database.transactions.init.RemoveOldExtensionsTransaction; import com.djrapitops.plan.storage.database.transactions.init.RemoveOldSampledDataTransaction; import com.djrapitops.plugin.logging.L; import com.djrapitops.plugin.logging.console.PluginLogger; @@ -65,6 +66,10 @@ public class DBCleanTask extends AbsRunnable { private final PluginLogger logger; private final ErrorHandler errorHandler; + // This variable assumes that the system is thrown away on reload and new one is constructed. + // It is to avoid cleaning extension data that has not been updated after uptime longer than the deletion threshold. + private final long lastReload; + @Inject public DBCleanTask( PlanConfig config, @@ -83,6 +88,8 @@ public class DBCleanTask extends AbsRunnable { this.serverInfo = serverInfo; this.logger = logger; this.errorHandler = errorHandler; + + lastReload = System.currentTimeMillis(); } @Override @@ -102,6 +109,10 @@ public class DBCleanTask extends AbsRunnable { if (removed > 0) { logger.info(locale.getString(PluginLang.DB_NOTIFY_CLEAN, removed)); } + Long deleteExtensionDataAfter = config.get(TimeSettings.DELETE_EXTENSION_DATA_AFTER); + if (System.currentTimeMillis() - lastReload <= deleteExtensionDataAfter) { + database.executeTransaction(new RemoveOldExtensionsTransaction(deleteExtensionDataAfter, serverInfo.getServerUUID())); + } } } catch (DBOpException e) { errorHandler.log(L.ERROR, this.getClass(), e); diff --git a/Plan/common/src/main/resources/assets/plan/bungeeconfig.yml b/Plan/common/src/main/resources/assets/plan/bungeeconfig.yml index baa28fbe0..7740523d2 100644 --- a/Plan/common/src/main/resources/assets/plan/bungeeconfig.yml +++ b/Plan/common/src/main/resources/assets/plan/bungeeconfig.yml @@ -90,6 +90,8 @@ Time: Unit: DAYS Remove_ping_data_after: 14 Unit: DAYS + Remove_disabled_extension_data_after: 2 + Unit: DAYS Periodic_tasks: Extension_data_refresh_every: 1 Unit: HOURS diff --git a/Plan/common/src/main/resources/assets/plan/config.yml b/Plan/common/src/main/resources/assets/plan/config.yml index d768a7400..a490a965f 100644 --- a/Plan/common/src/main/resources/assets/plan/config.yml +++ b/Plan/common/src/main/resources/assets/plan/config.yml @@ -95,6 +95,8 @@ Time: Unit: DAYS Remove_ping_data_after: 14 Unit: DAYS + Remove_disabled_extension_data_after: 2 + Unit: DAYS Periodic_tasks: Extension_data_refresh_every: 1 Unit: HOURS