package cz.cuni.mff.d3s.been.manager; import java.util.Collection; import java.util.Collections; import java.util.LinkedList; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.hazelcast.core.EntryEvent; import com.hazelcast.core.EntryListener; import com.hazelcast.core.IMap; import com.hazelcast.query.SqlPredicate; import cz.cuni.mff.d3s.been.cluster.ServiceException; import cz.cuni.mff.d3s.been.cluster.context.ClusterContext; import cz.cuni.mff.d3s.been.core.ri.RuntimeInfo; import cz.cuni.mff.d3s.been.core.task.TaskEntry; import cz.cuni.mff.d3s.been.core.task.TaskState; import cz.cuni.mff.d3s.been.manager.msg.Messages; import cz.cuni.mff.d3s.been.manager.msg.TaskMessage; import cz.cuni.mff.d3s.been.mq.IMessageSender; import cz.cuni.mff.d3s.been.mq.MessagingException; /** * Listens on local changes of Host Runtimes. * * Takes necessary actions (i.e. rescheduling of waiting tasks). * * @author Martin Sixta */ final class LocalRuntimeListener extends TaskManagerService implements EntryListener<String, RuntimeInfo> { private static final String WAITING_TASKS_QUERY = "state = WAITING"; /** logging */ private static final Logger log = LoggerFactory.getLogger(LocalRuntimeListener.class); /** connection to the cluster */ private ClusterContext clusterCtx; /** for sending task manager messages for processing */ private IMessageSender<TaskMessage> sender; /** Map with Host Runtimes */ private final IMap<String, RuntimeInfo> runtimesMap; /** Map with tasks */ private final IMap<String, TaskEntry> tasksMap; /** * Creates LocalRuntimeListener. * * @param clusterCtx * connection to the cluster */ public LocalRuntimeListener(ClusterContext clusterCtx) { this.clusterCtx = clusterCtx; this.runtimesMap = clusterCtx.getRuntimes().getRuntimeMap(); this.tasksMap = clusterCtx.getTasks().getTasksMap(); } @Override public synchronized void entryAdded(EntryEvent<String, RuntimeInfo> event) { log.debug("Host Runtime added: {}", event.getKey()); scheduleWaitingTasks(); } @Override public synchronized void entryRemoved(EntryEvent<String, RuntimeInfo> event) { log.debug("Host Runtime removed: {}", event.getKey()); } @Override public synchronized void entryUpdated(EntryEvent<String, RuntimeInfo> event) { log.debug("Host Runtime updated: {}", event.getKey()); RuntimeInfo oldValue = event.getOldValue(); RuntimeInfo currentValue = event.getValue(); if (oldValue.getTaskCount() != currentValue.getTaskCount()) { if (currentValue.getTaskCount() == 0) { scheduleWaitingTasks(); } } } @Override public synchronized void entryEvicted(EntryEvent<String, RuntimeInfo> event) { log.warn("Host Runtime evicted: {}", event.getKey()); Collection<TaskEntry> tasks = getTasksOnRuntime(event.getValue().getId()); for (TaskEntry entry : tasks) { try { TaskState state = entry.getState(); if (state == TaskState.RUNNING) { sender.send(Messages.createAbortTaskMessage(entry, "Host Runtime Failed")); } else if (state == TaskState.SCHEDULED) { sender.send(Messages.createScheduleTaskMessage(entry)); } } catch (MessagingException e) { String msg = String.format("Cannot send message to '%s'", sender.getConnection()); log.error(msg, e); } } } @Override public void start() throws ServiceException { sender = createSender(); runtimesMap.addLocalEntryListener(this); } @Override public void stop() { runtimesMap.removeEntryListener(this); sender.close(); } /** * Reschedules all waiting tasks. */ private void scheduleWaitingTasks() { for (TaskEntry entry : getWaitingTasks()) { try { TaskMessage msg = Messages.createTaskChangedMessage(entry); sender.send(msg); } catch (MessagingException e) { String msg = String.format("Cannot send message to '%s'", sender.getConnection()); log.error(msg, e); } } } /** * Returns all tasks waiting to be scheduled * * @return all waiting tasks */ private Collection<TaskEntry> getWaitingTasks() { try { final Collection<TaskEntry> values = tasksMap.values(new SqlPredicate(WAITING_TASKS_QUERY)); Collection<TaskEntry> waitingTasks = new LinkedList<>(); for (TaskEntry entry : values) { if (entry.getTaskDependency() == null) { waitingTasks.add(entry); } } return waitingTasks; } catch (Exception e) { log.error("Error while looking for waiting tasks", e); } return Collections.emptyList(); } private static final String TASKS_ON_RUNTIME_FMT = "runtimeId = '%s'"; /** * Returns all waiting tasks * * @return all waiting tasks */ private Collection<TaskEntry> getTasksOnRuntime(String runtimeId) { try { String query = String.format(TASKS_ON_RUNTIME_FMT, runtimeId); return tasksMap.values(new SqlPredicate(query)); } catch (Exception e) { log.error("Error while looking tasks on a runtime", e); } return Collections.emptyList(); } }