diff --git a/Plan/common/src/main/java/com/djrapitops/plan/storage/database/SQLDB.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/SQLDB.java index 4c483d499..05755dd3b 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/storage/database/SQLDB.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/SQLDB.java @@ -148,7 +148,7 @@ public abstract class SQLDB extends AbstractDatabase { @Override public void init() { - List unfinishedTransactions = closeTransactionExecutor(transactionExecutor); + List unfinishedTransactions = forceCloseTransactionExecutor(); this.transactionExecutor = transactionExecutorServiceProvider.get(); setState(State.PATCHING); @@ -167,9 +167,9 @@ public abstract class SQLDB extends AbstractDatabase { } } - private List closeTransactionExecutor(ExecutorService transactionExecutor) { + protected boolean attemptToCloseTransactionExecutor() { if (transactionExecutor == null || transactionExecutor.isShutdown() || transactionExecutor.isTerminated()) { - return Collections.emptyList(); + return true; } transactionExecutor.shutdown(); try { @@ -179,20 +179,27 @@ public abstract class SQLDB extends AbstractDatabase { logger.warn(TimeSettings.DB_TRANSACTION_FINISH_WAIT_DELAY.getPath() + " was set to over 5 minutes, using 5 min instead."); waitMs = TimeUnit.MINUTES.toMillis(5L); } - if (!transactionExecutor.awaitTermination(waitMs, TimeUnit.MILLISECONDS)) { - List unfinished = transactionExecutor.shutdownNow(); - int unfinishedCount = unfinished.size(); - if (unfinishedCount > 0) { - logger.warn(unfinishedCount + " unfinished database transactions were not executed."); - } - return unfinished; - } + return transactionExecutor.awaitTermination(waitMs, TimeUnit.MILLISECONDS); } catch (InterruptedException e) { Thread.currentThread().interrupt(); + } + return true; + } + + protected List forceCloseTransactionExecutor() { + if (transactionExecutor == null || transactionExecutor.isShutdown() || transactionExecutor.isTerminated()) { + return Collections.emptyList(); + } + try { + List unfinished = transactionExecutor.shutdownNow(); + int unfinishedCount = unfinished.size(); + if (unfinishedCount > 0) { + logger.warn(unfinishedCount + " unfinished database transactions were not executed."); + } + return unfinished; } finally { logger.info(locale.getString(PluginLang.DISABLED_WAITING_TRANSACTIONS_COMPLETE)); } - return Collections.emptyList(); } Patch[] patches() { @@ -307,13 +314,18 @@ public abstract class SQLDB extends AbstractDatabase { @Override public void close() { + // SQLiteDB Overrides this, so any additions to this should also be reflected there. if (getState() == State.OPEN) setState(State.CLOSING); - closeTransactionExecutor(transactionExecutor); + if (attemptToCloseTransactionExecutor()) { + logger.info(locale.getString(PluginLang.DISABLED_WAITING_TRANSACTIONS_COMPLETE)); + } else { + forceCloseTransactionExecutor(); + } unloadDriverClassloader(); setState(State.CLOSED); } - private void unloadDriverClassloader() { + protected void unloadDriverClassloader() { // Unloading class loader using close() causes issues when reloading. // It is better to leak this memory than crash the plugin on reload. @@ -349,6 +361,8 @@ public abstract class SQLDB extends AbstractDatabase { return CompletableFuture.supplyAsync(() -> { try { + if (getState() == State.CLOSED) return CompletableFuture.completedFuture(null); + accessLock.performDatabaseOperation(() -> { if (!ranIntoFatalError.get()) {transaction.executeTransaction(this);} }, transaction); @@ -360,6 +374,9 @@ public abstract class SQLDB extends AbstractDatabase { } private boolean determineIfShouldDropUnimportantTransactions(int queueSize) { + if (getState() == State.CLOSING) { + return true; + } boolean dropTransactions = dropUnimportantTransactions.get(); if (queueSize >= 500 && !dropTransactions) { logger.warn("Database queue size: " + queueSize + ", dropping some unimportant transactions. If this keeps happening disable some extensions or optimize MySQL."); diff --git a/Plan/common/src/main/java/com/djrapitops/plan/storage/database/SQLiteDB.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/SQLiteDB.java index 3d105ec3d..ff2149183 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/storage/database/SQLiteDB.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/SQLiteDB.java @@ -201,11 +201,21 @@ public class SQLiteDB extends SQLDB { @Override public void close() { - super.close(); + if (getState() == State.OPEN) setState(State.CLOSING); + boolean transactionQueueClosed = attemptToCloseTransactionExecutor(); + if (transactionQueueClosed) logger.info(locale.getString(PluginLang.DISABLED_WAITING_TRANSACTIONS_COMPLETE)); + + unloadDriverClassloader(); + setState(State.CLOSED); + stopConnectionPingTask(); logger.info(locale.getString(PluginLang.DISABLED_WAITING_SQLITE)); connectionLock.waitUntilNothingAccessing(); + + // Transaction queue can't be force-closed before all connections have terminated. + if (!transactionQueueClosed) forceCloseTransactionExecutor(); + if (connection != null) { MiscUtils.close(connection); }