mirror of
https://github.com/plan-player-analytics/Plan.git
synced 2025-02-05 16:30:24 +08:00
Drop ThrowawayTransactions if database queues over 500 transactions.
This should reduce the likelihood of out of memory crashes due to slow database write performance. Affects issues: - Close #1963
This commit is contained in:
parent
a1d53b8910
commit
24e955e428
@ -26,6 +26,7 @@ import com.djrapitops.plan.settings.config.paths.TimeSettings;
|
||||
import com.djrapitops.plan.settings.locale.Locale;
|
||||
import com.djrapitops.plan.settings.locale.lang.PluginLang;
|
||||
import com.djrapitops.plan.storage.database.queries.Query;
|
||||
import com.djrapitops.plan.storage.database.transactions.ThrowawayTransaction;
|
||||
import com.djrapitops.plan.storage.database.transactions.Transaction;
|
||||
import com.djrapitops.plan.storage.database.transactions.init.CreateIndexTransaction;
|
||||
import com.djrapitops.plan.storage.database.transactions.init.CreateTablesTransaction;
|
||||
@ -54,6 +55,7 @@ import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
@ -85,6 +87,8 @@ public abstract class SQLDB extends AbstractDatabase {
|
||||
private Supplier<ExecutorService> transactionExecutorServiceProvider;
|
||||
private ExecutorService transactionExecutor;
|
||||
|
||||
private final AtomicInteger transactionQueueSize = new AtomicInteger(0);
|
||||
private final AtomicBoolean dropUnimportantTransactions = new AtomicBoolean(false);
|
||||
private final AtomicBoolean ranIntoFatalError = new AtomicBoolean(false);
|
||||
|
||||
protected SQLDB(
|
||||
@ -291,17 +295,10 @@ public abstract class SQLDB extends AbstractDatabase {
|
||||
}
|
||||
|
||||
private void unloadDriverClassloader() {
|
||||
// Unloading class loader causes issues when reloading.
|
||||
// Unloading class loader using close() causes issues when reloading.
|
||||
// It is better to leak this memory than crash the plugin on reload.
|
||||
|
||||
// try {
|
||||
// if (driverClassLoader instanceof IsolatedClassLoader) {
|
||||
// ((IsolatedClassLoader) driverClassLoader).close();
|
||||
// }
|
||||
driverClassLoader = null;
|
||||
// } catch (IOException e) {
|
||||
// errorLogger.error(e, ErrorContext.builder().build());
|
||||
// }
|
||||
}
|
||||
|
||||
public abstract Connection getConnection() throws SQLException;
|
||||
@ -322,15 +319,38 @@ public abstract class SQLDB extends AbstractDatabase {
|
||||
|
||||
Exception origin = new Exception();
|
||||
|
||||
if (determineIfShouldDropUnimportantTransactions(transactionQueueSize.incrementAndGet())
|
||||
&& transaction instanceof ThrowawayTransaction) {
|
||||
// Drop throwaway transaction immediately.
|
||||
return CompletableFuture.completedFuture(null);
|
||||
}
|
||||
|
||||
return CompletableFuture.supplyAsync(() -> {
|
||||
try {
|
||||
accessLock.checkAccess(transaction);
|
||||
if (!ranIntoFatalError.get()) {
|
||||
transaction.executeTransaction(this);
|
||||
}
|
||||
return CompletableFuture.completedFuture(null);
|
||||
} finally {
|
||||
transactionQueueSize.decrementAndGet();
|
||||
}
|
||||
}, getTransactionExecutor()).exceptionally(errorHandler(transaction, origin));
|
||||
}
|
||||
|
||||
private boolean determineIfShouldDropUnimportantTransactions(int queueSize) {
|
||||
boolean dropTransactions = dropUnimportantTransactions.get();
|
||||
if (queueSize >= 500 && !dropTransactions) {
|
||||
logger.warn("Database can't keep up with transactions (Queue size: " + queueSize + "), dropping some unimportant transactions from execution.");
|
||||
dropUnimportantTransactions.set(true);
|
||||
return true;
|
||||
} else if (queueSize < 50 && dropTransactions) {
|
||||
dropUnimportantTransactions.set(false);
|
||||
return false;
|
||||
}
|
||||
return dropTransactions;
|
||||
}
|
||||
|
||||
private Function<Throwable, CompletableFuture<Object>> errorHandler(Transaction transaction, Exception origin) {
|
||||
return throwable -> {
|
||||
if (throwable == null) {
|
||||
@ -399,4 +419,8 @@ public abstract class SQLDB extends AbstractDatabase {
|
||||
public Locale getLocale() {
|
||||
return locale;
|
||||
}
|
||||
|
||||
public boolean shouldDropUnimportantTransactions() {
|
||||
return dropUnimportantTransactions.get();
|
||||
}
|
||||
}
|
||||
|
@ -70,8 +70,8 @@ public abstract class Transaction {
|
||||
|
||||
if (db.isUnderHeavyLoad()) {
|
||||
try {
|
||||
Thread.sleep(db.getHeavyLoadDelayMs());
|
||||
Thread.yield();
|
||||
Thread.sleep(db.getHeavyLoadDelayMs());
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
@ -112,7 +112,7 @@ public abstract class Transaction {
|
||||
if (!db.isUnderHeavyLoad()) {
|
||||
db.getLogger().warn("Database appears to be under heavy load. Dropping some unimportant transactions and adding short pauses for next 10 minutes.");
|
||||
db.getRunnableFactory().create(db::assumeNoMoreHeavyLoad)
|
||||
.runTaskLaterAsynchronously(TimeAmount.toTicks(10, TimeUnit.MINUTES));
|
||||
.runTaskLaterAsynchronously(TimeAmount.toTicks(2, TimeUnit.MINUTES));
|
||||
}
|
||||
db.increaseHeavyLoadDelay();
|
||||
executeTransaction(db); // Recurse to attempt again.
|
||||
@ -267,7 +267,7 @@ public abstract class Transaction {
|
||||
}
|
||||
|
||||
public boolean dbIsNotUnderHeavyLoad() {
|
||||
return !db.isUnderHeavyLoad();
|
||||
return !db.isUnderHeavyLoad() && !db.shouldDropUnimportantTransactions();
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
|
Loading…
Reference in New Issue
Block a user