/**
Copyright (C) SYSTAP, LLC DBA Blazegraph 2006-2016. All rights reserved.
Contact:
SYSTAP, LLC DBA Blazegraph
2501 Calvert ST NW #106
Washington, DC 20008
licenses@blazegraph.com
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/*
* Created on Oct 1, 2007
*/
package com.bigdata.concurrent;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import junit.framework.TestCase;
import org.apache.log4j.Logger;
import com.bigdata.service.DataService;
import com.bigdata.testutil.ExperimentDriver;
import com.bigdata.testutil.ExperimentDriver.IComparisonTest;
import com.bigdata.testutil.ExperimentDriver.Result;
import com.bigdata.util.DaemonThreadFactory;
import com.bigdata.util.NV;
/**
* Suite of stress tests of the concurrency control mechanisms (without the
* database implementation) - See {@link LockManager}.
* <p>
* Goals:
* <p>
* 1. higher concurrency of unisolated operations on the ds/journal with group
* commit. this only requires a "lock" per writable named index, e.g., an
* operation will lock exactly one resource. show consistency of the data in a
* suite of stress tests with varying #s of threads, tasks, and resources. Each
* task will lock exactly one resource - the unisolated named index on which it
* would write. DO THIS W/O the TxDAG first and get group commit debugged before
* trying to work through the tx commit stuff, which requires the WAITS_FOR
* graph support.
* <p>
* 2. transaction processing integrated with unisolated operations. since a
* transaction MAY write on more than one index this requires a TxDAG and a
* queue of resources waiting to get into the granted group (which will always
* be a singleton since writes on unisolated named indices require exclusive
* locks). show that concurrency control never deadlocks in a suite of stress
* tests with varying #s of threads, tasks, resources, and resource locked per
* task. Again, a resource is a unisolated named index.
*
* @todo test to verify that we can interrupt running tasks.
*
* @todo verify more necessary outcomes of the different tests using assertions.
*
* @author <a href="mailto:thompsonbry@users.sourceforge.net">Bryan Thompson</a>
*/
public class TestLockManager extends TestCase implements IComparisonTest {
private static final Logger log = Logger.getLogger(TestLockManager.class);
/**
*
*/
public TestLockManager() {
super();
}
public TestLockManager(String name) {
super(name);
}
/**
* Waits 10ms once it acquires its locks.
*
* @author <a href="mailto:thompsonbry@users.sourceforge.net">Bryan Thompson</a>
* @version $Id$
*/
public static class Wait10ResourceTask implements Callable<Object> {
@Override
public Object call() throws Exception {
synchronized (this) {
try {
wait(10/* milliseconds */);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
return null;
}
}
/**
* Dies once it acquires its locks by throwing {@link HorridTaskDeath}.
*
* @author <a href="mailto:thompsonbry@users.sourceforge.net">Bryan Thompson</a>
* @version $Id$
*/
static class DeathResourceTask implements Callable<Object> {
@Override
public Object call() throws Exception {
throw new HorridTaskDeath();
}
}
/**
* Test driver.
* <p>
* Note: A "resource" is a named index (partition), so set nresources based
* on your expectations for the #of index partitions on a journal or
* federation.
* <p>
* Note: The likelihood of deadlock increases as (a) more locks are
* requested per task; and (b) fewer resources are available to be locked.
* When minLocks==maxLocks==nresources then tasks will be serialized since
* each task requires all resources in order to proceed.
* <p>
* Note: At minLocks==maxLocks==1 this test can be used to explore the
* behavior of tasks that lock only a single resource, eg., unisolated
* operations on the {@link DataService}.
*/
@Override
public Result doComparisonTest(final Properties properties) throws Exception {
final long testTimeout = Integer.parseInt(properties.getProperty(
TestOptions.TIMEOUT, TestOptions.DEFAULT_TIMEOUT));
final int nthreads = Integer.parseInt(properties
.getProperty(TestOptions.NTHREADS));
final int ntasks = Integer.parseInt(properties
.getProperty(TestOptions.NTASKS));
final double percentTaskDeath = Double.parseDouble(properties
.getProperty(TestOptions.PERCENT_TASK_DEATH,
TestOptions.DEFAULT_PERCENT_TASK_DEATHS));
final int nresources = Integer.parseInt(properties
.getProperty(TestOptions.NRESOURCES));
final int minLocks = Integer.parseInt(properties
.getProperty(TestOptions.MIN_LOCKS));
final int maxLocks = Integer.parseInt(properties
.getProperty(TestOptions.MAX_LOCKS));
final long lockTimeout = Integer.parseInt(properties.getProperty(
TestOptions.LOCK_TIMEOUT, TestOptions.DEFAULT_LOCK_TIMEOUT));
final int maxLockTries = Integer
.parseInt(properties.getProperty(TestOptions.MAX_LOCK_TRIES,
TestOptions.DEFAULT_MAX_LOCK_TRIES));
final boolean predeclareLocks = Boolean.parseBoolean(properties
.getProperty(TestOptions.PREDECLARE_LOCKS,
TestOptions.DEFAULT_PREDECLARE_LOCKS));
final boolean sortLockRequests = Boolean.parseBoolean(properties
.getProperty(TestOptions.SORT_LOCK_REQUESTS,
TestOptions.DEFAULT_SORT_LOCK_REQUESTS));
/*
* Note: without pre-declaration of locks, you can expect high deadlock
* rates when minLocks=maxLocks=nresources since all tasks will contend
* for all resources and locks will be assigned incrementally.
*/
assert minLocks <= maxLocks;
assert minLocks <= nresources;
assert maxLocks <= nresources;
assert maxLockTries >= 1;
final ExecutorService execService = Executors.newFixedThreadPool(nthreads,
DaemonThreadFactory.defaultThreadFactory());
final Collection<Callable<Object>> tasks = new ArrayList<Callable<Object>>(
ntasks);
LockManager<String> db = new LockManager<String>(
nthreads/* multi-programming level */, predeclareLocks,
sortLockRequests);
// distinct resource names. references are reused by reach task.
final String[] resources = new String[nresources];
for (int i = 0; i < nresources; i++) {
resources[i] = "resource" + i;
// assertTrue(db.addResource(resources[i]));
}
final Random r = new Random();
// create tasks; each will use between minLocks and maxLocks distinct
// resources.
for (int i = 0; i < ntasks; i++) {
// #of locks that this task will seek to acquire.
final int nlocks = (minLocks == maxLocks ? minLocks : r
.nextInt(maxLocks - minLocks)
+ minLocks);
// final int nlocks = maxLocks;
final String[] resource = new String[nlocks];
// find a [nlocks] unique resources to lock for this task.
for (int j = 0; j < nlocks; j++) {
int t;
while (true) {
// random resource index.
t = r.nextInt(Integer.MAX_VALUE) % nresources;
// ensure distinct resources for this task.
boolean duplicate = false;
for (int k = 0; k < j; k++) {
if (resource[k] == resources[t]) {
duplicate = true;
break;
}
}
if (!duplicate)
break;
}
resource[j] = resources[t];
}
final LockManagerTask<String,Object> task;
if(r.nextDouble()<percentTaskDeath) {
task = new LockManagerTask<String,Object>(db,resource,new DeathResourceTask());
} else {
task = new LockManagerTask<String,Object>(db,resource,new Wait10ResourceTask());
}
task.setMaxLockTries(maxLockTries);
task.setLockTimeout(lockTimeout);
tasks.add( task );
}
// run everyone.
log.info("invoking all tasks");
final long begin = System.currentTimeMillis();
final List<Future<Object>> futures;
if(testTimeout==0L) {
futures = execService.invokeAll(tasks);
} else {
futures = execService.invokeAll(tasks, testTimeout, TimeUnit.SECONDS);
}
// // normal shutdown (run to completion).
// log.info("Shutting down service");
// execService.shutdown();
final long elapsed = System.currentTimeMillis() - begin;
// terminate running and pending tasks.
log.info("Shutting down service");
execService.shutdownNow();
// await termination (should be rapid unless there is a deadlock).
log.info("Awaiting termination");
execService.awaitTermination(1, TimeUnit.SECONDS);
// check the futures.
int nerrors = 0;
int ndeadlock = 0;
int ntimeout = 0;
int ncomplete = 0;
int ncancel = 0;
int nhorriddeath = 0;
Iterator<Future<Object>> itr = futures.iterator();
while (itr.hasNext()) {
Future<Object> future = itr.next();
if (future.isCancelled()) {
ncancel++;
} else {
ncomplete++;
try {
future.get();
} catch (ExecutionException ex) {
if (ex.getCause() instanceof DeadlockException) {
ndeadlock++;
} else if (ex.getCause() instanceof TimeoutException) {
ntimeout++;
} else if( ex.getCause() instanceof HorridTaskDeath ) {
nhorriddeath++;
} else {
nerrors++;
log.error("Task threw: " + ex, ex);
}
}
}
}
// Done.
System.out.println(db.toString());
final Result result = new Result();
double perSec = (ncomplete *1000d) / elapsed;
result.put("maxrunning", ""+db.maxrunning);
result.put("nrunning", ""+db.nrunning);
result.put("nstarted", ""+db.nstarted);
result.put("nended", ""+db.nended);
result.put("nerror", ""+db.nerror);
result.put("ndeadlock", ""+db.ndeadlock);
result.put("ntimeout", ""+db.ntimeout);
result.put("ncomplete", ""+ncomplete);
result.put("ncancel", ""+ncancel);
result.put("nhorriddeath", ""+nhorriddeath); // Note: This is an expected task death.
result.put("perSec", ""+perSec);
result.put("elapsed", ""+elapsed);
System.err.println(result.toString(true/*newline*/));
if (nerrors > 0) {
fail("There were " + nerrors + " errors and " + ndeadlock
+ " deadlocks::" + result.toString());
}
return result;
}
/**
* Options for {@link TestLockManager#doComparisonTest(Properties)}.
*
* @author <a href="mailto:thompsonbry@users.sourceforge.net">Bryan Thompson</a>
* @version $Id$
*/
public static class TestOptions {
/**
* Maximum amount of time that the test will run (seconds).
*/
public static final String TIMEOUT = "testTimeout";
/**
* The #of concurrent threads (multi-programming level).
*/
public static final String NTHREADS = "nthreads";
/**
* Total #of tasks to execute.
*/
public static final String NTASKS = "ntasks";
/**
* The percentage of tasks that will die a {@link HorridTaskDeath} in
* [0.0:1.0] (default is 0.0). This is used to stress the error handling
* mechanisms.
*/
public static final String PERCENT_TASK_DEATH = "percentTaskDeath";
/**
* The #of declared resources.
*/
public static final String NRESOURCES = "nresources";
/**
* The minimum #of locks that a task will seek to acquire.
*/
public static final String MIN_LOCKS = "minLocks";
/**
* The maximum #of locks that a task will seek to acquire.
*/
public static final String MAX_LOCKS = "maxLocks";
/**
* The timeout when attempting to acquire a lock (milliseconds) -or-
* <code>0</code> iff no timeout will be used.
*/
public static final String LOCK_TIMEOUT = "lockTimeout";
/**
* The maximum #of times that a task will attempt to acquire its locks
* before failing. Temporary failures may occur due to deadlock or
* timeout during lock acquisition. Such failures may be retried. The
* minimum value is ONE (1) since that means that we make only one
* attempt to obtain the necessary locks for the task.
*/
public static final String MAX_LOCK_TRIES = "maxLockTries";
/**
* When true, operations MUST pre-declare their locks (default true).
* <p>
* Note: The {@link LockManager} uses this information to avoid
* deadlocks by the simple expediency of sorting the resources in each
* lock request into a common order. With this option deadlocks are NOT
* possible but all locks MUST be pre-declared by the operation before
* it begins to execute.
*/
public static final String PREDECLARE_LOCKS = "predeclareLocks";
/**
* When true, the resources in a lock request are sorted before the lock
* requests are issued (default true). This option is ONLY turned off
* for testing purposes. Since predeclaration plus sorting makes
* deadlocks impossible, this option MAY be turned off in order to
* exercise the deadlock detection logic in {@link TxDag} and the
* handling of deadlocks when they are detected.
*/
public static final String SORT_LOCK_REQUESTS = "sortLockRequest";
/**
* The default is no timeout for the test.
*/
public static final String DEFAULT_TIMEOUT = "0";
/**
* The default is 1 try for locks.
*/
public static final String DEFAULT_MAX_LOCK_TRIES = "1";
/**
* The default is no timeout for locks.
*/
public static final String DEFAULT_LOCK_TIMEOUT = "0";
/**
* By default we do not force any tasks to die.
*/
public static final String DEFAULT_PERCENT_TASK_DEATHS = "0.0";
/**
* By default the operations will predeclare their locks.
*/
public static final String DEFAULT_PREDECLARE_LOCKS = "true";
/**
* By default lock requests will be sorted.
*/
public static final String DEFAULT_SORT_LOCK_REQUESTS = "true";
}
public void setUpComparisonTest(Properties properties) throws Exception {
}
public void tearDownComparisonTest() throws Exception {
}
public void test_noResourcesDoesNotWait() throws Exception {
Properties properties = new Properties();
properties.setProperty(TestOptions.NTHREADS,"5");
properties.setProperty(TestOptions.NTASKS,"1000");
properties.setProperty(TestOptions.NRESOURCES,"10");
properties.setProperty(TestOptions.MIN_LOCKS,"0");
properties.setProperty(TestOptions.MAX_LOCKS,"0");
properties.setProperty(TestOptions.PREDECLARE_LOCKS,"true");
properties.setProperty(TestOptions.SORT_LOCK_REQUESTS,"true");
Result result = doComparisonTest(properties);
/*
* Make sure that the tasks were not single threaded. ideally they will
* run with full concurrency (NTHREADS == maxrunning).
*/
assertTrue(Integer.parseInt(result.get("maxrunning"))==5);
}
/**
* Test where each operation locks only a single resource (low concurrency
* condition w/ 5 threads).
*/
public void test_singleResourceLocking_lowConcurrency5() throws Exception {
Properties properties = new Properties();
properties.setProperty(TestOptions.NTHREADS,"5");
properties.setProperty(TestOptions.NTASKS,"1000");
properties.setProperty(TestOptions.NRESOURCES,"100");
properties.setProperty(TestOptions.MIN_LOCKS,"1");
properties.setProperty(TestOptions.MAX_LOCKS,"1");
properties.setProperty(TestOptions.PREDECLARE_LOCKS,"false");
properties.setProperty(TestOptions.SORT_LOCK_REQUESTS,"false");
doComparisonTest(properties);
}
/**
* Test where each operation locks only a single resource (default concurrency).
*/
public void test_singleResourceLocking_defaultConcurrency20() throws Exception {
Properties properties = new Properties();
properties.setProperty(TestOptions.NTHREADS,"20"); // whoops! was 5
properties.setProperty(TestOptions.NTASKS,"1000");
properties.setProperty(TestOptions.NRESOURCES,"100");
properties.setProperty(TestOptions.MIN_LOCKS,"1");
properties.setProperty(TestOptions.MAX_LOCKS,"1");
properties.setProperty(TestOptions.PREDECLARE_LOCKS,"false");
properties.setProperty(TestOptions.SORT_LOCK_REQUESTS,"false");
doComparisonTest(properties);
}
/**
* Test where each operation locks only a single resource (high concurrency).
*/
public void test_singleResourceLocking_highConcurrency100() throws Exception {
Properties properties = new Properties();
properties.setProperty(TestOptions.NTHREADS,"100");
properties.setProperty(TestOptions.NTASKS,"1000");
properties.setProperty(TestOptions.NRESOURCES,"100");
properties.setProperty(TestOptions.MIN_LOCKS,"1");
properties.setProperty(TestOptions.MAX_LOCKS,"1");
properties.setProperty(TestOptions.PREDECLARE_LOCKS,"false");
properties.setProperty(TestOptions.SORT_LOCK_REQUESTS,"false");
doComparisonTest(properties);
}
/**
* Test where each operation locks only a single resource and there is only one
* resource to be locked so that all operations MUST be serialized.
*/
public void test_singleResourceLocking_serialized_lowConcurrency2() throws Exception {
Properties properties = new Properties();
properties.setProperty(TestOptions.NTHREADS,"2");
properties.setProperty(TestOptions.NTASKS,"1000");
properties.setProperty(TestOptions.NRESOURCES,"1");
properties.setProperty(TestOptions.MIN_LOCKS,"1");
properties.setProperty(TestOptions.MAX_LOCKS,"1");
properties.setProperty(TestOptions.PREDECLARE_LOCKS,"false");
properties.setProperty(TestOptions.SORT_LOCK_REQUESTS,"false");
doComparisonTest(properties);
}
/**
* Test where each operation locks only a single resource and there is only
* one resource to be locked so that all operations MUST be serialized.
*/
public void test_singleResourceLocking_serialized_lowConcurrency5() throws Exception {
Properties properties = new Properties();
properties.setProperty(TestOptions.NTHREADS,"3");
// Note: Small ntasks since this case otherwise is slow.
properties.setProperty(TestOptions.NTASKS,"100");
properties.setProperty(TestOptions.NRESOURCES,"1");
properties.setProperty(TestOptions.MIN_LOCKS,"1");
properties.setProperty(TestOptions.MAX_LOCKS,"1");
// properties.setProperty(TestOptions.LOCK_TIMEOUT,"0"); // Note: timeout==0 when debugging.
properties.setProperty(TestOptions.PREDECLARE_LOCKS,"false");
properties.setProperty(TestOptions.SORT_LOCK_REQUESTS,"false");
doComparisonTest(properties);
}
/**
* Test where each operation locks only a single resource and there is only
* one resource to be locked so that all operations MUST be serialized and
* where 10% of all tasks die a horrid death.
*/
public void test_singleResourceLocking_serialized_lowConcurrency5_withTaskDeath() throws Exception {
Properties properties = new Properties();
properties.setProperty(TestOptions.NTHREADS,"3");
// Note: Small ntasks since this case otherwise is slow.
properties.setProperty(TestOptions.NTASKS,"100");
properties.setProperty(TestOptions.NRESOURCES,"1");
properties.setProperty(TestOptions.MIN_LOCKS,"1");
properties.setProperty(TestOptions.MAX_LOCKS,"1");
// properties.setProperty(TestOptions.LOCK_TIMEOUT,"0"); // Note: timeout==0 when debugging.
properties.setProperty(TestOptions.PREDECLARE_LOCKS,"false");
properties.setProperty(TestOptions.SORT_LOCK_REQUESTS,"false");
properties.setProperty(TestOptions.PERCENT_TASK_DEATH,".10");
doComparisonTest(properties);
}
/**
* Test where each operation locks only a single resource and there is only
* one resource to be locked so that all operations MUST be serialized.
*/
public void test_singleResourceLocking_serialized_highConcurrency() throws Exception {
Properties properties = new Properties();
properties.setProperty(TestOptions.NTHREADS,"100");
// Note: Small ntasks since otherwise this case takes very long.
properties.setProperty(TestOptions.NTASKS,"100");
properties.setProperty(TestOptions.NRESOURCES,"1");
properties.setProperty(TestOptions.MIN_LOCKS,"1");
properties.setProperty(TestOptions.MAX_LOCKS,"1");
// properties.setProperty(TestOptions.LOCK_TIMEOUT,"0");
properties.setProperty(TestOptions.PREDECLARE_LOCKS,"false");
properties.setProperty(TestOptions.SORT_LOCK_REQUESTS,"false");
doComparisonTest(properties);
}
/**
* Test where each operation locks only a single resource and there is only
* one resource to be locked so that all operations MUST be serialized with
* a non-zero lock timeout. This test stresses the logic in lock() that is
* responsible for backing out a lock request on timeout.
*/
public void test_singleResourceLocking_serialized_highConcurrency_lockTimeout() throws Exception {
Properties properties = new Properties();
properties.setProperty(TestOptions.NTHREADS,"100");
// Note: Small ntasks since otherwise this case takes very long.
properties.setProperty(TestOptions.NTASKS,"100");
properties.setProperty(TestOptions.NRESOURCES,"1");
properties.setProperty(TestOptions.MIN_LOCKS,"1");
properties.setProperty(TestOptions.MAX_LOCKS,"1");
properties.setProperty(TestOptions.LOCK_TIMEOUT,"1000");
properties.setProperty(TestOptions.PREDECLARE_LOCKS,"false");
properties.setProperty(TestOptions.SORT_LOCK_REQUESTS,"false");
Result result = doComparisonTest(properties);
/*
* Deadlocks should not be possible with only one resource.
*/
assertEquals("ndeadlock","0",result.get("ndeadlock"));
}
/**
* Test where each operation locks one or more resources.
* <p>
* Note: This condition provides the basis for deadlocks.
*
* @throws Exception
*/
public void test_multipleResourceLocking_resources3_locktries_3() throws Exception {
Properties properties = new Properties();
properties.setProperty(TestOptions.NTHREADS,"20");
properties.setProperty(TestOptions.NTASKS,"1000");
properties.setProperty(TestOptions.NRESOURCES,"100");
properties.setProperty(TestOptions.MIN_LOCKS,"3");
properties.setProperty(TestOptions.MAX_LOCKS,"3");
properties.setProperty(TestOptions.MAX_LOCK_TRIES,"3");
properties.setProperty(TestOptions.PREDECLARE_LOCKS,"false");
properties.setProperty(TestOptions.SORT_LOCK_REQUESTS,"false");
doComparisonTest(properties);
}
/**
* Test where each operation locks one or more resources.
* <p>
* Note: This condition provides the basis for deadlocks. In fact, since we
* have 10 resource locks for each operation and only 100 operations the
* chances of a deadlock on any given operation are extremely high. However,
* since we are predeclaring our locks and the lock requests are being
* sorted NO deadlocks should result.
*
* @throws Exception
*
* @see <a href="https://sourceforge.net/apps/trac/bigdata/ticket/237" > CI
* deadlock in
* com.bigdata.concurrent.TestLockManager.test_multipleResourceLocking_resources10_locktries10</a>
*/
public void test_multipleResourceLocking_resources10_locktries10_predeclareLocks() throws Exception {
Properties properties = new Properties();
properties.setProperty(TestOptions.NTHREADS,"20");
properties.setProperty(TestOptions.NTASKS,"1000");
properties.setProperty(TestOptions.NRESOURCES,"100");
properties.setProperty(TestOptions.MIN_LOCKS,"10");
properties.setProperty(TestOptions.MAX_LOCKS,"10");
properties.setProperty(TestOptions.MAX_LOCK_TRIES,"10");
properties.setProperty(TestOptions.PREDECLARE_LOCKS,"true");
properties.setProperty(TestOptions.SORT_LOCK_REQUESTS,"true");
Result result = doComparisonTest(properties);
/*
* Deadlocks should not be possible when we predeclare and sort locks.
*/
assertEquals("ndeadlock","0",result.get("ndeadlock"));
}
/**
* Test where each operation locks one or more resources.
* <p>
* Note: This condition provides the basis for deadlocks. In fact, since we
* have 10 resource locks for each operation and only 100 operations the
* chances of a deadlock on any given operation are extremely high. However,
* since we are predeclaring our locks and the lock requests are being
* sorted NO deadlocks should result.
*
* @throws Exception
*
* @see <a href="https://sourceforge.net/apps/trac/bigdata/ticket/237" > CI
* deadlock in
* com.bigdata.concurrent.TestLockManager.test_multipleResourceLocking_resources10_locktries10</a>
*/
public void test_multipleResourceLocking_resources10_locktries10() throws Exception {
final Properties properties = new Properties();
properties.setProperty(TestOptions.NTHREADS,"20");
properties.setProperty(TestOptions.NTASKS,"1000");
properties.setProperty(TestOptions.NRESOURCES,"100");
properties.setProperty(TestOptions.MIN_LOCKS,"10");
properties.setProperty(TestOptions.MAX_LOCKS,"10");
properties.setProperty(TestOptions.MAX_LOCK_TRIES,"10");
properties.setProperty(TestOptions.PREDECLARE_LOCKS,"false");
properties.setProperty(TestOptions.SORT_LOCK_REQUESTS,"false");
/*
* Note: A timeout was introduced in order to work cause this test to
* fail rather than deadlock. It very occasionally will deadlock in CI.
*/
properties.setProperty(TestOptions.TIMEOUT, Long.toString(3 * 60/* seconds */));
doComparisonTest(properties);
}
/**
* Generates an XML file that can be used to (re-)run the concurrency
* control tests. The outputs are appended to a file so you can see how
* performance and collected counters change from run to run.
*
* @see ExperimentDriver
*
* @author <a href="mailto:thompsonbry@users.sourceforge.net">Bryan Thompson</a>
* @version $Id$
*
* @todo refactor the tests generated below to use apply() and run each
* basic condition with and without lock timeout and with and without
* predeclaration of locks (and without sorting when locks are NOT
* predeclared so that we can exercise the deadlock detection stuff).
* We could also run each condition at (2), (20), and (100) threads.
*/
static public class Generate extends ExperimentDriver {
/**
* Generates an XML file that can be run by {@link ExperimentDriver}.
*
* @param args
*/
public static void main(String[] args) throws Exception {
// this is the test to be run.
String className = TestLockManager.class.getName();
Map<String,String> defaultProperties = new HashMap<String,String>();
/*
* Set defaults for each condition.
*/
defaultProperties.put(TestOptions.TIMEOUT,"30"); // secs.
defaultProperties.put(TestOptions.NTHREADS,"20");
defaultProperties.put(TestOptions.NTASKS,"1000");
defaultProperties.put(TestOptions.NRESOURCES,"100");
defaultProperties.put(TestOptions.MIN_LOCKS,"1");
defaultProperties.put(TestOptions.MAX_LOCKS,"3");
defaultProperties.put(TestOptions.MAX_LOCK_TRIES,"1");
defaultProperties.put(TestOptions.PREDECLARE_LOCKS,"false");
defaultProperties.put(TestOptions.SORT_LOCK_REQUESTS,"false");
defaultProperties.put(TestOptions.LOCK_TIMEOUT,"1000"); // ms
List<Condition>conditions = new ArrayList<Condition>();
// low concurrency.
conditions.add(getCondition(
defaultProperties, new NV[] { new NV(TestOptions.NTHREADS,
"2") }));
// and with a non-zero lock timeout.
conditions.add(getCondition(defaultProperties, new NV[] {
new NV(TestOptions.NTHREADS, "2"),
new NV(TestOptions.LOCK_TIMEOUT, "1000") }));
// default concurrency.
conditions.add(getCondition(
defaultProperties, new NV[] {}));
// and with a non-zero lock timeout.
conditions.add(getCondition(defaultProperties, new NV[] { new NV(
TestOptions.LOCK_TIMEOUT, "1000") }));
// default concurrency with 10% task death.
conditions.add(getCondition(
defaultProperties, new NV[] { new NV(TestOptions.PERCENT_TASK_DEATH,
".10") }));
// and with a non-zero lock timeout.
conditions.add(getCondition(
defaultProperties, new NV[] {
new NV(TestOptions.PERCENT_TASK_DEATH, ".10"),
new NV(TestOptions.LOCK_TIMEOUT, "1000") }));
// high concurrency.
conditions.add(getCondition(
defaultProperties, new NV[] { new NV(TestOptions.NTHREADS,
"100") }));
// and with a non-zero lock timeout
conditions.add(getCondition(defaultProperties, new NV[] {
new NV(TestOptions.NTHREADS, "100"),
new NV(TestOptions.LOCK_TIMEOUT, "1000") }));
// high concurrency with 10% task death.
conditions.add(getCondition(defaultProperties, new NV[] {
new NV(TestOptions.NTHREADS, "100"),
new NV(TestOptions.PERCENT_TASK_DEATH, ".10") }));
// and with a non-zero lock timeout.
conditions.add(getCondition(defaultProperties, new NV[] {
new NV(TestOptions.NTHREADS, "100"),
new NV(TestOptions.PERCENT_TASK_DEATH, ".10"),
new NV(TestOptions.LOCK_TIMEOUT, "1000") }));
// force sequential execution by limiting
// nresources==minlocks==maxlocks.
conditions.add(getCondition(
defaultProperties, new NV[] { new NV(TestOptions.NRESOURCES,
"1"), new NV(TestOptions.MIN_LOCKS,
"1"), new NV(TestOptions.MAX_LOCKS,
"1") }));
// and with a non-zero lock timeout
conditions.add(getCondition(
defaultProperties, new NV[] { new NV(TestOptions.NRESOURCES,
"1"), new NV(TestOptions.MIN_LOCKS,
"1"), new NV(TestOptions.MAX_LOCKS,
"1"), new NV(TestOptions.LOCK_TIMEOUT,"1000") }));
// force sequential execution by limiting nresources==minlocks==maxlocks.
conditions.add(getCondition(
defaultProperties, new NV[] { new NV(TestOptions.NRESOURCES,
"3"), new NV(TestOptions.MIN_LOCKS,
"3"), new NV(TestOptions.MAX_LOCKS,
"3") }));
// and with a non-zero lock timeout
conditions.add(getCondition(
defaultProperties, new NV[] { new NV(TestOptions.NRESOURCES,
"3"), new NV(TestOptions.MIN_LOCKS,
"3"), new NV(TestOptions.MAX_LOCKS,
"3"), new NV(TestOptions.LOCK_TIMEOUT,"1000") }));
Experiment exp = new Experiment(className,defaultProperties,conditions);
// copy the output into a file and then you can run it later.
System.err.println(exp.toXML());
}
}
}