In distributed transaction cases, the transaction completion occurs asynchronously and concurrently to code following the transaction scope disposal.
In such case, it is possible to release the connection to the pool while its transaction is still ongoing, without having any clue about that. And requesting a connection from pool may then yield a connection still having an ongoing transaction.
Trying to begin a new transaction on it then fails with error: System.InvalidOperationException : A transaction is currently active. Parallel transactions are not supported.
Granted, that is a wicked case.
discussion with a Microsoft employee: when distributed, MSDTC considers a transaction to be committed once it has collected all participant votes for committing from prepare phase. It then immediately notifies all participants of the outcome. This causes TransactionScope.Dispose to leave while the second phase of participants may still be executing. This means the transaction from the db view point can still be pending and not yet committed after the scope disposal. This is by design of MSDTC and we have to cope with that.
Some data provider may have a global locking mechanism causing any subsequent connection use to wait for the end of the commit phase, but this is not a general case. Such a mechanism has been put in place for NHibernate own volatile resource, but this only guarantee us that this NHibernate resource has ended its processing, it does not guarantee us that is the case of the Firebird one.
This causes a number of NHibernate tests to fail with Firebird regularly but not consistently.
A test case pattern for this would likely be like the [DistributedRollback|https://github.com/npgsql/npgsql/blob/dev/test/Npgsql.Tests/SystemTransactionTests.cs#L204
] test of Npgsql. There are more wicked cases involving distributed transaction in this test file, it may be worth checking them with Firebird too.
(For information, Npgsql checks distributed transaction status on connection when closed for avoiding returning them to pool if the transaction is not ended, something like just flagging them as having to return to pool once their transaction completion event finally gets executed. This avoid at least the pool trouble but not some other issues like trying to reuse the same connection immediately after a distributed scope disposal.)