race-condition bug causing transactions to fail due to SessionScope to be in an
inconsistent state.
---------------------------------------------------------------------------------------------------
Key: IBATIS-286
URL: http://issues.apache.org/jira/browse/IBATIS-286
Project: iBatis for Java
Type: Bug
Components: SQL Maps
Versions: 2.1.7
Environment: JDK 1.4.2
SunOS eqd-dev8.uk.jpmorgan.com 5.8 Generic_117350-28 sun4u sparc
SUNW,Sun-Fire-480R
Reporter: Scott William Sinclair
Summary:
Discovered a race-condition bug which causes the
com.ibatis.sqlmap.engine.transaction.TransactionManager to throw exceptions due
to the com.ibatis.sqlmap.engine.scope.SessionScope being in an inconsistent
state.
The transaction manager checks the
com.ibatis.sqlmap.engine.transaction.TransactionState
(STATE_STARTED,STATE_COMMITTED,STATE_ENDED,STATE_USER_PROVIDED) of the
SessionScope and throws an exception if it is not in the expected state.
Worked Example:
a SqlMapSessionImpl s1 is created for a thread t1 and a session scope ss1 is
popped from the pool
s1.setUserConnection(con) is called to perform a user-controlled
transaction
the transaction executes normally
s1.setUserConnection(null) is called to free up the connection as
specified in the iBatis docs
The SessionScope ss1 is pushed back into the pool for later use
a SqlMapSessionImpl s2 is created for a thread t2 and session scope ss1 is
popped from the pool again
s2.startTransaction(...) is called to start a conventional iBatis
managed transaction on thread t2 with SessionScope ss1
s1.startTransaction(...) is called to start a conventional iBatis
managed transaction on thread t1 with SessionScope ss1
thread t1 throws the exception <INSERT HERE>
this is because SessionScope ss1 has been initialized by
s2.startTransaction() causing the s1.startTransaction() to fail due to the
unexpected state of ss1.
SqlMapSessionImpl s1 is closed and SessionScope ss1 is returned to the
pool for later use.
s2.execute(...) is called to execute statements
s2.commitTransaction(...) is called to commit the iBatis managed
transaction
s2.endTransaction(...) is called to complete the iBatis managed
transaction
this closes SqlMapSessionImpl s2, and SessionScope ss1 is returned to
the pool for later use.
SessionScope ss1 is now in the pool twice, causing ss1 to be available
to multiple threads.
This results in the same race condition reoccurring.
Fix:
Ensure that SqlMapSessionImpl is closed when
SqlMapSessionImpl.setUserConnection(null) is called.
Class com.ibatis.sqlmap.engine.impl.SqlMapClientImpl:
public void setUserConnection(Connection connection) throws SQLException {
try {
getLocalSqlMapSession().setUserConnection(connection);
} finally {
if (connection == null) { //bug fix:if connection == null, close the
session like we are ending a transaction
getLocalSqlMapSession().close();
}
}
}
Remove the push(SessionScope) call in
SqlMapExecutorDelegate.setUserProvidedTransaction(SessionScope session,
Connection userConnection) when userConnection is null. This makes sure that
the SessionScope is not pushed twice.
Class com.ibatis.sqlmap.engine.impl.SqlMapExecutorDelegate:
public void setUserProvidedTransaction(SessionScope session, Connection
userConnection) {
if (session.getTransactionState() == TransactionState.STATE_USER_PROVIDED) {
session.recallTransactionState();
}
if (userConnection != null) {
Connection conn = userConnection;
session.saveTransactionState();
session.setTransaction(new UserProvidedTransaction(conn));
session.setTransactionState(TransactionState.STATE_USER_PROVIDED);
} else {
session.setTransaction(null);
//pushSession(session); bug fix: commented out done as part of closing the
SqlMapSessionImpl
}
}
--
This message is automatically generated by JIRA.
-
If you think it was sent incorrectly contact one of the administrators:
http://issues.apache.org/jira/secure/Administrators.jspa
-
For more information on JIRA, see:
http://www.atlassian.com/software/jira