You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@ibatis.apache.org by "Clinton Begin (JIRA)" <ib...@incubator.apache.org> on 2006/06/13 20:33:32 UTC

[jira] Closed: (IBATIS-286) race-condition bug causing transactions to fail due to SessionScope to be in an inconsistent state.

     [ http://issues.apache.org/jira/browse/IBATIS-286?page=all ]
     
Clinton Begin closed IBATIS-286:
--------------------------------

    Resolution: Fixed

fixed as described

> 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