/* * ProActive Parallel Suite(TM): * The Open Source library for parallel and distributed * Workflows & Scheduling, Orchestration, Cloud Automation * and Big Data Analysis on Enterprise Grids & Clouds. * * Copyright (c) 2007 - 2017 ActiveEon * Contact: contact@activeeon.com * * This library is free software: you can redistribute it and/or * modify it under the terms of the GNU Affero General Public License * as published by the Free Software Foundation: version 3 of * the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * * If needed, contact us to obtain a release under GPL Version 2 or 3 * or a different license than the AGPL. */ package functionaltests.job.scheduling; import java.io.File; import java.net.URL; import java.util.ArrayList; import java.util.List; import org.apache.commons.lang3.tuple.ImmutablePair; import org.apache.commons.lang3.tuple.Pair; import org.junit.After; import org.junit.Assert; import org.junit.Test; import org.ow2.proactive.scheduler.common.Scheduler; import org.ow2.proactive.scheduler.common.job.Job; import org.ow2.proactive.scheduler.common.job.JobId; import org.ow2.proactive.scheduler.common.job.JobPriority; import org.ow2.proactive.scheduler.common.job.TaskFlowJob; import org.ow2.proactive.scheduler.common.job.factories.JobFactory; import org.ow2.proactive.scheduler.common.task.JavaTask; import org.ow2.proactive.scheduler.common.task.ParallelEnvironment; import org.ow2.proactive.scheduler.common.task.TaskState; import org.ow2.proactive.scheduler.examples.EmptyTask; import org.ow2.proactive.scripting.SelectionScript; import com.google.common.collect.ImmutableMap; import functionaltests.utils.RMTHelper; import functionaltests.utils.SchedulerFunctionalTestNonForkModeWithRestart; /** * Several tests related to job scheduling priorities and starvation */ public class TestJobSchedulingStarvationAndPriority extends SchedulerFunctionalTestNonForkModeWithRestart { private static URL jobDescriptor = TestJobSchedulingStarvationAndPriority.class.getResource("/functionaltests/descriptors/Job_With_Replication.xml"); private static final String REPLICATE_TASK_NAME = "task2replicate"; private static final String REPLICATE_TASK_NAME_FILTER = REPLICATE_TASK_NAME + ".*"; /** * Tests that a starvation does not occur with selection scripts. A set of high-priority task with a selection script * returning false does not prevent a low priority task to run. * * @throws Exception */ @Test public void testStarvationSelectionScript() throws Exception { schedulerHelper.log("testStarvationSelectionScript"); Scheduler scheduler = schedulerHelper.getSchedulerInterface(); JobId jobIdLow; List<JobId> jobIds = new ArrayList<>(RMTHelper.DEFAULT_NODES_NUMBER); for (int i = 0; i < RMTHelper.DEFAULT_NODES_NUMBER; i++) { jobIds.add(scheduler.submit(createJobHighSelectFalse())); } jobIdLow = scheduler.submit(createJobLow()); schedulerHelper.waitForEventTaskRunning(jobIdLow, "taskLow"); for (int i = 0; i < RMTHelper.DEFAULT_NODES_NUMBER; i++) { schedulerHelper.killJob("" + jobIds.get(i)); } } /** * Tests that a starvation does not occur with multi-node tasks : * A high-priority task needing more nodes than available does not prevent a low priority task to execute. * * @throws Exception */ @Test public void testStarvationMultiNode() throws Exception { schedulerHelper.log("testStarvationMultiNode"); Scheduler scheduler = schedulerHelper.getSchedulerInterface(); JobId jobIdLow; JobId jobIdHigh; jobIdHigh = scheduler.submit(createJobHighMoreMultiNode()); jobIdLow = scheduler.submit(createJobLow()); schedulerHelper.waitForEventTaskRunning(jobIdLow, "taskLow"); schedulerHelper.killJob("" + jobIdHigh); } /** * Tests that all replicated tasks in a low priority job are executed AFTER replicated tasks in a high priority one * * @throws Exception */ @Test public void testJobPriorityStandard() throws Exception { schedulerHelper.log("testJobPriorityStandard"); testJobPriority(false); } /** * Tests that all replicated tasks in a low priority job are executed AFTER replicated tasks in a high priority one * * Additionally, add extra nodes during the run and ensure that new nodes are not picked by low priority tasks * * @throws Exception */ @Test public void testJobPriorityWithNewNodes() throws Exception { schedulerHelper.log("testJobPriorityWithNewNodes"); testJobPriority(true); } public void testJobPriority(boolean addNewNodes) throws Exception { Scheduler scheduler = schedulerHelper.getSchedulerInterface(); int nbNewNodes = 15; int nbRunsHigh = RMTHelper.DEFAULT_NODES_NUMBER * 2 + (addNewNodes ? nbNewNodes * 3 : 0); int nbRunsLow = RMTHelper.DEFAULT_NODES_NUMBER * 2 + (addNewNodes ? nbNewNodes * 2 : 0); String jobDescriptorPath = new File(jobDescriptor.toURI()).getAbsolutePath(); Job jobHigh = JobFactory.getFactory().createJob(jobDescriptorPath, ImmutableMap.of("RUNS", "" + nbRunsHigh)); jobHigh.setPriority(JobPriority.HIGH); JobId jobIdHigh = schedulerHelper.submitJob(jobHigh); schedulerHelper.waitForEventTaskRunning(jobIdHigh, REPLICATE_TASK_NAME); Job jobLow = JobFactory.getFactory().createJob(jobDescriptorPath, ImmutableMap.of("RUNS", "" + nbRunsLow)); jobLow.setPriority(JobPriority.LOW); JobId jobIdLow = schedulerHelper.submitJob(jobLow); if (addNewNodes) { schedulerHelper.addExtraNodes(nbNewNodes); } schedulerHelper.waitForEventJobFinished(jobIdHigh); schedulerHelper.waitForEventJobFinished(jobIdLow); Pair<Long, Long> minMaxHigh = computeMinMaxStartingTime(scheduler, jobIdHigh, REPLICATE_TASK_NAME_FILTER); Pair<Long, Long> minMaxLow = computeMinMaxStartingTime(scheduler, jobIdLow, REPLICATE_TASK_NAME_FILTER); Assert.assertTrue("Low Priority tasks min start time : " + minMaxLow.getLeft() + " should be greater than the max start time of high priority jobs : " + minMaxHigh.getRight(), minMaxLow.getLeft() > minMaxHigh.getRight()); } /** * Computes min start time and max start time for all tasks which match a given pattern * If a task in the set did not start, then the set max will be Long.MAX_VALUE */ Pair<Long, Long> computeMinMaxStartingTime(Scheduler scheduler, JobId jobId, String taskNameFilter) throws Exception { long min = Long.MAX_VALUE; long max = -1; for (TaskState state : scheduler.getJobState(jobId).getTasks()) { if (state.getName().matches(taskNameFilter)) { long startTime = state.getStartTime() > -1 ? state.getStartTime() : Long.MAX_VALUE; min = Math.min(min, startTime); max = Math.max(max, startTime); } } return new ImmutablePair<>(min, max); } @After public void releaseNodes() { try { schedulerHelper.removeExtraNodeSource(); } catch (Exception ignored) { } } /* * Job high priority with one task, task's selection script always returns 'false' so task can't * start */ private TaskFlowJob createJobHighSelectFalse() throws Exception { TaskFlowJob job = new TaskFlowJob(); job.setName(this.getClass().getSimpleName() + "_High_SelectFalse"); job.setPriority(JobPriority.HIGHEST); JavaTask javaTask = new JavaTask(); javaTask.setExecutableClassName(EmptyTask.class.getName()); javaTask.setName("taskSelectFalse"); SelectionScript selScript = new SelectionScript("selected = false;", "js"); javaTask.setSelectionScript(selScript); job.addTask(javaTask); return job; } /* * Job high priority with one task and with a required number of nodes greater than currently * available */ private TaskFlowJob createJobHighMoreMultiNode() throws Exception { TaskFlowJob job = new TaskFlowJob(); job.setName(this.getClass().getSimpleName() + "_High_MoreMultiNode"); job.setPriority(JobPriority.HIGHEST); JavaTask javaTask = new JavaTask(); javaTask.setExecutableClassName(EmptyTask.class.getName()); javaTask.setName("taskMoreMultiNode"); javaTask.setParallelEnvironment(new ParallelEnvironment(RMTHelper.DEFAULT_NODES_NUMBER + 1)); job.addTask(javaTask); return job; } /* * Job high priority with one task and high priority */ private TaskFlowJob createJobReplicate(int nbRun, JobPriority priority) throws Exception { String jobDescriptorPath = new File(jobDescriptor.toURI()).getAbsolutePath(); TaskFlowJob job = new TaskFlowJob(); job.setName(this.getClass().getSimpleName() + "_High"); job.setPriority(JobPriority.HIGHEST); JavaTask javaTask = new JavaTask(); javaTask.setExecutableClassName(EmptyTask.class.getName()); javaTask.setName("taskHigh"); job.addTask(javaTask); return job; } /* * Job high priority with one task and low priority */ private TaskFlowJob createJobLow() throws Exception { TaskFlowJob job = new TaskFlowJob(); job.setName(this.getClass().getSimpleName() + "_Low"); job.setPriority(JobPriority.LOW); JavaTask javaTask = new JavaTask(); javaTask.setExecutableClassName(EmptyTask.class.getName()); javaTask.setName("taskLow"); job.addTask(javaTask); return job; } }