Search in sources :

Example 1 with MantaroShard

use of net.kodehawa.mantarobot.core.shard.MantaroShard in project MantaroBot by Mantaro.

the class ShardWatcher method run.

@Override
public void run() {
    LogUtils.shard("ShardWatcherThread started");
    // Executes the restart queue handler. For the actual logic behind all this, check the next while(true) loop.
    THREAD_POOL.execute(() -> {
        while (true) {
            MantaroShard shard = RESTART_QUEUE.poll();
            if (shard == null) {
                // poll the queue every 10 seconds
                try {
                    Thread.sleep(10000);
                } catch (InterruptedException e) {
                    LogUtils.shard("Shard restarter task interrupted");
                    return;
                }
                // Continue to the next loop cycle if no shard is on the restart queue.
                continue;
            }
            // Alert us, plz no panic
            LogUtils.shard(String.format("(Resume request failed or errored) " + "Dead shard? Starting automatic shard restart on shard #%d due to it being inactive for longer than 30 seconds.", shard.getId()));
            try {
                // Reboot the shard.
                shard.start(true);
            } catch (Exception e) {
                // If the shard wasn't able to restart by itself, alert us so we can reboot manually later.
                LogUtils.shard(String.format("Shard %d was unable to be restarted: %s", shard.getId(), e));
            }
            try {
                // Wait 5 seconds as a backoff.
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                LogUtils.shard("Shard restarter task interrupted");
                return;
            }
        }
    });
    final int wait = MantaroData.config().get().shardWatcherWait;
    while (true) {
        try {
            // Run every x ms (usually every 10 minutes unless changed)
            Thread.sleep(wait);
            MantaroEventManager.getLog().info("Checking shards...");
            // Just in case...
            if (shardedMantaro == null)
                shardedMantaro = MantaroBot.getInstance().getShardedMantaro();
            // Get and propagate the shard event.
            // This event will propagate over all Mantaro-specific listeners, and see if the shards are responding accordingly.
            ShardMonitorEvent sme = new ShardMonitorEvent(shardedMantaro.getTotalShards());
            EventUtils.propagateEvent(sme);
            // Start the procedure...
            int[] dead = sme.getDeadShards();
            // Oh well... we can try to recover them now!
            if (dead.length != 0) {
                MantaroEventManager.getLog().error("Dead shards found: {}", Arrays.toString(dead));
                // restart on..." kinda message.
                if (dead.length > 15) {
                    LogUtils.shard("Seems like Megumin struck our castle and we got a horribly high amount of dead shards (" + dead.length + ")\n" + "This could be just due to them reconnecting though, if nothing appears down there talking about how the shards are rebooting " + "you might aswell ignore this warning.");
                }
                // Under the hood this basically calls for a RESUME JDA instance and if it fails, it adds it to the restart queue to replace it with a completely new one.
                for (int id : dead) {
                    try {
                        MantaroShard shard = MantaroBot.getInstance().getShard(id);
                        // But, if the shard has been inactive for too long, we're better off scrapping this session as the shard might be stuck on connecting.
                        if ((shard.getStatus() == JDA.Status.RECONNECT_QUEUED || shard.getStatus() == JDA.Status.ATTEMPTING_TO_RECONNECT || shard.getStatus() == JDA.Status.SHUTDOWN) && shard.getEventManager().getLastJDAEventTimeDiff() < 200000) {
                            LogUtils.shard(String.format("Skipping shard %d due to it being currently reconnecting to the websocket or was shutdown manually...", id));
                            continue;
                        }
                        LogUtils.shard(String.format("Found dead shard (#%d)... attempting RESUME request and waiting 20 seconds to validate.", id));
                        // Send the RESUME request.
                        ((JDAImpl) (shard.getJDA())).getClient().close(4000);
                        RESUME_WAITER.schedule(() -> {
                            if (shard.getEventManager().getLastJDAEventTimeDiff() > 18000) {
                                RESTART_QUEUE.add(shard);
                            }
                        }, 20, TimeUnit.SECONDS);
                    } catch (Exception e) {
                        // Print the exception so we can look at it later...
                        e.printStackTrace();
                        // Force add into the queue
                        RESUME_WAITER.schedule(() -> RESTART_QUEUE.add(MantaroBot.getInstance().getShard(id)), 30, TimeUnit.SECONDS);
                        // Somehow we couldn't reboot the shard.
                        LogUtils.shard(String.format("Cannot restart shard %d. Try to do it manually.", id));
                    }
                }
            } else {
                // yay
                MantaroEventManager.getLog().info("No dead shards found");
                long ping = MantaroBot.getInstance().getPing();
                // We might have a few soft-dead shards on here... (or internet went to shit)
                if (ping > 400) {
                    LogUtils.shard(String.format("No dead shards found, but average ping is high (%dms). Ping breakdown: %s", ping, Arrays.toString(MantaroBot.getInstance().getPings())));
                }
            }
        } catch (InterruptedException e) {
            // Just in case we stop this for any reason, we want to know that we interrupted this, just so we know we won't have a shard watcher running on the background.
            log.error("ShardWatcher interrupted, stopping...");
            LogUtils.shard("ShardWatcher interrupted, stopping...");
            return;
        }
    }
}
Also used : ShardMonitorEvent(net.kodehawa.mantarobot.core.listeners.events.ShardMonitorEvent) MantaroShard(net.kodehawa.mantarobot.core.shard.MantaroShard)

Example 2 with MantaroShard

use of net.kodehawa.mantarobot.core.shard.MantaroShard in project MantaroBot by Mantaro.

the class MantaroBot method startCheckingBirthdays.

public void startCheckingBirthdays() {
    ScheduledExecutorService executorService = Executors.newScheduledThreadPool(2);
    // How much until tomorrow? That's the initial delay, then run it once a day.
    ZoneId z = ZoneId.of("America/Chicago");
    ZonedDateTime now = ZonedDateTime.now(z);
    LocalDate tomorrow = now.toLocalDate().plusDays(1);
    ZonedDateTime tomorrowStart = tomorrow.atStartOfDay(z);
    Duration duration = Duration.between(now, tomorrowStart);
    long millisecondsUntilTomorrow = duration.toMillis();
    // It actually cut off the time from 50 minutes to 20 seconds.
    for (MantaroShard shard : core.getShardedInstance().getShards()) {
        shard.startBirthdayTask(millisecondsUntilTomorrow);
    }
    // Start the birthday cacher.
    executorService.scheduleWithFixedDelay(birthdayCacher::cache, 22, 22, TimeUnit.HOURS);
}
Also used : ScheduledExecutorService(java.util.concurrent.ScheduledExecutorService) ZoneId(java.time.ZoneId) ZonedDateTime(java.time.ZonedDateTime) MantaroShard(net.kodehawa.mantarobot.core.shard.MantaroShard) Duration(java.time.Duration) LocalDate(java.time.LocalDate)

Aggregations

MantaroShard (net.kodehawa.mantarobot.core.shard.MantaroShard)2 Duration (java.time.Duration)1 LocalDate (java.time.LocalDate)1 ZoneId (java.time.ZoneId)1 ZonedDateTime (java.time.ZonedDateTime)1 ScheduledExecutorService (java.util.concurrent.ScheduledExecutorService)1 ShardMonitorEvent (net.kodehawa.mantarobot.core.listeners.events.ShardMonitorEvent)1