All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.endoscope.core.CurrentStatsAsyncTasks Maven / Gradle / Ivy

The newest version!
package com.github.endoscope.core;

import java.io.ByteArrayOutputStream;
import java.io.PrintWriter;
import java.util.UUID;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import com.github.endoscope.storage.StatsPersistence;
import com.github.endoscope.storage.Storage;
import org.slf4j.Logger;

import static org.slf4j.LoggerFactory.getLogger;

public class CurrentStatsAsyncTasks implements AsyncTasksFactory {
    private static final Logger log = getLogger(CurrentStatsAsyncTasks.class);
    public static final String COLLECTOR_THREAD_NAME = "endoscope-stats-collect";
    public static final String SAVING_THREAD_NAME = "endoscope-stats-save";
    public static final String COLLECTOR_ID = UUID.randomUUID().toString();
    public static final String SAVING_ID = UUID.randomUUID().toString();

    private ExecutorService collectingExecutor;
    private ExecutorService savingExecutor;
    private CurrentStats currentStats;
    private StatsPersistence statsPersistence;
    private boolean enabled = true;
    private Future collectingTaskResult;
    private Future savingTaskResult;

    public CurrentStatsAsyncTasks(CurrentStats currentStats, Storage storage) {
        this.currentStats = currentStats;
        this.statsPersistence = new StatsPersistence(storage);

        collectingExecutor = new ThreadPoolExecutor(0, 1,
                60L, TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(),
                runnable -> {
                    Thread t = Executors.defaultThreadFactory().newThread(runnable);
                    t.setDaemon(true);//we don't want to block JVM shutdown
                    t.setName(COLLECTOR_THREAD_NAME);
                    return t;
        });

        savingExecutor = new ThreadPoolExecutor(0, 1,
                60L, TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(),
                runnable -> {
                    Thread t = Executors.defaultThreadFactory().newThread(runnable);
                    t.setDaemon(true);//we don't want to block JVM shutdown
                    t.setName(SAVING_THREAD_NAME);
                    return t;
                });
    }

    public void triggerAsyncTask() {
        try{
            if (!enabled || (collectingTaskResult != null && !collectingTaskResult.isDone())) {
                //previous task is still running
                //it's not a perfect synchronization but it doesn't have to be
                return;
            }
            log.debug("Creating new async task for collector: {}", COLLECTOR_ID);
            collectingTaskResult = collectingExecutor.submit(() -> {

                //this stuff runs in new thread

                safeSleep();
                log.debug("started async task for collector: {}", COLLECTOR_ID);
                try {
                    currentStats.processAllFromQueue();
                    if (statsPersistence != null && statsPersistence.threadSafeShouldSave()) {
                        //run save in another thread so we don't block processing elements from queue
                        triggerAsyncSafeSave();
                    }
                } catch (Exception e) {
                    currentStats.setFatalError(getStacktrace(e));
                    log.debug("error occurred when processing async task for collector: {}", COLLECTOR_ID, e);
                }
                log.debug("finished async task for collector: {}", COLLECTOR_ID);
            });
            log.debug("Created new async task for collector: {}", COLLECTOR_ID);
        }catch(Exception e){
            log.warn("Failed to trigger async task for collector: {}", COLLECTOR_ID, e);
        }
    }

    private void triggerAsyncSafeSave() {
        try{
            if (!enabled ) {
                return;
            }
            if (savingTaskResult != null && !savingTaskResult.isDone()) {
                log.debug("Previous async task for saving stats: {} is still running...skipping", SAVING_ID);
                //it's not a perfect synchronization but it doesn't have to be
                return;
            }
            log.debug("Creating new async task for saving stats: {}", SAVING_ID);
            savingTaskResult = savingExecutor.submit(() -> {

                //this stuff runs in new thread

                safeSleep();
                log.debug("started async task for saving stats: {}", SAVING_ID);
                try {
                    safeSave();
                } catch (Exception e) {
                    log.debug("error occurred when processing async task for saving stats: {}", SAVING_ID, e);
                }
                log.debug("finished async task for saving stats: {}", SAVING_ID);
            });
            log.debug("Created new async task for saving stats: {}", SAVING_ID);
        }catch(Exception e){
            log.warn("Failed to trigger async task for saving stats: {}", SAVING_ID, e);
        }
    }

    private boolean safeSave() {
        //this stuff runs in new thread

        log.debug("persisting stats");

        //don't lock for long here! - just get stats we plan to save and start to collect new values
        Stats oldStats = currentStats.lockReadStats(stats -> currentStats.resetStats() );

        boolean saved = statsPersistence.safeSave(oldStats);
        if( !saved ){
            log.debug("failed to save stats - returning stats in order to try again later");
            currentStats.lockReadStats(stats -> {
                stats.merge(oldStats, true);
                return true;
            });
        } else {
            log.debug("saved stats - running stats cleanup");
            statsPersistence.safeCleanup();
        }

        log.debug("finished persisting stats");
        return true;
    }

    public void stopStatsProcessorThread() {
        log.info("Requested threads: {}, {} shutdown", COLLECTOR_THREAD_NAME, SAVING_THREAD_NAME);
        enabled = false;
        collectingExecutor.shutdownNow();
        savingExecutor.shutdownNow();
    }

    public boolean isEnabled() {
        return enabled;
    }

    private static void safeSleep() {
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
        }
    }

    private static String getStacktrace(Exception e) {
        ByteArrayOutputStream buf = new ByteArrayOutputStream();
        PrintWriter pw = new PrintWriter(buf);
        e.printStackTrace(pw);
        pw.flush();
        return buf.toString();
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy