[
https://issues.apache.org/jira/browse/DBCP-484?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=16410711#comment-16410711
]
Zheng Feng commented on DBCP-484:
---------------------------------
Hi,
I think there might be a race condition when closing the ManagedConnection
There are two methods which could call connection.close() and the one is in
[ManagedConnection.close()|https://github.com/apache/commons-dbcp/blob/master/src/main/java/org/apache/commons/dbcp2/managed/ManagedConnection.java#L160]
and the other is
[ManagedConncetion.transactionComplete()|https://github.com/apache/commons-dbcp/blob/master/src/main/java/org/apache/commons/dbcp2/managed/ManagedConnection.java#L188]
which is used in calling back when the transaction is completed.
{code}
public void close() throws SQLException {
if (!isClosedInternal()) {
try {
// Don't actually close the connection if in a transaction. The
// connection will be closed by the transactionComplete method.
if (transactionContext == null) {
super.close();
}
} finally {
setClosedInternal(true);
}
}
}
{code}
The thread of close() could be switched after check (transactionContext ==
null) and before the setCloseInternal(true). So it thinks it is still in a
transaction but dose not set the _closed.
{code}
protected void transactionComplete() {
transactionContext = null;
// If we were using a shared connection, clear the reference now that
// the transaction has completed
if (isSharedConnection) {
setDelegate(null);
isSharedConnection = false;
}
// If this connection was closed during the transaction and there is
// still a delegate present close it
final Connection delegate = getDelegateInternal();
if (isClosedInternal() && delegate != null) {
try {
setDelegate(null);
if (!delegate.isClosed()) {
delegate.close();
}
} catch (final SQLException ignored) {
// Not a whole lot we can do here as connection is closed
// and this is a transaction callback so there is no
// way to report the error.
}
}
}
{code}
The thread of transationComplete() should check isClosedInternal() which is
still false. So it will not close the connection. And the connection.close() is
missed both in close() and transactionComplete(). I think it could be one cause
of the connection leaking.
> Connection leak during XATransaction in high load
> -------------------------------------------------
>
> Key: DBCP-484
> URL: https://issues.apache.org/jira/browse/DBCP-484
> Project: Commons Dbcp
> Issue Type: Bug
> Affects Versions: 2.2.0
> Reporter: Emanuel Freitas
> Priority: Major
>
> We're experiencing a connection leak in a distributed transaction when the
> system is under heavy load. We're using commons-dbcp (latest version) +
> eclipselink and narayana to perform transaction coordination.
> From time to time we can see a stacktrace reporting an abandoned connection.
> We are trying to figure out what's the root cause and we think that might be
> some issue in the commons dbcp (not sure) . More specifically, this parte of
> the code:
> ManagedConnection#updateTransactionStatus
> {code:java}
> if (transactionContext != null) {
> if (transactionContext.isActive()) {
> if (transactionContext !=
> transactionRegistry.getActiveTransactionContext()) {
> throw new SQLException("Connection can not be used while enlisted
> in another transaction");
> }
> return;
> }
> // transaction should have been cleared up by TransactionContextListener,
> but in
> // rare cases another lister could have registered which uses the
> connection before
> // our listener is called. In that rare case, trigger the transaction
> complete call now
> transactionComplete();
> }{code}
>
> If the transactionContext is different than null but the state is not
> "active" (ex: STATUS_ROLLEDBACK, STATUS_ROLLING_BACK, etc) it executes the
> transactionComplete mothod that clears the reference to a shared connection
> and after that the connection is never closed (returned to the pool).
>
> If we move the transactionComplete(); to an else,(see below), the connection
> leak does not happen.
> {code:java}
> if (transactionContext != null) {
> if (transactionContext.isActive()) {
> if (transactionContext !=
> transactionRegistry.getActiveTransactionContext()) {
> throw new SQLException("Connection can not be used while enlisted
> in another transaction");
> }
> return;
> }
> } else {
> transactionComplete();
> }{code}
>
> After this the dbcp unit tests still pass but I'm not sure about this
> changes. Can you please check?
> Thanks
>
--
This message was sent by Atlassian JIRA
(v7.6.3#76005)