/** 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.Properties; import com.bigdata.testutil.ExperimentDriver.Result; /** * Stress tests where a {@link TxDag} is used to detect deadlock. * * @author <a href="mailto:thompsonbry@users.sourceforge.net">Bryan Thompson</a> * @version $Id$ */ public class StressTestNonBlockingLockManagerWithTxDag extends AbstractStressTestNonBlockingLockManager { /** * */ public StressTestNonBlockingLockManagerWithTxDag() { super(); } public StressTestNonBlockingLockManagerWithTxDag(String name) { super(name); } /** * Test where each operation locks only a single resource (low concurrency * condition w/ 5 threads). There is no timeout. All tasks should run to * completion. Since modestly large number of resources (100) is used and * since each task declares a single resource lock, the maximum observed * concurrency should be the #of tasks in the thread pool. */ public void test_singleResourceLocking_waitsFor_lowConcurrency5() throws Exception { final Properties properties = new Properties(); final int nthreads = 5; final int ntasks = 1000; properties.setProperty(TestOptions.CORE_POOL_SIZE,""+nthreads); properties.setProperty(TestOptions.NTASKS,""+ntasks); 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"); final Result result = doComparisonTest(properties); // Deadlocks should not be possible with only one resource. assertEquals("ndeadlock", 0, Integer.parseInt(result.get("ndeadlock"))); // all tasks completed successfully. assertEquals("nsuccess", ntasks, Integer.parseInt(result .get("nsuccess"))); // tasks were run in parallel. assertEquals("maxrunning", nthreads, Integer.parseInt(result .get("maxrunning"))); } /** * Test where each operation locks only a single resource using a thread * pool with 20 core threads. Since there is no timeout, all tasks should * run to completion. The maximum observed concurrency SHOULD be equal to * the size of the thread pool. */ public void test_singleResourceLocking_waitsFor_defaultConcurrency20() throws Exception { final Properties properties = new Properties(); final int nthreads = 20; final int ntasks = 1000; properties.setProperty(TestOptions.CORE_POOL_SIZE,""+nthreads); properties.setProperty(TestOptions.NTASKS,""+ntasks); 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"); final Result result = doComparisonTest(properties); // Deadlocks should not be possible with only one resource. assertEquals("ndeadlock", 0, Integer.parseInt(result.get("ndeadlock"))); // all tasks complete successfully. assertEquals("nsuccess", ntasks, Integer.parseInt(result .get("nsuccess"))); // tasks were run in parallel. assertEquals("maxrunning", nthreads, Integer.parseInt(result .get("maxrunning"))); } /** * Test where each operation locks only a single resource (high concurrency * condition with 100 threads). */ public void test_singleResourceLocking_waitsFor_highConcurrency100() throws Exception { final Properties properties = new Properties(); final int nthreads = 100; final int ntasks = 1000; properties.setProperty(TestOptions.CORE_POOL_SIZE,""+nthreads); properties.setProperty(TestOptions.NTASKS,""+ntasks); 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"); final Result result = doComparisonTest(properties); // Deadlocks should not be possible with only one resource. assertEquals("ndeadlock", 0, Integer.parseInt(result.get("ndeadlock"))); // all tasks complete successfully. assertEquals(ntasks, Integer.parseInt(result.get("nsuccess"))); /* * Tasks were run in parallel with at least 50% of the threads executing * concurrently. */ final int maxrunning = Integer.parseInt(result.get("maxrunning")); assertTrue("nthreads=" + nthreads + ", but maxrunning is only" + maxrunning, maxrunning >= nthreads / 2); } /** * 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_waitsFor_lowConcurrency2() throws Exception { final Properties properties = new Properties(); final int nthreads = 2; final int ntasks = 1000; properties.setProperty(TestOptions.CORE_POOL_SIZE,""+nthreads); properties.setProperty(TestOptions.NTASKS,""+ntasks); 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"); final Result result = doComparisonTest(properties); // Deadlocks should not be possible with only one resource. assertEquals("ndeadlock", 0, Integer.parseInt(result.get("ndeadlock"))); // all tasks completed successfully. assertEquals("nsuccess", ntasks, Integer.parseInt(result .get("nsuccess"))); // tasks were serialized. assertEquals("maxrunning", 1, Integer .parseInt(result.get("maxrunning"))); } /** * 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_waitsFor_lowConcurrency5_withTaskDeath() throws Exception { final Properties properties = new Properties(); // Note: Small ntasks since this case otherwise is slow. final int nthreads = 3; final int ntasks = 100; final double percentTaskDeath = .1d; final double expectedErrorRate = percentTaskDeath; properties.setProperty(TestOptions.CORE_POOL_SIZE, ""+nthreads); properties.setProperty(TestOptions.NTASKS, ""+ntasks); 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"); properties.setProperty(TestOptions.PERCENT_TASK_DEATH, ""+percentTaskDeath); final Result result = doComparisonTest(properties); // tasks were serialized. assertEquals("maxrunning", 1, Integer .parseInt(result.get("maxrunning"))); // Deadlocks should not be possible with only one resource. assertEquals("ndeadlock", 0, Integer.parseInt(result.get("ndeadlock"))); final int nsuccess = Integer.parseInt(result.get("nsuccess")); final int nhorriddeath = Integer.parseInt(result.get("nhorriddeath")); // all tasks were either successful or a died a horrid death. assertEquals(ntasks, nsuccess + nhorriddeath); /* * Verify that the observed error rate corresponds closely to the * specified error rate. It need not be exact since the actual #of tasks * scheduled to die is random. */ final double actualErrorRate = nhorriddeath / (double) ntasks; /* * Note: I've increased the upper bound on the allowed error rate a bit * since the CI builds were occasionally failing this with an actual * error rate which was quite reasonable, e.g., .16. [And since I have * observed a lower bound on the error rate as low as .01 so I have also * adjusted the lower bound which is now effectively zero (0).] */ if ((actualErrorRate < expectedErrorRate - .1/*.05*/) || (actualErrorRate > expectedErrorRate + .1)) { fail("error rate: expected=" + expectedErrorRate + ", actual=" + actualErrorRate); } } /** * 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_waitsFor_highConcurrency() throws Exception { final Properties properties = new Properties(); // Note: Small ntasks since otherwise this case takes very long. final int nthreads = 100; final int ntasks = 100; properties.setProperty(TestOptions.CORE_POOL_SIZE,""+nthreads); properties.setProperty(TestOptions.NTASKS,""+ntasks); 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"); final Result result = doComparisonTest(properties); // Deadlocks should not be possible with only one resource. assertEquals("ndeadlock", 0, Integer.parseInt(result.get("ndeadlock"))); // Should have been serialized. assertEquals("maxrunning",1, Integer.parseInt(result.get("maxrunning"))); // All tasks complete successfully. assertEquals("nsuccess", ntasks, Integer.parseInt(result .get("nsuccess"))); } /** * 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. The * task timeout is non-zero, so long-running tasks will be cancelled. This * test stresses the logic in lock() that is responsible for backing out a * lock requests when a task is cancelled either while awaiting its locks or * while running. */ public void test_singleResourceLocking_serialized_waitsFor_highConcurrency_taskTimeout() throws Exception { final Properties properties = new Properties(); final int nthreads = 100; final int ntasks = 1000; properties.setProperty(TestOptions.CORE_POOL_SIZE, "" + nthreads); properties.setProperty(TestOptions.NTASKS, "" + ntasks); properties.setProperty(TestOptions.NRESOURCES, "1"); properties.setProperty(TestOptions.MIN_LOCKS, "1"); properties.setProperty(TestOptions.MAX_LOCKS, "1"); properties.setProperty(TestOptions.TASK_TIMEOUT, "3000"); properties.setProperty(TestOptions.PREDECLARE_LOCKS, "false"); properties.setProperty(TestOptions.SORT_LOCK_REQUESTS, "false"); final Result result = doComparisonTest(properties); // Deadlocks should not be possible with only one resource. assertEquals("ndeadlock", 0, Integer.parseInt(result.get("ndeadlock"))); // Should have been serialized. assertEquals("maxrunning", 1, Integer .parseInt(result.get("maxrunning"))); // Note: Timeouts should be expected. They will show up as cancelled // tasks. final int ncancel = Integer.parseInt(result.get("ncancel")); assertTrue("No cancelled tasks?", ncancel > 0); } /** * Test where each operation locks one or more resources. * <p> * Note: This condition provides the basis for deadlocks. * * FIXME We don't have real-deadlocks w/o 2PL since we are processing the * lock requests atomically (all requests for a given task are posted at * once). So we don't really need {@link TxDag} for that. Modify to support * 2PL and to use {@link TxDag} when 2PL is possible and locks are not * predeclared (you can not use 2PL if you predeclare locks). */ public void test_multipleResourceLocking_resources3_waitsFor_deadlocks_locktries3() throws Exception { final Properties properties = new Properties(); final int nthreads = 20; final int ntasks = 1000; properties.setProperty(TestOptions.CORE_POOL_SIZE, ""+nthreads); properties.setProperty(TestOptions.NTASKS, ""+ntasks); properties.setProperty(TestOptions.NRESOURCES, "100"); properties.setProperty(TestOptions.MIN_LOCKS, "3"); properties.setProperty(TestOptions.MAX_LOCKS, "3"); properties.setProperty(TestOptions.MAX_LOCK_TRIES, "1"); properties.setProperty(TestOptions.PREDECLARE_LOCKS, "false"); properties.setProperty(TestOptions.SORT_LOCK_REQUESTS, "false"); final Result result = doComparisonTest(properties); // All tasks complete successfully. assertEquals("nsuccess", ntasks, Integer.parseInt(result .get("nsuccess"))); } /** * 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 resources the * chances of a deadlock on any given operation are extremely high. * * FIXME We don't have real-deadlocks w/o 2PL since we are processing the * lock requests atomically (all requests for a given task are posted at * once). So we don't really need {@link TxDag} for that. Modify to support * 2PL and to use {@link TxDag} when 2PL is possible and locks are not * predeclared (you can not use 2PL if you predeclare locks). */ public void test_multipleResourceLocking_resources10_waitsFor_deadlocks_locktries10() throws Exception { final Properties properties = new Properties(); final int nthreads = 20; final int ntasks = 1000; properties.setProperty(TestOptions.CORE_POOL_SIZE, ""+nthreads); properties.setProperty(TestOptions.NTASKS, ""+ntasks); 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"); doComparisonTest(properties); final Result result = doComparisonTest(properties); // All tasks complete successfully. assertEquals("nsuccess", ntasks, Integer.parseInt(result .get("nsuccess"))); } }