You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cayenne.apache.org by aa...@apache.org on 2016/03/09 20:29:13 UTC

[3/4] cayenne git commit: Cayenne 4.0 connection pool is occasionally running out of connections

Cayenne 4.0 connection pool is occasionally running out of connections

* trying to reproduce


Project: http://git-wip-us.apache.org/repos/asf/cayenne/repo
Commit: http://git-wip-us.apache.org/repos/asf/cayenne/commit/0a9974bd
Tree: http://git-wip-us.apache.org/repos/asf/cayenne/tree/0a9974bd
Diff: http://git-wip-us.apache.org/repos/asf/cayenne/diff/0a9974bd

Branch: refs/heads/master
Commit: 0a9974bd1674857ca492789b46858ec3eb8249f4
Parents: ab6faea
Author: Andrus Adamchik <an...@objectstyle.com>
Authored: Wed Mar 9 07:49:10 2016 -0800
Committer: Andrus Adamchik <an...@objectstyle.com>
Committed: Wed Mar 9 08:52:55 2016 -0800

----------------------------------------------------------------------
 .../datasource/ManagedPoolingDataSource.java    |   8 ++
 .../datasource/ManagedPoolingDataSourceIT.java  | 123 +++++++++++++++++++
 2 files changed, 131 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cayenne/blob/0a9974bd/cayenne-server/src/main/java/org/apache/cayenne/datasource/ManagedPoolingDataSource.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/datasource/ManagedPoolingDataSource.java b/cayenne-server/src/main/java/org/apache/cayenne/datasource/ManagedPoolingDataSource.java
index 3a794de..1075e3d 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/datasource/ManagedPoolingDataSource.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/datasource/ManagedPoolingDataSource.java
@@ -55,6 +55,14 @@ public class ManagedPoolingDataSource implements PoolingDataSource, ScopeEventLi
 		return dataSourceManager;
 	}
 
+	int poolSize() {
+		return dataSourceManager.getDataSource().poolSize();
+	}
+
+	int availableSize() {
+		return dataSourceManager.getDataSource().availableSize();
+	}
+
 	/**
 	 * Calls {@link #shutdown()} to drain the underlying pool, close open
 	 * connections and block the DataSource from creating any new connections.

http://git-wip-us.apache.org/repos/asf/cayenne/blob/0a9974bd/cayenne-server/src/test/java/org/apache/cayenne/datasource/ManagedPoolingDataSourceIT.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/datasource/ManagedPoolingDataSourceIT.java b/cayenne-server/src/test/java/org/apache/cayenne/datasource/ManagedPoolingDataSourceIT.java
new file mode 100644
index 0000000..1580ce5
--- /dev/null
+++ b/cayenne-server/src/test/java/org/apache/cayenne/datasource/ManagedPoolingDataSourceIT.java
@@ -0,0 +1,123 @@
+package org.apache.cayenne.datasource;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.sql.Connection;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import javax.sql.DataSource;
+
+import org.apache.cayenne.unit.di.server.CayenneProjects;
+import org.apache.cayenne.unit.di.server.UseServerRuntime;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+import org.mockito.stubbing.OngoingStubbing;
+
+@UseServerRuntime(CayenneProjects.TESTMAP_PROJECT)
+public class ManagedPoolingDataSourceIT {
+
+	private int poolSize;
+	private Map<ExpiringConnection, Object> connections;
+	private UnmanagedPoolingDataSource unmanagedPool;
+	private ManagedPoolingDataSource managedPool;
+
+	@Before
+	public void before() throws SQLException {
+
+		this.poolSize = 3;
+		this.connections = new ConcurrentHashMap<>();
+
+		DataSource mockDataSource = mock(DataSource.class);
+		when(mockDataSource.getConnection()).thenAnswer(new Answer<Connection>() {
+
+			@Override
+			public Connection answer(InvocationOnMock invocation) throws Throwable {
+				return createMockConnection();
+			}
+		});
+
+		PoolingDataSourceParameters parameters = new PoolingDataSourceParameters();
+		parameters.setMaxConnections(poolSize);
+		parameters.setMinConnections(poolSize - 1);
+		parameters.setMaxQueueWaitTime(500);
+		parameters.setValidationQuery("SELECT 1");
+		this.unmanagedPool = new UnmanagedPoolingDataSource(mockDataSource, parameters);
+
+		this.managedPool = new ManagedPoolingDataSource(unmanagedPool, 10000);
+	}
+
+	private Connection createMockConnection() throws SQLException {
+		ExpiringConnection connectionWrapper = new ExpiringConnection();
+		connections.put(connectionWrapper, 1);
+		return connectionWrapper.mockConnection;
+	}
+
+	@After
+	public void after() {
+		if (managedPool != null) {
+			managedPool.close();
+		}
+	}
+
+	private void expireConnections() throws SQLException {
+		Iterator<ExpiringConnection> it = connections.keySet().iterator();
+		while (it.hasNext()) {
+			ExpiringConnection c = it.next();
+			it.remove();
+			c.expire();
+		}
+	}
+
+	@Test
+	public void testAbruptReset() throws SQLException {
+
+		assertTrue(managedPool.poolSize() > 0);
+		assertTrue(managedPool.availableSize() > 0);
+
+		// make sure conn
+		expireConnections();
+
+		// CAY-2067 ... this should work on an invalid pool
+		assertNotNull(managedPool.getConnection());
+	}
+
+	static class ExpiringConnection {
+
+		private Connection mockConnection;
+		private OngoingStubbing<Statement> createStatementMock;
+
+		ExpiringConnection() throws SQLException {
+			this.mockConnection = mock(Connection.class);
+			this.createStatementMock = when(mockConnection.createStatement());
+
+			createStatementMock.thenAnswer(new Answer<Statement>() {
+				@Override
+				public Statement answer(InvocationOnMock invocation) throws Throwable {
+
+					ResultSet mockRs = mock(ResultSet.class);
+					when(mockRs.next()).thenReturn(true, false, false, false);
+
+					Statement mockStatement = mock(Statement.class);
+					when(mockStatement.executeQuery(anyString())).thenReturn(mockRs);
+					return mockStatement;
+				}
+			});
+		}
+
+		void expire() throws SQLException {
+			createStatementMock.thenThrow(new SQLException("Expired"));
+		}
+	}
+}