Make call-site for SQLite JVM wait more accurate

Affects issues:
- #3436
This commit is contained in:
Aurora Lahtela 2024-02-17 09:06:45 +02:00
parent e041e193fc
commit 2daf3943b7
4 changed files with 35 additions and 15 deletions

View File

@ -84,6 +84,7 @@ public abstract class SQLDB extends AbstractDatabase {
private Supplier<ExecutorService> transactionExecutorServiceProvider;
private ExecutorService transactionExecutor;
private static final ThreadLocal<StackTraceElement[]> TRANSACTION_ORIGIN = new ThreadLocal<>();
private final AtomicInteger transactionQueueSize = new AtomicInteger(0);
private final AtomicBoolean dropUnimportantTransactions = new AtomicBoolean(false);
@ -345,13 +346,17 @@ public abstract class SQLDB extends AbstractDatabase {
return accessLock.performDatabaseOperation(() -> query.executeQuery(this), transaction);
}
public static ThreadLocal<StackTraceElement[]> getTransactionOrigin() {
return TRANSACTION_ORIGIN;
}
@Override
public CompletableFuture<?> executeTransaction(Transaction transaction) {
if (getState() == State.CLOSED) {
throw new DBClosedException("Transaction tried to execute although database is closed.");
}
Exception origin = new Exception();
StackTraceElement[] origin = Thread.currentThread().getStackTrace();
if (determineIfShouldDropUnimportantTransactions(transactionQueueSize.incrementAndGet())
&& transaction instanceof ThrowawayTransaction) {
@ -361,6 +366,7 @@ public abstract class SQLDB extends AbstractDatabase {
return CompletableFuture.supplyAsync(() -> {
try {
TRANSACTION_ORIGIN.set(origin);
if (getState() == State.CLOSED) return CompletableFuture.completedFuture(null);
accessLock.performDatabaseOperation(() -> {
@ -369,6 +375,7 @@ public abstract class SQLDB extends AbstractDatabase {
return CompletableFuture.completedFuture(null);
} finally {
transactionQueueSize.decrementAndGet();
TRANSACTION_ORIGIN.remove();
}
}, getTransactionExecutor()).exceptionally(errorHandler(transaction, origin));
}
@ -389,7 +396,7 @@ public abstract class SQLDB extends AbstractDatabase {
return dropTransactions;
}
private Function<Throwable, CompletableFuture<Object>> errorHandler(Transaction transaction, Exception origin) {
private Function<Throwable, CompletableFuture<Object>> errorHandler(Transaction transaction, StackTraceElement[] origin) {
return throwable -> {
if (throwable == null) {
return CompletableFuture.completedFuture(null);

View File

@ -76,7 +76,7 @@ public class SQLiteDB extends SQLDB {
super(() -> serverInfo.get().getServerUUID(), locale, config, files, runnableFactory, logger, errorLogger);
dbName = databaseFile.getName();
this.databaseFile = databaseFile;
connectionLock = new SemaphoreAccessCounter(config);
connectionLock = new SemaphoreAccessCounter();
}
@Override

View File

@ -16,7 +16,8 @@
*/
package com.djrapitops.plan.utilities;
import com.djrapitops.plan.settings.config.PlanConfig;
import com.djrapitops.plan.storage.database.SQLDB;
import com.djrapitops.plan.utilities.java.ThrowableUtils;
import org.jetbrains.annotations.NotNull;
import java.util.*;
@ -27,14 +28,11 @@ import java.util.logging.Logger;
public class SemaphoreAccessCounter {
private final PlanConfig config;
private final AtomicInteger accessCounter;
private final Object lockObject;
private final Collection<String> holds = Collections.newSetFromMap(new ConcurrentHashMap<>());
public SemaphoreAccessCounter(PlanConfig config) {
this.config = config;
public SemaphoreAccessCounter() {
accessCounter = new AtomicInteger(0);
lockObject = new Object();
}
@ -43,7 +41,11 @@ public class SemaphoreAccessCounter {
private static String getAccessingThing() {
boolean previousWasAccess = false;
List<StackTraceElement> accessors = new ArrayList<>();
for (StackTraceElement e : Thread.currentThread().getStackTrace()) {
StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
StackTraceElement[] origin = SQLDB.getTransactionOrigin().get();
StackTraceElement[] callSite = ThrowableUtils.combineStackTrace(origin, stackTrace);
for (StackTraceElement e : callSite) {
if (previousWasAccess) {
accessors.add(e);
previousWasAccess = false;
@ -54,7 +56,7 @@ public class SemaphoreAccessCounter {
previousWasAccess = true;
}
}
if (accessors.isEmpty()) accessors.addAll(Arrays.asList(Thread.currentThread().getStackTrace()));
if (accessors.isEmpty()) accessors.addAll(Arrays.asList(callSite));
return accessors.toString();
}

View File

@ -16,6 +16,8 @@
*/
package com.djrapitops.plan.utilities.java;
import org.jetbrains.annotations.NotNull;
import java.util.Arrays;
import java.util.stream.Stream;
@ -30,20 +32,29 @@ public class ThrowableUtils {
/* Static method class */
}
public static void appendEntryPointToCause(Throwable throwable, Throwable originPoint) {
public static void appendEntryPointToCause(Throwable throwable, StackTraceElement[] originPoint) {
Throwable cause = throwable.getCause();
while (cause.getCause() != null) {
cause = cause.getCause();
}
cause.setStackTrace(
Stream.concat(
Arrays.stream(cause.getStackTrace()),
Arrays.stream(originPoint.getStackTrace())
).toArray(StackTraceElement[]::new)
combineStackTrace(originPoint, cause.getStackTrace())
);
}
@NotNull
public static StackTraceElement[] combineStackTrace(StackTraceElement[] originPoint, StackTraceElement[] cause) {
if (originPoint == null && cause == null) return new StackTraceElement[0];
if (originPoint == null) return cause;
if (cause == null) return originPoint;
return Stream.concat(
Arrays.stream(cause),
Arrays.stream(originPoint)
).toArray(StackTraceElement[]::new);
}
public static String findCallerAfterClass(StackTraceElement[] stackTrace, Class<?> afterThis) {
boolean found = false;
for (StackTraceElement stackTraceElement : stackTrace) {