package uk.ac.imperial.lsds.seepmaster.scheduler; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import uk.ac.imperial.lsds.seep.api.DataReference; import uk.ac.imperial.lsds.seep.api.RuntimeEvent; import uk.ac.imperial.lsds.seep.core.DatasetMetadata; import uk.ac.imperial.lsds.seep.core.DatasetMetadataPackage; import uk.ac.imperial.lsds.seep.scheduler.ScheduleDescription; import uk.ac.imperial.lsds.seep.scheduler.Stage; import uk.ac.imperial.lsds.seep.scheduler.StageStatus; import uk.ac.imperial.lsds.seep.scheduler.StageType; import uk.ac.imperial.lsds.seepmaster.scheduler.memorymanagement.MemoryManagementPolicy; public class ScheduleTracker { final private Logger LOG = LoggerFactory.getLogger(ScheduleTracker.class); private ScheduleDescription scheduleDescription; private Set<Stage> stages; private ScheduleStatus status; private Stage sink; private Map<Stage, StageStatus> scheduleStatus; private StageTracker currentStageTracker; // The registry of all the datasets in the cluster private ClusterDatasetRegistry clusterDatasetRegistry; // RuntimeEvents piggybacked with the status of the last stage executed private boolean runtimeEventsInLastStageExecution = false; private Map<Integer, List<RuntimeEvent>> lastStageRuntimeEvents = null; public ScheduleTracker(ScheduleDescription scheduleDescription, MemoryManagementPolicy mmp) { this.scheduleDescription = scheduleDescription; this.stages = this.scheduleDescription.getStages(); status = ScheduleStatus.NON_INITIALIZED; // Keep track of overall schedule scheduleStatus = new HashMap<>(); for(Stage stage : stages) { if(stage.getStageId() == 0) { // sanity check if(! stage.getStageType().equals(StageType.SINK_STAGE)){ LOG.error("Stage 0 is not SINK_STAGE !!"); // FIXME: throw a proper exception instead System.exit(0); } sink = stage; } scheduleStatus.put(stage, StageStatus.WAITING); } this.lastStageRuntimeEvents = new HashMap<>(); this.clusterDatasetRegistry = new ClusterDatasetRegistry(mmp); } public ScheduleDescription getScheduleDescription() { return scheduleDescription; } public ClusterDatasetRegistry getClusterDatasetRegistry() { return clusterDatasetRegistry; } public boolean didLastStageGenerateRuntimeEvents() { return this.runtimeEventsInLastStageExecution; } public Map<Integer, List<RuntimeEvent>> getRuntimeEventsOfLastStageExecution() { return this.lastStageRuntimeEvents; } public boolean isScheduledFinished() { return this.status == ScheduleStatus.FINISHED; } public Stage getHead(){ return sink; } public Map<Stage, StageStatus> stages() { return scheduleStatus; } public boolean setReady(Stage stage) { this.scheduleStatus.put(stage, StageStatus.READY); return true; } public Set<Stage> getReadySet() { Set<Stage> toReturn = new HashSet<>(); for(Stage stage : stages) { if(this.isStageReady(stage)) { toReturn.add(stage); } } return toReturn; } public boolean setFinished(Stage stage, Map<Integer, Set<DataReference>> results) { // results contains the DataReferences for downstream stages, indexed by // downstream stage id LOG.info("[FINISH] SCHEDULING Stage {}", stage.getStageId()); // Set finish this.scheduleStatus.put(stage, StageStatus.FINISHED); if(stage.getStageType().equals(StageType.SINK_STAGE)) { // Finished schedule this.status = ScheduleStatus.FINISHED; } // Check whether the new stage makes ready new stages, and propagate results for(Stage downstream : stage.getDependants()) { // Results for this stage Set<DataReference> resultsForThisStage = results.get(stage.getStageId()); if(resultsForThisStage != null) { // TODO: if the task produced results, add them downstream.addInputDataReference(stage.getStageId(), resultsForThisStage); } if(isStageReadyToRun(downstream)) { this.scheduleStatus.put(downstream, StageStatus.READY); } } return true; } public boolean isStageReady(Stage stage) { return this.scheduleStatus.get(stage).equals(StageStatus.READY); } public boolean isStageWaiting(Stage stage) { return this.scheduleStatus.get(stage).equals(StageStatus.WAITING); } public boolean isStageFinished(Stage stage) { return this.scheduleStatus.get(stage).equals(StageStatus.FINISHED); } public boolean resetAllStagesTo(StageStatus newStatus) { for(Stage st : this.scheduleStatus.keySet()) { this.scheduleStatus.put(st, newStatus); } return true; } private boolean isStageReadyToRun(Stage stage) { for(Stage st : stage.getDependencies()) { if(! scheduleStatus.get(st).equals(StageStatus.FINISHED)) { return false; } } return true; } public void trackWorkersAndBlock(Stage stage, Set<Integer> euInvolved) { currentStageTracker = new StageTracker(stage.getStageId(), euInvolved); currentStageTracker.waitForStageToFinish(); } public void waitForFinishedStageAndCompleteBookeeping(Stage stage) { currentStageTracker.waitForStageToFinish(); // Check status of the stage if(currentStageTracker.finishedSuccessfully()) { Map<Integer, Set<DataReference>> results = currentStageTracker.getStageResults(); setFinished(stage, results); } else{ LOG.warn("Not successful stage... CHECK, non-FT fully-fledged implementation yet :|"); System.exit(-1); } } public void finishStage(int euId, int stageId, Map<Integer, Set<DataReference>> results, List<RuntimeEvent> runtimeEvents, DatasetMetadataPackage managedDatasets) { // Keep runtimeEvents of last executed Stage if(runtimeEvents.size() > 0) { this.runtimeEventsInLastStageExecution = true; // Store runtimeEvents on a per node basis (eu -> execution unit) this.lastStageRuntimeEvents.put(euId, runtimeEvents); } else { this.runtimeEventsInLastStageExecution = false; } // Update DatasetRegistry clusterDatasetRegistry.updateDatasetsForNode(euId, managedDatasets, stageId); // Then notify the stageTracker that the stage was successful currentStageTracker.notifyOk(euId, stageId, results); } }