Close transaction queue after connection wait on SQLite

Affects issues:
- #3436
This commit is contained in:
Aurora Lahtela 2024-02-11 15:47:39 +02:00
parent bf3bdb599d
commit e041e193fc
2 changed files with 42 additions and 15 deletions

View File

@ -148,7 +148,7 @@ public abstract class SQLDB extends AbstractDatabase {
@Override
public void init() {
List<Runnable> unfinishedTransactions = closeTransactionExecutor(transactionExecutor);
List<Runnable> unfinishedTransactions = forceCloseTransactionExecutor();
this.transactionExecutor = transactionExecutorServiceProvider.get();
setState(State.PATCHING);
@ -167,9 +167,9 @@ public abstract class SQLDB extends AbstractDatabase {
}
}
private List<Runnable> 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<Runnable> 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<Runnable> forceCloseTransactionExecutor() {
if (transactionExecutor == null || transactionExecutor.isShutdown() || transactionExecutor.isTerminated()) {
return Collections.emptyList();
}
try {
List<Runnable> 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.");

View File

@ -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);
}