Log connection holding call sites in dev-mode

This commit is contained in:
Aurora Lahtela 2024-02-03 10:17:31 +02:00
parent 4615c6b6b0
commit 5ddbd52d37
2 changed files with 59 additions and 2 deletions

View File

@ -59,7 +59,7 @@ public class SQLiteDB extends SQLDB {
* one thread closing the connection while another is executing a statement as
* that might lead to a SIGSEGV signal JVM crash.
*/
private final SemaphoreAccessCounter connectionLock = new SemaphoreAccessCounter();
private final SemaphoreAccessCounter connectionLock;
private Constructor<?> connectionConstructor;
@ -76,6 +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);
}
@Override

View File

@ -16,24 +16,65 @@
*/
package com.djrapitops.plan.utilities;
import com.djrapitops.plan.settings.config.PlanConfig;
import com.djrapitops.plan.settings.config.paths.PluginSettings;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;
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<>());
private AtomicBoolean logHolds = null;
public SemaphoreAccessCounter() {
public SemaphoreAccessCounter(PlanConfig config) {
this.config = config;
accessCounter = new AtomicInteger(0);
lockObject = new Object();
}
@NotNull
private static String getAccessingThing() {
boolean previousWasAccess = false;
List<StackTraceElement> accessors = new ArrayList<>();
for (StackTraceElement e : Thread.currentThread().getStackTrace()) {
if (previousWasAccess) {
accessors.add(e);
previousWasAccess = false;
}
String call = e.getClassName() + "." + e.getMethodName();
if ("com.djrapitops.plan.storage.database.SQLDB.query".equals(call)
|| "com.djrapitops.plan.storage.database.SQLDB.executeTransaction".equals(call)) {
previousWasAccess = true;
}
}
return accessors.toString();
}
public void enter() {
accessCounter.incrementAndGet();
if (logHolds == null) logHolds = new AtomicBoolean(config.isTrue(PluginSettings.DEV_MODE));
if (logHolds.get()) holds.add(getAccessingThing());
}
public void exit() {
synchronized (lockObject) {
if (logHolds == null) logHolds = new AtomicBoolean(config.isTrue(PluginSettings.DEV_MODE));
if (logHolds.get()) holds.remove(getAccessingThing());
int value = accessCounter.decrementAndGet();
if (value == 0) {
lockObject.notifyAll();
@ -45,6 +86,7 @@ public class SemaphoreAccessCounter {
while (accessCounter.get() > 0) {
synchronized (lockObject) {
try {
logAccess();
lockObject.wait();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
@ -52,4 +94,18 @@ public class SemaphoreAccessCounter {
}
}
}
private void logAccess() {
if (logHolds != null && logHolds.get()) {
Logger logger = Logger.getLogger("Plan");
if (logger == null) logger = Logger.getGlobal();
if (logger.isLoggable(Level.INFO) && !holds.isEmpty()) {
logger.log(Level.INFO, "DEBUG - Following call sites are holding connection:");
for (String hold : holds) {
logger.log(Level.INFO, String.format("DEBUG - %s", hold));
}
}
}
}
}