Coreoz / Wisp

A simple Java Scheduler library with a minimal footprint and a straightforward API
Apache License 2.0
131 stars 22 forks source link

Stops scheduling after a few minutes of executions #12

Closed cixastra closed 2 years ago

cixastra commented 2 years ago

Heyo! This scheduler is absolutely awesome first off! I am having an issue with the following code, the error is also below.

00:24:20.968 [Wisp Scheduler Worker #0] ERROR com.coreoz.wisp.Scheduler - Error during job 'xxx.xxx.xxx.Scheduled.ServerStatus@385afffe' execution
java.lang.OutOfMemoryError: unable to create native thread: possibly out of memory or process/resource limits reached
        at java.base/java.lang.Thread.start0(Native Method)
        at java.base/java.lang.Thread.start(Thread.java:800)
        at java.base/java.util.concurrent.ThreadPoolExecutor.addWorker(ThreadPoolExecutor.java:939)
        at java.base/java.util.concurrent.ThreadPoolExecutor.ensurePrestart(ThreadPoolExecutor.java:1585)
        at java.base/java.util.concurrent.ScheduledThreadPoolExecutor.delayedExecute(ScheduledThreadPoolExecutor.java:346)
        at java.base/java.util.concurrent.ScheduledThreadPoolExecutor.schedule(ScheduledThreadPoolExecutor.java:562)
        at com.mattmalec.pterodactyl4j.requests.RateLimiter.lambda$runQueue$2(RateLimiter.java:106)
        at java.base/java.util.concurrent.ConcurrentHashMap.computeIfAbsent(ConcurrentHashMap.java:1708)
        at com.mattmalec.pterodactyl4j.requests.RateLimiter.lambda$runQueue$3(RateLimiter.java:105)
        at com.mattmalec.pterodactyl4j.utils.LockUtils.locked(LockUtils.java:29)
        at com.mattmalec.pterodactyl4j.requests.RateLimiter.runQueue(RateLimiter.java:105)
        at com.mattmalec.pterodactyl4j.requests.RateLimiter.lambda$queueRequest$0(RateLimiter.java:61)
        at com.mattmalec.pterodactyl4j.utils.LockUtils.locked(LockUtils.java:29)
        at com.mattmalec.pterodactyl4j.requests.RateLimiter.queueRequest(RateLimiter.java:59)
        at com.mattmalec.pterodactyl4j.requests.Requester.request(Requester.java:57)
        at com.mattmalec.pterodactyl4j.requests.RequestFuture.<init>(RequestFuture.java:29)
        at com.mattmalec.pterodactyl4j.requests.PteroActionImpl.execute(PteroActionImpl.java:92)
        at com.mattmalec.pterodactyl4j.PteroAction.execute(PteroAction.java:97)
        at dev.elliotfrost.anarchybot.Scheduled.ServerStatus.BungeeStatus(ServerStatus.java:26)
        at dev.elliotfrost.anarchybot.Scheduled.ServerStatus.run(ServerStatus.java:51)
        at com.coreoz.wisp.Scheduler.runJob(Scheduler.java:481)
        at com.coreoz.wisp.Scheduler.lambda$launcher$0(Scheduler.java:450)
        at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1130)
        at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:630)
        at java.base/java.lang.Thread.run(Thread.java:831)
00:24:20.968 [Wisp Scheduler Worker #0] DEBUG com.coreoz.wisp.Scheduler - Job 'xxx.xxx.xxx.Scheduled.ServerStatus@385afffe' executed in 257ms

The code for the scheduler (whole file not included):

    public void onReady(ReadyEvent event) {
        LOGGER.info("{} is ready", event.getJDA().getSelfUser().getAsTag());
        new ServerStatus().newStatuses();
        new Scheduler(
                SchedulerConfig
                        .builder()
                        .minThreads(1)
                        .maxThreads(2)
                        .threadsKeepAliveTime(Duration.ofSeconds(5))
                        .build()
        ).schedule(new ServerStatus(), Schedules.fixedDelaySchedule(Duration.ofSeconds(10)));
    }

The scheduled code:

public class ServerStatus implements Runnable {

    public void BungeeStatus() {
        ArrayList<String> messages = new ArrayList<>();
        PteroClient api = PteroBuilder.createClient("https://panel.skynode.pro", Config.get("PTERO_TOKEN"));
        UtilizationState Powerstate = api.retrieveServerByIdentifier(Config.get("BUNGEE-SERVER-ID")).execute().retrieveUtilization().execute().getState();
        // Objects.requireNonNull(Bot.getJDA().getGuildById(Config.get("GUILD-ID")).getTextChannelById(Config.get("STATUS-CHANNEL"))).editMessageById().queue();
        try {
            File myObj = new File("status-messages.txt");
            Scanner myReader = new Scanner(myObj);
            while (myReader.hasNextLine()) {
                messages.add(myReader.nextLine());
            }
            myReader.close();
        } catch (FileNotFoundException e) {
            System.out.println("An error occurred.");
            e.printStackTrace();
        }
        MessageEmbed embed = new EmbedBuilder()
                .setTitle("Status: " + Powerstate.toString())
                .setDescription("The server that you actually connect to!")
                .setFooter("anarchy.ciputin.cf","https://i.imgur.com/i4ht6nZ.png")
                .setColor(Determinestatecolor(Powerstate))
                .build();
        Bot.getJDA().getGuildById(Config.get("GUILD-ID")).getTextChannelById(Config.get("STATUS-CHANNEL")).editMessageEmbedsById(messages.get(3), embed).queue();
    }

    @Override
    public void run() {
        if (Config.get("DEV").equals("true")) {return;}
        BungeeStatus();
        AnarchyStatus();
        LobbyStatus();
        SMPStatus();
    }
    public void newStatuses() {
        if (Config.get("DEV").equals("true")) {return;}
        /* Delete old Messages */
            List<Message> old = Bot.getJDA().getGuildById(Config.get("GUILD-ID")).getTextChannelById(Config.get("STATUS-CHANNEL"))
                    .getHistory()
                    .retrievePast(100)
                    .complete();
        /* Send new messages */
        Bot.getJDA().getGuildById(Config.get("GUILD-ID")).getTextChannelById(Config.get("STATUS-CHANNEL")).sendMessage("BungeeCord:").complete();
        Bot.getJDA().getGuildById(Config.get("GUILD-ID")).getTextChannelById(Config.get("STATUS-CHANNEL")).sendMessage("Anarchy:").complete();
        Bot.getJDA().getGuildById(Config.get("GUILD-ID")).getTextChannelById(Config.get("STATUS-CHANNEL")).sendMessage("Lobby:").complete();
        Bot.getJDA().getGuildById(Config.get("GUILD-ID")).getTextChannelById(Config.get("STATUS-CHANNEL")).sendMessage("SMP:").complete();

        /* Get history of said messages */
        List<Message> messages = Bot.getJDA().getGuildById(Config.get("GUILD-ID")).getTextChannelById(Config.get("STATUS-CHANNEL")).getIterableHistory().limit(4).complete();

        /* save history */
        try {
            FileWriter myWriter = new FileWriter("status-messages.txt");
            for (int i = 0, messagesSize = messages.size(); i < messagesSize; i++) {
                Message message = messages.get(i);
                myWriter.append(message.getId() + "\n");
            }
            myWriter.close();
        } catch (IOException e) {
            System.out.println("An error occurred.");
            e.printStackTrace();
        }
        for (Message message : old) {
            Bot.getJDA().getGuildById(Config.get("GUILD-ID")).getTextChannelById(Config.get("STATUS-CHANNEL")).deleteMessageById(message.getId()).complete();
        }
    }

    public void LobbyStatus() {
        ArrayList<String> messages = new ArrayList<>();
        PteroClient api = PteroBuilder.createClient("https://panel.skynode.pro", Config.get("PTERO_TOKEN"));
        UtilizationState Powerstate = api.retrieveServerByIdentifier(Config.get("LOBBY-SERVER-ID")).execute().retrieveUtilization().execute().getState();
        // Objects.requireNonNull(Bot.getJDA().getGuildById(Config.get("GUILD-ID")).getTextChannelById(Config.get("STATUS-CHANNEL"))).editMessageById().queue();
        try {
            File myObj = new File("status-messages.txt");
            Scanner myReader = new Scanner(myObj);
            while (myReader.hasNextLine()) {
                messages.add(myReader.nextLine());
            }
            myReader.close();
        } catch (FileNotFoundException e) {
            System.out.println("An error occurred.");
            e.printStackTrace();
        }
        MessageEmbed embed = new EmbedBuilder()
                .setTitle("Status: " + Powerstate.toString())
                .setDescription("The server that you connect to on join!")
                .setFooter("anarchy.ciputin.cf", "https://i.imgur.com/i4ht6nZ.png")
                .setColor(Determinestatecolor(Powerstate))
                .build();
        Bot.getJDA().getGuildById(Config.get("GUILD-ID")).getTextChannelById(Config.get("STATUS-CHANNEL")).editMessageEmbedsById(messages.get(1), embed).queue();
    }

    public void AnarchyStatus() {
        ArrayList<String> messages = new ArrayList<>();
        PteroClient api = PteroBuilder.createClient("https://panel.skynode.pro", Config.get("PTERO_TOKEN"));
        UtilizationState Powerstate = api.retrieveServerByIdentifier(Config.get("ANARCHY-SERVER-ID")).execute().retrieveUtilization().execute().getState();
        // Objects.requireNonNull(Bot.getJDA().getGuildById(Config.get("GUILD-ID")).getTextChannelById(Config.get("STATUS-CHANNEL"))).editMessageById().queue();
        try {
            File myObj = new File("status-messages.txt");
            Scanner myReader = new Scanner(myObj);
            while (myReader.hasNextLine()) {
                messages.add(myReader.nextLine());
            }
            myReader.close();
        } catch (FileNotFoundException e) {
            System.out.println("An error occurred.");
            e.printStackTrace();
        }
        MessageEmbed embed = new EmbedBuilder()
                .setTitle("Status: " + Powerstate.toString())
                .setDescription("The main event :smiling_imp:")
                .setFooter("anarchy.ciputin.cf","https://i.imgur.com/i4ht6nZ.png")
                .setColor(Determinestatecolor(Powerstate))
                .build();
        Bot.getJDA().getGuildById(Config.get("GUILD-ID")).getTextChannelById(Config.get("STATUS-CHANNEL")).editMessageEmbedsById(messages.get(2), embed).queue();
    }

    public void SMPStatus() {
        ArrayList<String> messages = new ArrayList<>();
        PteroClient api = PteroBuilder.createClient("https://panel.skynode.pro", Config.get("PTERO_TOKEN"));
        UtilizationState Powerstate = api.retrieveServerByIdentifier(Config.get("SMP-SERVER-ID")).execute().retrieveUtilization().execute().getState();
        // Objects.requireNonNull(Bot.getJDA().getGuildById(Config.get("GUILD-ID")).getTextChannelById(Config.get("STATUS-CHANNEL"))).editMessageById().queue();
        try {
            File myObj = new File("status-messages.txt");
            Scanner myReader = new Scanner(myObj);
            while (myReader.hasNextLine()) {
                messages.add(myReader.nextLine());
            }
            myReader.close();
        } catch (FileNotFoundException e) {
            System.out.println("An error occurred.");
            e.printStackTrace();
        }
        MessageEmbed embed = new EmbedBuilder()
                .setTitle("Status: " + Powerstate.toString())
                .setDescription("The SMP Server!")
                .setFooter("anarchy.ciputin.cf","https://i.imgur.com/i4ht6nZ.png")
                .setColor(Determinestatecolor(Powerstate))
                .build();
        Bot.getJDA().getGuildById(Config.get("GUILD-ID")).getTextChannelById(Config.get("STATUS-CHANNEL")).editMessageEmbedsById(messages.get(0), embed).queue();
    }
    private Color Determinestatecolor(UtilizationState Powerstate) {
        if (String.valueOf(Powerstate) != "RUNNING") { return Color.RED; } else { return Color.blue; }
    }
}

The container (this runs in a docker container for java) is not out of memory, but is actually only using 200/1024mb. Another issue is that with each execution, the memory usage gets higher, with no relief. But it levels out at 200mb, coincidentally when it stops working...

amanteaux commented 2 years ago

Thank you for your interest in Wisp!

I suspect that the memory leak is in your code rather than in Wisp library. To verify this, can you try to run your code in a for loop of maybe 100 iterations and see what is going on?

cixastra commented 2 years ago

I'll try that! If the memory leak isn't in Wisp I'll look elsewhere! Thanks! (Don't close it yet, just in case it is wisp :))

cixastra commented 2 years ago

I apologize! You were correct, the memory leak is in my own code. I'll have to look elsewhere. Thanks so much for this awesome scheduler!

amanteaux commented 2 years ago

You are welcome, I'm glad I could help!