/* * 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 org.ow2.proactive.scheduler.core.db.schedulerdb; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import org.ow2.proactive.scheduler.common.job.JobId; import org.ow2.proactive.scheduler.common.job.TaskFlowJob; import org.ow2.proactive.scheduler.common.task.JavaTask; import org.ow2.proactive.scheduler.job.InternalJob; @RunWith(Parameterized.class) public class SchedulerDbManagerConcurrencyTest extends BaseSchedulerDBTest { private Scenario scenario; @Parameterized.Parameters(name = "{1}") public static Collection<Object[]> data() { return Arrays.asList(new Object[][] { { new ConcurrentJobInsertionScenario(), ConcurrentJobInsertionScenario.class.getSimpleName() }, { new ConcurrentJobDeletionScenario(true), ConcurrentJobDeletionScenario.class.getSimpleName() + "DeleteData" }, { new ConcurrentJobDeletionScenario(false), ConcurrentJobDeletionScenario.class.getSimpleName() + "DoNotDeleteData" }, { new ConcurrentJobInsertionAndDeletionScenario(), ConcurrentJobInsertionAndDeletionScenario.class.getSimpleName() } }); } public SchedulerDbManagerConcurrencyTest(Scenario scenario, String testName) { // second parameter 'testName' is used by the @Parameterized.Parameters // annotation this.scenario = scenario; } @Test(timeout = 60000) public void test() throws Exception { scenario.doTest(this); } private static class ConcurrentJobDeletionScenario extends PreInsertJobScenario { private final boolean deleteData; public ConcurrentJobDeletionScenario(boolean deleteData) { super(10); this.deleteData = deleteData; } @Override public void execute(final SchedulerDbManagerConcurrencyTest test) throws InterruptedException { for (int i = 0; i < nbJobs; i++) { final int finalI = i; threadPool.submit(new Runnable() { @Override public void run() { try { test.deleteJob(jobIds.get(finalI), deleteData); } catch (Exception e) { e.printStackTrace(); } } }); } } @Override protected int getExpectedNumberOfJobInDb() { return 0; } } private static abstract class PreInsertJobScenario extends Scenario { protected final int nbJobs; protected final List<JobId> jobIds; public PreInsertJobScenario(int nbJobs) { this.nbJobs = nbJobs; this.jobIds = new ArrayList<JobId>(nbJobs); } @Override protected void setUp(final SchedulerDbManagerConcurrencyTest test) throws Exception { super.setUp(test); for (int i = 0; i < nbJobs; i++) { jobIds.add(test.createAndInsertJob().getId()); } } } private static class ConcurrentJobInsertionAndDeletionScenario extends PreInsertJobScenario { private AtomicInteger expectedNumberOfJobs; public ConcurrentJobInsertionAndDeletionScenario() { super(30); expectedNumberOfJobs = new AtomicInteger(nbJobs); } @Override public void execute(final SchedulerDbManagerConcurrencyTest test) throws InterruptedException { for (int i = 0; i < nbJobs; i++) { final int iCopy = i; threadPool.submit(new Callable<Void>() { @Override public Void call() throws Exception { int value = iCopy % 3; switch (value) { case 0: test.createAndInsertJob(); expectedNumberOfJobs.getAndIncrement(); break; case 1: test.deleteJob(jobIds.get(iCopy), false); expectedNumberOfJobs.getAndDecrement(); break; case 2: test.deleteJob(jobIds.get(iCopy), true); expectedNumberOfJobs.getAndDecrement(); break; } return null; } }); } awaitTermination(); } @Override protected int getExpectedNumberOfJobInDb() { return expectedNumberOfJobs.get(); } } private static class ConcurrentJobInsertionScenario extends Scenario { @Override public void execute(final SchedulerDbManagerConcurrencyTest test) throws InterruptedException { for (int i = 0; i < 10; i++) { threadPool.submit(new Runnable() { @Override public void run() { try { test.createAndInsertJob(); } catch (Exception e) { e.printStackTrace(); } } }); } } @Override protected int getExpectedNumberOfJobInDb() { return 10; } } private static abstract class Scenario { protected ExecutorService threadPool = Executors.newFixedThreadPool(10); public void doTest(SchedulerDbManagerConcurrencyTest manager) throws Exception { setUp(manager); execute(manager); awaitTermination(); assertTermination(manager); tearDown(manager); } protected void setUp(SchedulerDbManagerConcurrencyTest test) throws Exception { } protected abstract void execute(SchedulerDbManagerConcurrencyTest test) throws InterruptedException; protected void awaitTermination() throws InterruptedException { threadPool.shutdown(); threadPool.awaitTermination(1, TimeUnit.MINUTES); } protected void assertTermination(SchedulerDbManagerConcurrencyTest test) { Assert.assertEquals(getExpectedNumberOfJobInDb(), test.getDbManager().getTotalJobsCount()); } protected void tearDown(SchedulerDbManagerConcurrencyTest test) throws InterruptedException { } protected abstract int getExpectedNumberOfJobInDb(); } public InternalJob createAndInsertJob() throws Exception { TaskFlowJob jobDef = new TaskFlowJob(); JavaTask javaTask = createDefaultTask("java task"); javaTask.setExecutableClassName(TestDummyExecutable.class.getName()); jobDef.addTask(javaTask); return defaultSubmitJob(jobDef); } public void deleteJob(JobId jobId, boolean deleteData) { dbManager.removeJob(jobId, System.currentTimeMillis(), deleteData); } }