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 05755dd3b..6a89a47ba 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 @@ -84,6 +84,7 @@ public abstract class SQLDB extends AbstractDatabase { private Supplier transactionExecutorServiceProvider; private ExecutorService transactionExecutor; + private static final ThreadLocal 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 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> errorHandler(Transaction transaction, Exception origin) { + private Function> errorHandler(Transaction transaction, StackTraceElement[] origin) { return throwable -> { if (throwable == null) { return CompletableFuture.completedFuture(null); 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 ff2149183..2e760bb76 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 @@ -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 diff --git a/Plan/common/src/main/java/com/djrapitops/plan/utilities/SemaphoreAccessCounter.java b/Plan/common/src/main/java/com/djrapitops/plan/utilities/SemaphoreAccessCounter.java index 6b08c495a..b116fda83 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/utilities/SemaphoreAccessCounter.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/utilities/SemaphoreAccessCounter.java @@ -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 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 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(); } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/utilities/java/ThrowableUtils.java b/Plan/common/src/main/java/com/djrapitops/plan/utilities/java/ThrowableUtils.java index 9dc75a0b6..7303d6e0e 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/utilities/java/ThrowableUtils.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/utilities/java/ThrowableUtils.java @@ -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) {