简体   繁体   中英

TransactionScope with uncommited first transaction fails on distributed transaction

When trying to debug something I found code that was effectively doing the following:

  1. Creating a TransactionScope
  2. Creating a Transaction (in this case an nHibernate tx, but not really important)
  3. Creating a second transaction (in this case a standard ADO.Net Tx)
  4. Committing the second transaction
  5. Calling Complete() on the Transaction scope
  6. Disposing the Transaction Scope.

Now - Creating a transaction and not committing is probably a bad idea anyway - especially when having (and that was the bug fix).

However when testing this - I tried various combinations of the above (committing all transactions, some transactions, no transactions (ie only TScope) committing the First, but not second, adding other transactions etc) and in all thesting I found that the following to be true:

Only when I failed to commit the first transaction AND the transaction scope became distributed, the Dispose of the TScope would fail with:

System.InvalidOperationException : The operation is not valid for the current state of the enlistment.

I am now curious and would like to know why this is the case?

I suspect the problem you see is covered by one of these: https://nhibernate.jira.com/issues/?jql=project%20%3D%2010000%20AND%20labels%20%3D%20TransactionScope

I'm not entirely sure what happens but I've seen similar behaviour, eg if NH enlists in the ambient transaction, and the transaction later becomes distributed, calling TransactionScope.Complete() might hang for 20 seconds and then fail.

NH will try to enlist in a TransactionScope even if you don't use an NH transaction. In this case, NH will flush changes during the ambient transaction's Prepare() phase. It will do this on the db connection, but that has also enlisted in the transaction and will get its own Prepare() call. Unfortunately I haven't been able to figure out the exact problem, but I suspect what happens is that in some circumstances the db connections Prepare() will be called before NHibernate's Prepare(). The latter will try to continue to use the db connection, and it appears this causes some sort of deadlock.

Using a NH transaction and committing this before completing the transaction scope will make NH flush its changes before the underlying DB connection enters the prepare-phase.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM