final jdbc connection never closed --> number of connections grows
------------------------------------------------------------------
Key: DBCP-269
URL: https://issues.apache.org/jira/browse/DBCP-269
Project: Commons Dbcp
Issue Type: Bug
Affects Versions: 1.3, 1.4, Nightly Builds
Environment: Windows, JDK 5
Reporter: Jean-Louis MONTEIRO
Using DBCP parameters:
JdbcDriver com.mysql.jdbc.Driver
JdbcUrl jdbc:mysql://localhost:3307/dbcp?cacheResultsetMetadata=true
UserName dbcp
Password dbcp
InitialSize 5
MaxActive 8
MaxWait -1
TestOnBorrow false
TestOnReturn false
PoolPreparedStatements true
MaxOpenPreparedStatements 10
# TimeBetweenEvictionRunsMillis 20000
# MinEvictableIdleTimeMillis 30000
# NumTestsPerEvictionRun 3
# TestWhileIdle false
MinIdle 2
MaxIdle 5
DefaultTransactionIsolation 1
provides strange behaviour.
The testcase is simple:
@Test
public void testMaxIdle() throws Exception {
int MAX_ACTIVE = 8;
List<Connection> connectionList = new
ArrayList<Connection>();
int i = 0;
DataSource dataSource = ...; // retrieve the
datasource
Assert.assertNotNull(dataSource);
System.out.println("*** Getting all connections
...");
for (i = 0; i < MAX_ACTIVE; i++) {
System.out.println("get
connection " + (i + 1));
Connection con =
dataSource.getConnection();
Assert.assertNotNull(con);
connectionList.add(con);
}
i = 0;
System.out.println("*** Releasing all
connections ...");
for (Iterator<Connection> iterator =
connectionList.iterator(); iterator.hasNext();) {
Connection connection =
(Connection) iterator.next();
System.out.println("close
connection " + (++i));
connection.close();
iterator.remove();
}
i = 0;
System.out.println("*** Getting all + X
connections ...");
for (i = 0; i < (MAX_ACTIVE + 1); i++) {
System.out.println("get
connection " + (i + 1));
Connection con =
dataSource.getConnection();
Assert.assertNotNull(con);
connectionList.add(con);
}
}
First of all, after initialization, the number of real database connections
established is 6 instead of 5. This due to the "protected synchronized
DataSource createDataSource" method of the BasicDataSource object because of
the validation of the ConnectionFactory. The "validateConnectionFactory" method
creates a new jdbc connection and destroy it at the end. Nonetheless, the
underlying database connection is not really closed.
At the end of the above test case, we should not have more than 8 connections
but we have in fact 12 of them (8 + 1 for validation + 3 because DBCP wants to
close extra connection MAX_ACTIVE - MAX_IDLE). In fact, those 3 connections are
not really closed.
Please find attached a patch correcting this bug. This is due to the method
isClosed() of the DelegatingConnection. From my point of view, the
implementation of the method should be:
public boolean isClosed() throws SQLException {
if(_closed && _conn.isClosed()) { // Instead of if(_closed ||
_conn.isClosed())
return true;
}
return false;
}
>From my understanding, a connection is considered as closed if and only if one
>of the connection chain is closed (OR in the test). But, if I'm write the
>_closed flag of the delegating connection is set to true during the
>passivation. So, after returning to the pool, a connection can not be closed.
>The isClosed method is really important because it's used indirectly by the
>reallyClose method of a PoolableConnection.
The behaviour is much more impressive if the evict thread of the configuration
is activated because, with a testcase that simply wait after getting the
datasource initialized (Thread.currentThread().sleep(600000);) you will see
database connections growing.
Finally, after applying the given patch, I faced some problems regarding DBCP
TestCases:
In some test cases, you have a test method
public void testClose() throws Exception {
ds.setAccessToUnderlyingConnectionAllowed(true);
[...]
// raw idle connection should now be closed
// FIXME should be assertTrue
assertFalse(rawIdleConnection.isClosed());
[...]
// both wrapper and raw active connection should be closed
assertTrue(activeConnection.isClosed());
assertTrue(rawActiveConnection.isClosed());
}
>From my point of view and regarding the comment the
>assertFalse(rawIdleConnection.isClosed()); sould become
>assertTrue(rawIdleConnection.isClosed());.
Moreover, the next test method fails.
// Bugzilla Bug 28251: Returning dead database connections to
BasicDataSource
// isClosed() failure blocks returning a connection to the pool
public void testIsClosedFailure() throws SQLException {
ds.setAccessToUnderlyingConnectionAllowed(true);
Connection conn = ds.getConnection();
assertNotNull(conn);
assertEquals(1, ds.getNumActive());
// set an IO failure causing the isClosed mathod to fail
TesterConnection tconn = (TesterConnection)
((DelegatingConnection)conn).getInnermostDelegate();
tconn.setFailure(new IOException("network error"));
try {
conn.close();
fail("Expected SQLException");
}
catch(SQLException ex) { }
assertEquals(0, ds.getNumActive());
}
But again, looking at the AddObjectToPool of the GenericObjectPool
(commons-pool), the IOException("Network error") is catch during the
passivation. It implies that the underlying connection (in error) is not
returned to the pool. It will be re-created by the evict thread if minIdle
property is set.
Am I wrong ?
Sorry for the little long post.
Kind regards,
Jean-Louis MONTEIRO
--
This message is automatically generated by JIRA.
-
You can reply to this email to add a comment to the issue online.