Repository: commons-dbcp Updated Branches: refs/heads/master 51124f92b -> afc2d3ddd
DBCP-513 create a unit test to reproduce the issue This is a timing glitch. So it's sometimes not easy to reproduce it. On my box (12 core Ryzen 2600X) it happens every second test run. It might have another behaviour depending on cpu cores. The higher the core count, the more likely it hangs obviously. Project: http://git-wip-us.apache.org/repos/asf/commons-dbcp/repo Commit: http://git-wip-us.apache.org/repos/asf/commons-dbcp/commit/f36ff599 Tree: http://git-wip-us.apache.org/repos/asf/commons-dbcp/tree/f36ff599 Diff: http://git-wip-us.apache.org/repos/asf/commons-dbcp/diff/f36ff599 Branch: refs/heads/master Commit: f36ff599a7670c025010ed7d86ea0fbda7e26bee Parents: 51124f9 Author: Mark Struberg <strub...@apache.org> Authored: Tue Oct 23 20:21:07 2018 +0200 Committer: Mark Struberg <strub...@apache.org> Committed: Tue Oct 23 20:21:07 2018 +0200 ---------------------------------------------------------------------- .../dbcp2/TestParallelCreationWithNoIdle.java | 146 +++++++++++++++++++ 1 file changed, 146 insertions(+) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/commons-dbcp/blob/f36ff599/src/test/java/org/apache/commons/dbcp2/TestParallelCreationWithNoIdle.java ---------------------------------------------------------------------- diff --git a/src/test/java/org/apache/commons/dbcp2/TestParallelCreationWithNoIdle.java b/src/test/java/org/apache/commons/dbcp2/TestParallelCreationWithNoIdle.java new file mode 100644 index 0000000..6d7367a --- /dev/null +++ b/src/test/java/org/apache/commons/dbcp2/TestParallelCreationWithNoIdle.java @@ -0,0 +1,146 @@ +package org.apache.commons.dbcp2; + +import org.apache.commons.logging.LogFactory; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import static org.junit.Assert.assertEquals; + +/** + * Test if the pooling if no idle objects are used + */ +public class TestParallelCreationWithNoIdle { + + + protected BasicDataSource ds = null; + private static final String CATALOG = "test catalog"; + + @BeforeClass + public static void setUpClass() { + // register a custom logger which supports inspection of the log messages + LogFactory.getFactory().setAttribute("org.apache.commons.logging.Log", "org.apache.commons.dbcp2.StackMessageLog"); + } + + @Before + public void setUp() throws Exception { + ds = new BasicDataSource(); + ds.setDriverClassName("org.apache.commons.dbcp2.TesterConnectionDelayDriver"); + ds.setUrl("jdbc:apache:commons:testerConnectionDelayDriver:50"); + ds.setMaxTotal(10); + + // this one is actually very important. + // see DBCP-513 + ds.setMaxIdle(0); + + // wait a minute. Usually the test runs in ~ 1 second + // but often it's getting stuck ^^ + // you have one second to get a thread dump ;) + ds.setMaxWaitMillis(60000); + + ds.setDefaultAutoCommit(Boolean.TRUE); + ds.setDefaultReadOnly(Boolean.FALSE); + ds.setDefaultTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED); + ds.setDefaultCatalog(CATALOG); + ds.setUsername("userName"); + ds.setPassword("password"); + ds.setValidationQuery("SELECT DUMMY FROM DUAL"); + ds.setConnectionInitSqls(Arrays.asList(new String[] { "SELECT 1", "SELECT 2"})); + ds.setDriverClassLoader(new TesterClassLoader()); + ds.setJmxName("org.apache.commons.dbcp2:name=test"); + } + + /** + * Fire up 100 Threads but only have 10 maxActive and forcedBlock. + * See + * @throws Exception + */ + @Test + public void testMassiveConcurrentInitBorrow() throws Exception { + final int numThreads = 200; + ds.setDriverClassName("org.apache.commons.dbcp2.TesterConnectionDelayDriver"); + ds.setUrl("jdbc:apache:commons:testerConnectionDelayDriver:20"); + ds.setInitialSize(8); + final List<Throwable> errors = Collections.synchronizedList(new ArrayList<>()); + + Thread[] threads = new Thread[numThreads]; + for (int i = 0; i < numThreads; i++) { + threads[i] = new TestThread(2, 0, 50); + threads[i].setUncaughtExceptionHandler((t, e) -> errors.add(e)); + } + + for (int i = 0; i < numThreads; i++) { + threads[i].start(); + + if (i%4 == 0) { + Thread.sleep(20); + } + } + + for (int i = 0; i < numThreads; i++) { + threads[i].join(); + } + + assertEquals(0, errors.size()); + ds.close(); + } + + + + class TestThread extends Thread { + java.util.Random _random = new java.util.Random(); + final int iter; + final int delay; + final int delayAfter; + + + public TestThread(final int iter, final int delay, final int delayAfter) { + this.iter = iter; + this.delay = delay; + this.delayAfter = delayAfter; + } + + + @Override + public void run() { + System.out.println("Thread started " + Thread.currentThread().toString()); + for(int i = 0; i< iter; i++) { + sleepMax(delay); + try (Connection conn = ds.getConnection(); + PreparedStatement stmt = conn.prepareStatement( + "select 'literal', SYSDATE from dual"); + ) { + System.out.println("Got Connection " + Thread.currentThread().toString()); + ResultSet rset = stmt.executeQuery(); + rset.next(); + sleepMax(delayAfter); + rset.close(); + } catch(final Exception e) { + e.printStackTrace(); + throw new RuntimeException(e); + } + } + System.out.println("Thread done " + Thread.currentThread().toString()); + } + + private void sleepMax(int timeMax) { + if (timeMax == 0) { + return; + } + try { + Thread.sleep(_random.nextInt(timeMax)); + } catch(final Exception e) { + // ignored + } + } + } + +}