package com.sungardas.enhancedsnapshots.components; import com.amazonaws.AmazonClientException; import com.sungardas.enhancedsnapshots.aws.dynamodb.model.NodeEntry; import com.sungardas.enhancedsnapshots.aws.dynamodb.model.TaskEntry; import com.sungardas.enhancedsnapshots.aws.dynamodb.repository.NodeRepository; import com.sungardas.enhancedsnapshots.aws.dynamodb.repository.TaskRepository; import com.sungardas.enhancedsnapshots.exception.EnhancedSnapshotsInterruptedException; import com.sungardas.enhancedsnapshots.exception.EnhancedSnapshotsTaskInterruptedException; import com.sungardas.enhancedsnapshots.service.NotificationService; import com.sungardas.enhancedsnapshots.service.SDFSStateService; import com.sungardas.enhancedsnapshots.service.SystemService; import com.sungardas.enhancedsnapshots.service.TaskService; import com.sungardas.enhancedsnapshots.tasks.executors.TaskExecutor; import com.sungardas.enhancedsnapshots.util.SystemUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.DependsOn; import org.springframework.stereotype.Service; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; import java.util.*; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import static com.sungardas.enhancedsnapshots.aws.dynamodb.model.TaskEntry.TaskEntryStatus.*; @Service @DependsOn({"ConfigurationMediator", "MasterService"}) public class WorkersDispatcher { private static final Comparator<TaskEntry> taskComparatorByTimeAndPriority = (o1, o2) -> { int priority = o2.getPriority() - o1.getPriority(); if (priority != 0) { return priority; } return o1.getSchedulerTime().compareTo(o2.getSchedulerTime()); }; @Autowired private ConfigurationMediator configurationMediator; @Autowired @Qualifier("awsBackupVolumeTaskExecutor") private TaskExecutor awsBackupVolumeTaskExecutor; @Autowired @Qualifier("awsDeleteTaskExecutor") private TaskExecutor awsDeleteTaskExecutor; @Autowired @Qualifier("awsRestoreVolumeTaskExecutor") private TaskExecutor awsRestoreVolumeTaskExecutor; @Autowired private TaskService taskService; @Autowired private SDFSStateService sdfsStateService; @Autowired private SystemService systemService; @Autowired private TaskRepository taskRepository; @Autowired private NotificationService notificationService; @Autowired private NodeRepository nodeRepository; private ExecutorService executor; private ThreadPoolExecutor backupExecutor; private ThreadPoolExecutor restoreExecutor; @Value("${enhancedsnapshots.default.backup.threadPool.size}") private int backupThreadPoolSize; @Value("${enhancedsnapshots.default.restore.threadPool.size}") private int restoreThreadPoolSize; private String instanceId; @PostConstruct private void init() { instanceId = SystemUtils.getInstanceId(); backupExecutor = (ThreadPoolExecutor) Executors.newFixedThreadPool(backupThreadPoolSize); restoreExecutor = (ThreadPoolExecutor) Executors.newFixedThreadPool(restoreThreadPoolSize); executor = Executors.newSingleThreadExecutor(); executor.execute(new TaskWorker()); } @PreDestroy public void destroy() { executor.shutdownNow(); backupExecutor.shutdownNow(); restoreExecutor.shutdownNow(); } private Set<TaskEntry> sortByTimeAndPriority(List<TaskEntry> list) { Set<TaskEntry> result = new TreeSet<>(taskComparatorByTimeAndPriority); result.addAll(list); return result; } private class TaskWorker implements Runnable { private final Logger LOGtw = LogManager.getLogger(TaskWorker.class); @Override public void run() { LOGtw.info("Starting worker dispatcher"); while (true) { if (Thread.interrupted()) { throw new EnhancedSnapshotsInterruptedException("Task interrupted"); } if (configurationMediator.isClusterMode()) { NodeEntry nodeEntry = nodeRepository.findOne(instanceId); nodeEntry.setFreeBackupWorkers(backupThreadPoolSize - backupExecutor.getActiveCount()); nodeEntry.setFreeRestoreWorkers(restoreThreadPoolSize - restoreExecutor.getActiveCount()); nodeRepository.save(nodeEntry); } TaskEntry entry = null; try { Set<TaskEntry> taskEntrySet = getAssignedTasks(); while (!taskEntrySet.isEmpty()) { entry = taskEntrySet.iterator().next(); try { if (taskService.exists(entry.getId())) { switch (TaskEntry.TaskEntryType.getType(entry.getType())) { case BACKUP: //TODO remove when sdfscli will allow to expand cache size at runtime if (!sdfsStateService.sdfsIsAvailable()) { break; } LOGtw.info("Task was identified as backup"); entry.setStatus(WAITING.getStatus()); taskRepository.save(entry); TaskEntry backupTask = entry; backupExecutor.submit(() -> awsBackupVolumeTaskExecutor.execute(backupTask)); break; case DELETE: { LOGtw.info("Task was identified as delete backup"); awsDeleteTaskExecutor.execute(entry); break; } case RESTORE: if (!sdfsStateService.sdfsIsAvailable()) { break; } LOGtw.info("Task was identified as restore"); entry.setStatus(WAITING.getStatus()); taskRepository.save(entry); TaskEntry restoreTask = entry; restoreExecutor.submit(() -> awsRestoreVolumeTaskExecutor.execute(restoreTask)); break; case SYSTEM_BACKUP: { LOGtw.info("Task was identified as system backup"); notificationService.notifyAboutRunningTaskProgress(entry.getId(), "System backup started", 0); entry.setStatus(RUNNING.getStatus()); taskRepository.save(entry); systemService.backup(entry.getId()); taskRepository.delete(entry); notificationService.notifyAboutRunningTaskProgress(entry.getId(), "System backup finished", 100); break; } case UNKNOWN: { LOGtw.warn("Executor for type {} is not implemented. Task {} is going to be removed.", entry.getType(), entry.getId()); taskService.removeTask(entry.getId()); } } } else { LOGtw.debug("Task canceled: {}", entry); } } catch (EnhancedSnapshotsTaskInterruptedException e) { //skip if task canceled LOGtw.debug("Task canceled: {}", entry); } taskEntrySet = getAssignedTasks(); } sleep(); } catch (AmazonClientException e) { LOGtw.error(e); } catch (EnhancedSnapshotsInterruptedException e) { return; // this is required to close thread when uninstalling system } catch (IllegalStateException e) { LOGtw.warn("Stopping worker dispatcher ..."); return; } catch (Exception e) { LOGtw.error(e); if (entry != null) { entry.setStatus(ERROR.getStatus()); taskRepository.save(entry); } } } } private Set<TaskEntry> getAssignedTasks() { List<TaskEntry> taskEntries = new ArrayList<>(); taskEntries.addAll(taskRepository.findByStatusAndRegularAndWorker(TaskEntry.TaskEntryStatus.QUEUED.getStatus(), Boolean.FALSE.toString(), instanceId)); taskEntries.addAll(taskRepository.findByStatusAndRegularAndWorker(TaskEntry.TaskEntryStatus.PARTIALLY_FINISHED.getStatus(), Boolean.FALSE.toString(), instanceId)); return sortByTimeAndPriority(taskEntries); } private void sleep() { try { TimeUnit.MILLISECONDS.sleep(configurationMediator.getWorkerDispatcherPollingRate()); } catch (InterruptedException e) { e.printStackTrace(); } } } }