Skip to content

Commit

Permalink
GH-162 Add DatabaseManager closure on shutdown, add scheduler adapter. (
Browse files Browse the repository at this point in the history
#162)

* Add DatabaseManager closure on shutdown

* Refactor task scheduling using VirtualThreadScheduler.

* Fix inconsistent indentation style

* Remove useless import.

* Remove unused EXECUTOR_SERVICE constant.
  • Loading branch information
vLuckyyy authored Sep 5, 2024
1 parent 8d448f3 commit 1c2aa8e
Show file tree
Hide file tree
Showing 6 changed files with 97 additions and 46 deletions.
56 changes: 16 additions & 40 deletions src/main/java/com/eternalcode/discordapp/DiscordApp.java
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@
import com.eternalcode.discordapp.review.command.GitHubReviewCommand;
import com.eternalcode.discordapp.review.database.GitHubReviewMentionRepository;
import com.eternalcode.discordapp.review.database.GitHubReviewMentionRepositoryImpl;
import com.eternalcode.discordapp.scheduler.Scheduler;
import com.eternalcode.discordapp.scheduler.VirtualThreadSchedulerImpl;
import com.eternalcode.discordapp.user.UserRepositoryImpl;
import com.jagrosh.jdautilities.command.CommandClient;
import com.jagrosh.jdautilities.command.CommandClientBuilder;
Expand All @@ -48,7 +50,6 @@
import java.util.EnumSet;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import net.dv8tion.jda.api.JDA;
import net.dv8tion.jda.api.JDABuilder;
import net.dv8tion.jda.api.entities.Activity;
Expand All @@ -63,11 +64,12 @@
public class DiscordApp {

private static final Logger LOGGER = LoggerFactory.getLogger(DiscordApp.class);
private static final ExecutorService EXECUTOR_SERVICE = Executors.newVirtualThreadPerTaskExecutor();

private static ExperienceService experienceService;
private static LevelService levelService;
private static GitHubReviewService gitHubReviewService;
private static DatabaseManager databaseManager;
private static Scheduler scheduler;

public static void main(String... args) throws InterruptedException {
Runtime.getRuntime().addShutdownHook(new Thread(DiscordApp::shutdown));
Expand All @@ -90,7 +92,7 @@ public static void main(String... args) throws InterruptedException {
}

try {
DatabaseManager databaseManager = new DatabaseManager(databaseConfig, new File("database"));
databaseManager = new DatabaseManager(databaseConfig, new File("database"));
databaseManager.connect();
UserRepositoryImpl.create(databaseManager);
GitHubReviewMentionRepository gitHubReviewMentionRepository =
Expand Down Expand Up @@ -169,57 +171,31 @@ public static void main(String... args) throws InterruptedException {
.awaitReady();

observerRegistry.observe(ExperienceChangeEvent.class, new LevelController(levelConfig, levelService, jda));

GuildStatisticsService guildStatisticsService = new GuildStatisticsService(config, jda);

Thread.setDefaultUncaughtExceptionHandler((thread, throwable) -> {
Sentry.captureException(throwable);
LOGGER.error("Uncaught exception", throwable);
});

EXECUTOR_SERVICE.submit(() -> {
while (true) {
new GuildStatisticsTask(guildStatisticsService).run();
try {
Thread.sleep(Duration.ofMinutes(5).toMillis());
}
catch (InterruptedException exception) {
Thread.currentThread().interrupt();
break;
}
}
});

EXECUTOR_SERVICE.submit(() -> {
while (true) {
new GitHubReviewTask(gitHubReviewService, jda).run();
try {
Thread.sleep(Duration.ofMinutes(5).toMillis());
}
catch (InterruptedException exception) {
Thread.currentThread().interrupt();
break;
}
}
});
scheduler = new VirtualThreadSchedulerImpl();
scheduler.schedule(new GuildStatisticsTask(guildStatisticsService), Duration.ofMinutes(5));
scheduler.schedule(new GitHubReviewTask(gitHubReviewService, jda), Duration.ofMinutes(5));
}

private static void shutdown() {
try {
LOGGER.info("Shutting down executor service...");
EXECUTOR_SERVICE.shutdown();

if (!EXECUTOR_SERVICE.awaitTermination(60, TimeUnit.SECONDS)) {
LOGGER.warn("Executor did not terminate in the specified time.");
EXECUTOR_SERVICE.shutdownNow();
}
databaseManager.close();
}
catch (Exception exception) {
throw new RuntimeException(exception);
}

LOGGER.info("Executor service shut down successfully.");
try {
scheduler.shutdown();
}
catch (InterruptedException exception) {
LOGGER.error("Shutdown interrupted", exception);
EXECUTOR_SERVICE.shutdownNow();
Thread.currentThread().interrupt();
throw new RuntimeException(exception);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,17 @@ public ConnectionSource getConnectionSource() {
return this.connectionSource;
}

public void close() throws Exception {
try {
this.connectionSource.close();
this.hikariDataSource.close();
}
catch (SQLException sqlException) {
Sentry.captureException(sqlException);
throw new DataAccessException("Failed to close connection", sqlException);
}
}

@SuppressWarnings("unchecked")
public <T, ID> Dao<T, ID> getDao(Class<T> clazz) {
Dao<?, ?> dao = this.daoCache.computeIfAbsent(clazz, key -> {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package com.eternalcode.discordapp.guildstats;

import java.util.TimerTask;

public class GuildStatisticsTask extends TimerTask {
public class GuildStatisticsTask implements Runnable {

private final GuildStatisticsService guildStatisticsService;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,7 @@
import io.sentry.Sentry;
import net.dv8tion.jda.api.JDA;

import java.util.TimerTask;

public class GitHubReviewTask extends TimerTask {
public class GitHubReviewTask implements Runnable {

private final GitHubReviewService gitHubReviewService;
private final JDA jda;
Expand Down
12 changes: 12 additions & 0 deletions src/main/java/com/eternalcode/discordapp/scheduler/Scheduler.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.eternalcode.discordapp.scheduler;

import java.time.Duration;

public interface Scheduler {

void schedule(Runnable task, Duration delay);

void schedule(Runnable task);

void shutdown() throws InterruptedException;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package com.eternalcode.discordapp.scheduler;

import java.time.Duration;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class VirtualThreadSchedulerImpl implements Scheduler {

// works on javca 21+
private static final ExecutorService EXECUTOR_SERVICE = Executors.newVirtualThreadPerTaskExecutor();
private static final Logger LOGGER = LoggerFactory.getLogger(VirtualThreadSchedulerImpl.class.getName());

@Override
public void schedule(Runnable task, Duration delay) {
EXECUTOR_SERVICE.submit(() -> {
while (!Thread.currentThread().isInterrupted()) {
task.run();

try {
Thread.sleep(delay.toMillis());
}
catch (InterruptedException exception) {
Thread.currentThread().interrupt();
}
}
});
}

@Override
public void schedule(Runnable task) {
EXECUTOR_SERVICE.submit(task);
}

@Override
public void shutdown() {
try {
LOGGER.info("Shutting down executor service...");
EXECUTOR_SERVICE.shutdown();

if (!EXECUTOR_SERVICE.awaitTermination(60, TimeUnit.SECONDS)) {
LOGGER.warn("Executor did not terminate in the specified time.");
EXECUTOR_SERVICE.shutdownNow();
}

LOGGER.info("Executor service shut down successfully.");
}
catch (InterruptedException exception) {
LOGGER.error("Shutdown interrupted", exception);
EXECUTOR_SERVICE.shutdownNow();
Thread.currentThread().interrupt();
}
}
}

0 comments on commit 1c2aa8e

Please sign in to comment.