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

com.netgrif.application.engine.elastic.service.ElasticTaskQueueManager Maven / Gradle / Ivy

Go to download

System provides workflow management functions including user, role and data management.

There is a newer version: 6.3.3
Show newest version
package com.netgrif.application.engine.elastic.service;

import com.netgrif.application.engine.elastic.domain.ElasticJob;
import com.netgrif.application.engine.elastic.domain.ElasticTask;
import com.netgrif.application.engine.elastic.domain.ElasticTaskJob;
import com.netgrif.application.engine.elastic.domain.ElasticTaskRepository;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;

import javax.annotation.PreDestroy;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.*;

@Slf4j
@Service
public class ElasticTaskQueueManager {

    private final ThreadPoolTaskExecutor elasticTaskExecutor;

    private final ElasticTaskRepository repository;

    private final ConcurrentHashMap> taskQueues = new ConcurrentHashMap<>();
    private final Set activeTasks = ConcurrentHashMap.newKeySet();

    public ElasticTaskQueueManager(@Qualifier("elasticTaskExecutor") ThreadPoolTaskExecutor elasticTaskExecutor, ElasticTaskRepository repository) {
        this.elasticTaskExecutor = elasticTaskExecutor;
        this.repository = repository;
    }


    public Future scheduleOperation(ElasticTaskJob task) {
        if (task.getTask().getTaskId() == null) {
            throw new IllegalArgumentException("Task id cannot be null");
        }

        String taskId = task.getTaskId();
        log.debug("Scheduling operation for task: {}", taskId);

        CompletableFuture future = new CompletableFuture<>();
        Runnable taskWrapper = () -> {
            try {
                ElasticTask result = processTask(task);
                future.complete(result);
            } catch (Exception e) {
                future.completeExceptionally(e);
            }
        };

        BlockingQueue queue = taskQueues.computeIfAbsent(taskId, k -> new LinkedBlockingQueue<>());
        try {
            queue.add(taskWrapper);
        } catch (Exception e) {
            log.error("Queue error:" + e.getMessage());
            throw e;
        }

        if (activeTasks.add(taskId)) {
            log.debug("Task {} is ready for processing. Submitting to executor.", taskId);
            elasticTaskExecutor.submit(Objects.requireNonNull(queue.poll()));
        } else {
            log.debug("Task {} is queued for processing.", taskId);
        }
        return future;
    }

    private ElasticTask processTask(ElasticTaskJob task) {
        try {
            log.debug("Processing task: {}", task.getTaskId());
            switch (task.getJobType()) {
                case INDEX:
                    return indexTaskWorker(task.getTask());
                case REMOVE:
                    return removeTaskWorker(task.getTask());
                default:
                    log.warn("Unknown job type for task: {}", task.getTaskId());
                    throw new IllegalArgumentException("Unknown job type: " + task.getJobType());
            }
        } catch (Exception e) {
            log.error("Error processing task {}: {}", task.getTaskId(), e.getMessage(), e);
            throw e;
        } finally {
            activeTasks.remove(task.getTaskId());
            scheduleNextTask(task.getTaskId());
        }
    }


    private void scheduleNextTask(String taskId) {
        BlockingQueue queue = taskQueues.get(taskId);
        if (queue != null) {
            Runnable nextTask = queue.poll();
            if (nextTask != null) {
                log.debug("Submitting next task for ID: {} to executor", taskId);
                elasticTaskExecutor.submit(nextTask);
            } else {
                activeTasks.remove(taskId);
                taskQueues.remove(taskId);
            }
        }
    }


    @PreDestroy
    public void destroy() throws InterruptedException {
        log.info("Shutting down ElasticTaskQueueManager");
        elasticTaskExecutor.shutdown();
    }


    private ElasticTask indexTaskWorker(ElasticTask task) {
        log.debug("Indexing task [{}] in thread [{}]", task.getTaskId(), Thread.currentThread().getName());
        ElasticTask elasticTask = null;
        try {
            elasticTask = repository.findByStringId(task.getStringId());
            if (elasticTask == null) {
                elasticTask = repository.save(task);
            } else {
                elasticTask.update(task);
                elasticTask = repository.save(elasticTask);
            }
            log.debug("[{}]: Task \"{}\" [{}] indexed", task.getCaseId(), task.getTitle(), task.getStringId());
        } catch (InvalidDataAccessApiUsageException e) {
            log.debug("[{}]: Task \"{}\" has duplicates, will be reindexed", task.getCaseId(), task.getTitle());
            repository.deleteAllByStringId(task.getStringId());
            repository.save(task);
            log.debug("[{}]: Task \"{}\" indexed", task.getCaseId(), task.getTitle());
        } catch (RuntimeException e) {
            log.error("Elastic executor was killed before finish: {}", e.getMessage());
        }
        return elasticTask;
    }

    private ElasticTask removeTaskWorker(ElasticTask task) {
        log.debug("Remove task [{}] in thread [{}]", task.getTaskId(), Thread.currentThread().getName());
        try {
            log.debug("[{}]: Task \"{}\" [{}] removed", task.getCaseId(), task.getTitle(), task.getStringId());
            return repository.deleteAllByTaskId(task.getTaskId());
        } catch (RuntimeException e) {
            log.error("Elastic executor was killed before finish: {}", e.getMessage());
        }
        return task;
    }

    public void removeTasksByProcess(String processId) {
        List tasks = repository.findAllByProcessId(processId);
        long maxWaitTime = 30;
        long baseWaitTime = 1;

        tasks.forEach(task -> {
            ElasticTaskJob job = new ElasticTaskJob(ElasticJob.REMOVE, task);
            CompletableFuture removeJobFuture = CompletableFuture.supplyAsync(() -> processTask(job), elasticTaskExecutor);

            long waitTime = baseWaitTime;
            while (true) {
                try {
                    removeJobFuture.get(waitTime, TimeUnit.SECONDS);
                    break;
                } catch (TimeoutException e) {
                    if (waitTime >= maxWaitTime) {
                        log.error("Timeout: Task {} did not complete within {} seconds", task.getTaskId(), maxWaitTime);
                        break;
                    }
                    waitTime *= 2;
                } catch (InterruptedException | ExecutionException e) {
                    log.error("Exception during task execution: {}", e.getMessage(), e);
                    break;
                }
            }
        });
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy