InvalidOperationException in AbstractBatcher when distributed transaction is aborted
Description
Environment
is related to
Activity

Frédéric Delaporte September 7, 2017 at 8:08 AM
Although no dedicated test case seems here for this specific case, I believe solves it by no more closing session from system transaction completion notifications.
You may even activate an option (Environment.UseConnectionOnSystemTransactionPrepare
/transaction.use_connection_on_system_prepare
in hibernate.cfg
) for disabling using the session from system transaction completion notifications. (This disables the flush on commit feature for system transactions, requiring explicit flushes instead.)

Joseph Lam August 20, 2013 at 4:22 PM
My observation has been that TransactionCompleted events are fired from the same thread for local-only transactions, and are fired from a different thread when the transaction is a distributed one.
In a distributed transaction, System.Transactions implements two-phase commit and due to the underlying async nature of the protocol, TransactionCompleted events are fired asynchronously as soon as the commit decision is made (but not necessarily after all participants have completed their execution on the decision, depending on how fast the commit/rollback executions take). For local transaction because the commit is single-phase, the event is fired synchronously.
When NH closes the connection in a separate TransactionCompleted handler thread under distributed transaction, it occasionally corrupts the connection pool (especially when the distributed transaction was aborted due to being a deadlock victom) and the next unlucky session who picks up the corrupted connection will run into errors like 'Server failed to resume transaction'.

Oskar Berggren September 9, 2012 at 7:13 PM
I think the real problem is that NHibernate, when under the influence of a TransactionScope, currently may close the session on a different thread. This seems to be specifically not supported by ADO.NET, and cause a whole bunch of problems.
Peter Beams July 25, 2012 at 12:46 PM
Fixed in pull request https://github.com/nhibernate/nhibernate-core/pull/130 after further changes needed.
Peter Beams July 23, 2012 at 9:45 AM
Fixed in pull request https://github.com/nhibernate/nhibernate-core/pull/128
Using MS SQL Server 2008 R2 with NHibernate, get this exception intermittently when a distributed transaction is being aborted.
Framework Version: v4.0.30319 Description: The process was terminated due to an unhandled exception. Exception Info: System.InvalidOperationException Stack: at System.Collections.Generic.Dictionary`2+KeyCollection+Enumerator[[System._Canon, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System._Canon, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]].MoveNext() at NHibernate.AdoNet.AbstractBatcher.CloseCommands() at NHibernate.AdoNet.AbstractBatcher.Dispose(Boolean) at NHibernate.Impl.SessionImpl.Close() at NHibernate.Impl.SessionImpl.Dispose(Boolean) at NHibernate.Transaction.AdoNetWithDistributedTransactionFactory+<>c_DisplayClass1.b_0(System.Object, System.Transactions.TransactionEventArgs) at System.Transactions.TransactionCompletedEventHandler.Invoke(System.Object, System.Transactions.TransactionEventArgs) at System.Transactions.TransactionStatePromotedAborted.EnterState(System.Transactions.InternalTransaction) at System.Transactions.InternalTransaction.DistributedTransactionOutcome(System.Transactions.InternalTransaction, System.Transactions.TransactionStatus) at System.Transactions.Oletx.RealOletxTransaction.FireOutcome(System.Transactions.TransactionStatus) at System.Transactions.Oletx.OutcomeEnlistment.InvokeOutcomeFunction(System.Transactions.TransactionStatus) at System.Transactions.Oletx.OletxTransactionManager.ShimNotificationCallback(System.Object, Boolean) at System.Threading._ThreadPoolWaitOrTimerCallback.PerformWaitOrTimerCallback(System.Object, Boolean)