Limit the total number of running threads

(Related #304)
This commit is contained in:
games647
2020-03-14 18:20:34 +01:00
parent e0bc7d914c
commit 57b84509da
12 changed files with 88 additions and 31 deletions
@@ -0,0 +1,40 @@
package com.github.games647.fastlogin.core;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
* This limits the number of threads that are used at maximum. Thread creation can be very heavy for the CPU and
* context switching between threads too. However we need many threads for blocking HTTP and database calls.
* Nevertheless this number can be further limited, because the number of actually working database threads
* is limited by the size of our database pool. The goal is to separate concerns into processing and blocking only
* threads.
*/
public class AsyncScheduler {
// 30 threads are still too many - the optimal solution is to separate into processing and blocking threads
// where processing threads could only be max number of cores while blocking threads could be minimized using
// non-blocking I/O and a single event executor
private final ThreadPoolExecutor executorService;
public AsyncScheduler(ThreadFactory threadFactory) {
executorService = new ThreadPoolExecutor(5, 30,
60L, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(1024), threadFactory);
}
public void runAsync(Runnable task) {
executorService.execute(task);
}
public void shutdown() {
executorService.shutdown();
try {
executorService.awaitTermination(1, TimeUnit.MINUTES);
} catch (InterruptedException interruptEx) {
Thread.currentThread().interrupt();
}
}
}
@@ -72,6 +72,9 @@ public class AuthStorage {
}
public void createTables() throws SQLException {
// choose surrogate PK(ID), because UUID can be null for offline players
// if UUID is always Premium UUID we would have to update offline player entries on insert
// name cannot be PK, because it can be changed for premium players
String createDataStmt = "CREATE TABLE IF NOT EXISTS `" + PREMIUM_TABLE + "` ("
+ "`UserID` INTEGER PRIMARY KEY AUTO_INCREMENT, "
+ "`UUID` CHAR(36), "
@@ -87,7 +90,7 @@ public class AuthStorage {
createDataStmt = createDataStmt.replace("AUTO_INCREMENT", "AUTOINCREMENT");
}
//todo: add uuid index usage
//todo: add unique uuid index usage
try (Connection con = dataSource.getConnection();
Statement createStmt = con.createStatement()) {
createStmt.executeUpdate(createDataStmt);
@@ -4,6 +4,7 @@ import com.github.games647.craftapi.resolver.MojangResolver;
import com.github.games647.craftapi.resolver.http.RotatingProxySelector;
import com.github.games647.fastlogin.core.AuthStorage;
import com.github.games647.fastlogin.core.CommonUtil;
import com.github.games647.fastlogin.core.AsyncScheduler;
import com.github.games647.fastlogin.core.hooks.AuthPlugin;
import com.github.games647.fastlogin.core.hooks.DefaultPasswordGenerator;
import com.github.games647.fastlogin.core.hooks.PasswordGenerator;
@@ -51,6 +52,7 @@ public class FastLoginCore<P extends C, C, T extends PlatformPlugin<C>> {
private final T plugin;
private final MojangResolver resolver = new MojangResolver();
private final AsyncScheduler scheduler;
private Configuration config;
private AuthStorage storage;
@@ -59,6 +61,7 @@ public class FastLoginCore<P extends C, C, T extends PlatformPlugin<C>> {
public FastLoginCore(T plugin) {
this.plugin = plugin;
this.scheduler = new AsyncScheduler(plugin.getThreadFactory());
}
public void load() {
@@ -239,7 +242,14 @@ public class FastLoginCore<P extends C, C, T extends PlatformPlugin<C>> {
}
}
public AsyncScheduler getAsyncScheduler() {
return scheduler;
}
public void close() {
plugin.getLog().info("Safely shutting down scheduler. This could take up to one minute.");
scheduler.shutdown();
if (storage != null) {
storage.close();
}
@@ -1,5 +1,7 @@
package com.github.games647.fastlogin.core.shared;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import java.nio.file.Path;
import java.util.concurrent.ThreadFactory;
@@ -22,6 +24,11 @@ public interface PlatformPlugin<C> {
}
default ThreadFactory getThreadFactory() {
return null;
return new ThreadFactoryBuilder()
.setNameFormat(getName() + " Pool Thread #%1$d")
// Hikari create daemons by default and we could daemon threads for our own scheduler too
// because we safely shutdown
.setDaemon(true)
.build();
}
}