From 064818bc88d6d66c24bf010a465332d4f1d3ae43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Delaporte?= <12201973+fredericdelaporte@users.noreply.github.com> Date: Sat, 24 Aug 2019 21:41:33 +0200 Subject: [PATCH 1/6] Fix dependent transaction failure Fix #2172 --- .../DistributedSystemTransactionFixture.cs | 44 ++++++++++++++++--- .../SystemTransactionFixture.cs | 41 ++++++++++++++++- .../DistributedSystemTransactionFixture.cs | 43 +++++++++++++++--- .../SystemTransactionFixture.cs | 40 ++++++++++++++++- src/NHibernate.Test/TestDialect.cs | 8 ++++ .../TestDialects/PostgreSQL83TestDialect.cs | 6 +++ .../AdoNetWithSystemTransactionFactory.cs | 1 - .../AdoNetWithSystemTransactionFactory.cs | 18 +++++--- 8 files changed, 181 insertions(+), 20 deletions(-) diff --git a/src/NHibernate.Test/Async/SystemTransactions/DistributedSystemTransactionFixture.cs b/src/NHibernate.Test/Async/SystemTransactions/DistributedSystemTransactionFixture.cs index d8cafe793a3..72998cf640f 100644 --- a/src/NHibernate.Test/Async/SystemTransactions/DistributedSystemTransactionFixture.cs +++ b/src/NHibernate.Test/Async/SystemTransactions/DistributedSystemTransactionFixture.cs @@ -13,13 +13,14 @@ using System.Threading; using System.Transactions; using log4net; -using log4net.Repository.Hierarchy; using NHibernate.Cfg; using NHibernate.Engine; -using NHibernate.Linq; using NHibernate.Test.TransactionTest; using NUnit.Framework; +using sysTran = System.Transactions; +using NHibernate.Linq; + namespace NHibernate.Test.SystemTransactions { using System.Threading.Tasks; @@ -48,7 +49,7 @@ public async Task SupportsEnlistingInDistributedAsync() Assert.AreNotEqual( Guid.Empty, - System.Transactions.Transaction.Current.TransactionInformation.DistributedIdentifier, + sysTran.Transaction.Current.TransactionInformation.DistributedIdentifier, "Transaction lacks a distributed identifier"); using (var s = OpenSession()) @@ -75,7 +76,7 @@ public async Task SupportsPromotingToDistributedAsync() "Failure promoting the transaction to distributed while already having enlisted a connection."); Assert.AreNotEqual( Guid.Empty, - System.Transactions.Transaction.Current.TransactionInformation.DistributedIdentifier, + sysTran.Transaction.Current.TransactionInformation.DistributedIdentifier, "Transaction lacks a distributed identifier"); } } @@ -688,6 +689,39 @@ public async Task EnforceConnectionUsageRulesOnTransactionCompletionAsync() Assert.That(interceptor.AfterException, Is.TypeOf()); } + [Theory] + public async Task CanUseDependentTransactionAsync(bool explicitFlush) + { + if (!TestDialect.SupportsDependentTransaction) + Assert.Ignore("Dialect does not support dependent transactions"); + IgnoreIfUnsupported(explicitFlush); + + using (var committable = new CommittableTransaction()) + { + sysTran.Transaction.Current = committable; + using (var clone = committable.DependentClone(DependentCloneOption.RollbackIfNotComplete)) + { + sysTran.Transaction.Current = clone; + + using (var s = OpenSession()) + { + if (!AutoJoinTransaction) + s.JoinTransaction(); + await (s.SaveAsync(new Person())); + + if (explicitFlush) + await (s.FlushAsync()); + clone.Complete(); + } + } + + sysTran.Transaction.Current = committable; + committable.Commit(); + } + + sysTran.Transaction.Current = null; + } + private Task DodgeTransactionCompletionDelayIfRequiredAsync(CancellationToken cancellationToken = default(CancellationToken)) { try @@ -716,7 +750,7 @@ public class ForceEscalationToDistributedTx : IEnlistmentNotification public static void Escalate(bool shouldRollBack = false) { var force = new ForceEscalationToDistributedTx(shouldRollBack); - System.Transactions.Transaction.Current.EnlistDurable(Guid.NewGuid(), force, EnlistmentOptions.None); + sysTran.Transaction.Current.EnlistDurable(Guid.NewGuid(), force, EnlistmentOptions.None); } private ForceEscalationToDistributedTx(bool shouldRollBack) diff --git a/src/NHibernate.Test/Async/SystemTransactions/SystemTransactionFixture.cs b/src/NHibernate.Test/Async/SystemTransactions/SystemTransactionFixture.cs index c5089580535..1bea1e4997c 100644 --- a/src/NHibernate.Test/Async/SystemTransactions/SystemTransactionFixture.cs +++ b/src/NHibernate.Test/Async/SystemTransactions/SystemTransactionFixture.cs @@ -16,9 +16,10 @@ using NHibernate.Cfg; using NHibernate.Driver; using NHibernate.Engine; -using NHibernate.Linq; using NHibernate.Test.TransactionTest; using NUnit.Framework; +using sysTran = System.Transactions; +using NHibernate.Linq; namespace NHibernate.Test.SystemTransactions { @@ -523,6 +524,44 @@ public async Task EnforceConnectionUsageRulesOnTransactionCompletionAsync() // Currently always forbidden, whatever UseConnectionOnSystemTransactionEvents. Assert.That(interceptor.AfterException, Is.TypeOf()); } + + [Theory] + public async Task CanUseDependentTransactionAsync(bool explicitFlush) + { + if (!TestDialect.SupportsDependentTransaction) + Assert.Ignore("Dialect does not support dependent transactions"); + IgnoreIfUnsupported(explicitFlush); + + try + { + using (var committable = new CommittableTransaction()) + { + sysTran.Transaction.Current = committable; + using (var clone = committable.DependentClone(DependentCloneOption.RollbackIfNotComplete)) + { + sysTran.Transaction.Current = clone; + + using (var s = OpenSession()) + { + if (!AutoJoinTransaction) + s.JoinTransaction(); + await (s.SaveAsync(new Person())); + + if (explicitFlush) + await (s.FlushAsync()); + clone.Complete(); + } + } + + sysTran.Transaction.Current = committable; + committable.Commit(); + } + } + finally + { + sysTran.Transaction.Current = null; + } + } } [TestFixture] diff --git a/src/NHibernate.Test/SystemTransactions/DistributedSystemTransactionFixture.cs b/src/NHibernate.Test/SystemTransactions/DistributedSystemTransactionFixture.cs index 78e23702e7b..22981640f9e 100644 --- a/src/NHibernate.Test/SystemTransactions/DistributedSystemTransactionFixture.cs +++ b/src/NHibernate.Test/SystemTransactions/DistributedSystemTransactionFixture.cs @@ -3,13 +3,13 @@ using System.Threading; using System.Transactions; using log4net; -using log4net.Repository.Hierarchy; using NHibernate.Cfg; using NHibernate.Engine; -using NHibernate.Linq; using NHibernate.Test.TransactionTest; using NUnit.Framework; +using sysTran = System.Transactions; + namespace NHibernate.Test.SystemTransactions { [TestFixture] @@ -37,7 +37,7 @@ public void SupportsEnlistingInDistributed() Assert.AreNotEqual( Guid.Empty, - System.Transactions.Transaction.Current.TransactionInformation.DistributedIdentifier, + sysTran.Transaction.Current.TransactionInformation.DistributedIdentifier, "Transaction lacks a distributed identifier"); using (var s = OpenSession()) @@ -64,7 +64,7 @@ public void SupportsPromotingToDistributed() "Failure promoting the transaction to distributed while already having enlisted a connection."); Assert.AreNotEqual( Guid.Empty, - System.Transactions.Transaction.Current.TransactionInformation.DistributedIdentifier, + sysTran.Transaction.Current.TransactionInformation.DistributedIdentifier, "Transaction lacks a distributed identifier"); } } @@ -711,6 +711,39 @@ public void AdditionalJoinDoesNotThrow() } } + [Theory] + public void CanUseDependentTransaction(bool explicitFlush) + { + if (!TestDialect.SupportsDependentTransaction) + Assert.Ignore("Dialect does not support dependent transactions"); + IgnoreIfUnsupported(explicitFlush); + + using (var committable = new CommittableTransaction()) + { + sysTran.Transaction.Current = committable; + using (var clone = committable.DependentClone(DependentCloneOption.RollbackIfNotComplete)) + { + sysTran.Transaction.Current = clone; + + using (var s = OpenSession()) + { + if (!AutoJoinTransaction) + s.JoinTransaction(); + s.Save(new Person()); + + if (explicitFlush) + s.Flush(); + clone.Complete(); + } + } + + sysTran.Transaction.Current = committable; + committable.Commit(); + } + + sysTran.Transaction.Current = null; + } + private void DodgeTransactionCompletionDelayIfRequired() { if (Sfi.ConnectionProvider.Driver.HasDelayedDistributedTransactionCompletion) @@ -725,7 +758,7 @@ public class ForceEscalationToDistributedTx : IEnlistmentNotification public static void Escalate(bool shouldRollBack = false) { var force = new ForceEscalationToDistributedTx(shouldRollBack); - System.Transactions.Transaction.Current.EnlistDurable(Guid.NewGuid(), force, EnlistmentOptions.None); + sysTran.Transaction.Current.EnlistDurable(Guid.NewGuid(), force, EnlistmentOptions.None); } private ForceEscalationToDistributedTx(bool shouldRollBack) diff --git a/src/NHibernate.Test/SystemTransactions/SystemTransactionFixture.cs b/src/NHibernate.Test/SystemTransactions/SystemTransactionFixture.cs index b68bbd5d86c..b79ad34fbb9 100644 --- a/src/NHibernate.Test/SystemTransactions/SystemTransactionFixture.cs +++ b/src/NHibernate.Test/SystemTransactions/SystemTransactionFixture.cs @@ -6,9 +6,9 @@ using NHibernate.Cfg; using NHibernate.Driver; using NHibernate.Engine; -using NHibernate.Linq; using NHibernate.Test.TransactionTest; using NUnit.Framework; +using sysTran = System.Transactions; namespace NHibernate.Test.SystemTransactions { @@ -522,6 +522,44 @@ public void AdditionalJoinDoesNotThrow() Assert.DoesNotThrow(() => s.JoinTransaction()); } } + + [Theory] + public void CanUseDependentTransaction(bool explicitFlush) + { + if (!TestDialect.SupportsDependentTransaction) + Assert.Ignore("Dialect does not support dependent transactions"); + IgnoreIfUnsupported(explicitFlush); + + try + { + using (var committable = new CommittableTransaction()) + { + sysTran.Transaction.Current = committable; + using (var clone = committable.DependentClone(DependentCloneOption.RollbackIfNotComplete)) + { + sysTran.Transaction.Current = clone; + + using (var s = OpenSession()) + { + if (!AutoJoinTransaction) + s.JoinTransaction(); + s.Save(new Person()); + + if (explicitFlush) + s.Flush(); + clone.Complete(); + } + } + + sysTran.Transaction.Current = committable; + committable.Commit(); + } + } + finally + { + sysTran.Transaction.Current = null; + } + } } [TestFixture] diff --git a/src/NHibernate.Test/TestDialect.cs b/src/NHibernate.Test/TestDialect.cs index 1e23fae97c3..f7d89ec02d0 100644 --- a/src/NHibernate.Test/TestDialect.cs +++ b/src/NHibernate.Test/TestDialect.cs @@ -157,6 +157,14 @@ public bool SupportsSqlType(SqlType sqlType) /// public virtual bool SupportsUsingConnectionOnSystemTransactionPrepare => true; + /// + /// Some databases fail with dependent transaction, typically when their driver tries to access the transaction + /// state from its two PC: the dependent transaction is meant to be disposed of before completing the actual + /// transaction, so it is usually disposed at this point, and its state cannot be read. (Drivers should always + /// clone transactions for avoiding this trouble.) + /// + public virtual bool SupportsDependentTransaction => true; + /// /// Some databases (provider?) fails to compute adequate column types for queries which columns /// computing include a parameter value. diff --git a/src/NHibernate.Test/TestDialects/PostgreSQL83TestDialect.cs b/src/NHibernate.Test/TestDialects/PostgreSQL83TestDialect.cs index 78f038ad3e6..c8541372762 100644 --- a/src/NHibernate.Test/TestDialects/PostgreSQL83TestDialect.cs +++ b/src/NHibernate.Test/TestDialects/PostgreSQL83TestDialect.cs @@ -16,5 +16,11 @@ public PostgreSQL83TestDialect(Dialect.Dialect dialect) /// Npgsql 3.2.4.1. /// public override bool SupportsUsingConnectionOnSystemTransactionPrepare => false; + + /// + /// Npgsql does not clone the transaction in its context, and uses it in its prepare phase. When that was a + /// dependent transaction, it is then usually already disposed of, causing Npgsql to crash. + /// + public override bool SupportsDependentTransaction => false; } } diff --git a/src/NHibernate/Async/Transaction/AdoNetWithSystemTransactionFactory.cs b/src/NHibernate/Async/Transaction/AdoNetWithSystemTransactionFactory.cs index 599752d270c..91558147f86 100644 --- a/src/NHibernate/Async/Transaction/AdoNetWithSystemTransactionFactory.cs +++ b/src/NHibernate/Async/Transaction/AdoNetWithSystemTransactionFactory.cs @@ -16,7 +16,6 @@ using NHibernate.AdoNet; using NHibernate.Engine; using NHibernate.Engine.Transaction; -using NHibernate.Impl; using NHibernate.Util; namespace NHibernate.Transaction diff --git a/src/NHibernate/Transaction/AdoNetWithSystemTransactionFactory.cs b/src/NHibernate/Transaction/AdoNetWithSystemTransactionFactory.cs index 3e0bedc3ed1..b1819d2937c 100644 --- a/src/NHibernate/Transaction/AdoNetWithSystemTransactionFactory.cs +++ b/src/NHibernate/Transaction/AdoNetWithSystemTransactionFactory.cs @@ -6,7 +6,6 @@ using NHibernate.AdoNet; using NHibernate.Engine; using NHibernate.Engine.Transaction; -using NHibernate.Impl; using NHibernate.Util; namespace NHibernate.Transaction @@ -186,6 +185,7 @@ public class SystemTransactionContext : ITransactionContext, IEnlistmentNotifica private readonly System.Transactions.Transaction _originalTransaction; private readonly ManualResetEventSlim _lock = new ManualResetEventSlim(true); private volatile bool _needCompletionLocking = true; + private bool _preparing; // Required for not locking the completion phase itself when locking session usages from concurrent threads. private static readonly AsyncLocal _bypassLock = new AsyncLocal(); private readonly int _systemTransactionCompletionLockTimeout; @@ -236,7 +236,7 @@ public virtual void Wait() // Remove the block then throw. Unlock(); throw new HibernateException( - "Synchronization timeout for transaction completion. Either raise {Cfg.Environment.SystemTransactionCompletionLockTimeout}, or this may be a bug in NHibernate."); + $"Synchronization timeout for transaction completion. Either raise {Cfg.Environment.SystemTransactionCompletionLockTimeout}, or this may be a bug in NHibernate."); } catch (HibernateException) { @@ -285,16 +285,17 @@ protected virtual void Unlock() { // Cloned transaction is not disposed "unexpectedly", its status is accessible till context disposal. var status = EnlistedTransaction.TransactionInformation.Status; - if (status != TransactionStatus.Active) + if (status != TransactionStatus.Active || _preparing) return status; - // The clone status can be out of date when active, check the original one (which could be disposed if - // the clone is out of date). + // The clone status can be out of date when active and not in prepare phase, in case of rollback. + // In such case the original transaction is already disposed, and trying to check its status will + // trigger a dispose exception. return _originalTransaction.TransactionInformation.Status; } catch (ObjectDisposedException ode) { - _logger.Warn(ode, "Enlisted transaction status was wrongly active, original transaction being already disposed. Will assume neither active nor committed."); + _logger.Warn(ode, "Enlisted transaction status is maybe wrongly active, original transaction being already disposed. Will assume neither active nor committed."); return null; } } @@ -310,6 +311,7 @@ protected virtual void Unlock() /// The object for notifying the prepare phase outcome. public virtual void Prepare(PreparingEnlistment preparingEnlistment) { + _preparing = true; using (_session.BeginContext()) { try @@ -343,10 +345,12 @@ public virtual void Prepare(PreparingEnlistment preparingEnlistment) Lock(); _logger.Debug("Prepared for system transaction"); + _preparing = false; preparingEnlistment.Prepared(); } catch (Exception exception) { + _preparing = false; _logger.Error(exception, "System transaction prepare phase failed"); try { @@ -404,7 +408,7 @@ protected virtual void ProcessSecondPhase(Enlistment enlistment, bool? success) /// /// Handle the transaction completion. Notify of the end of the /// transaction. Notify end of transaction to the session and to - /// if any. Close sessions requiring it then cleanup transaction contextes and then blocked + /// if any. Close sessions requiring it then cleanup transaction contexts and then blocked /// threads. /// /// if the transaction is committed, From e8d2f124859069b587bcc7c4bd7191a43a8e2c5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Delaporte?= <12201973+fredericdelaporte@users.noreply.github.com> Date: Sun, 25 Aug 2019 16:38:44 +0200 Subject: [PATCH 2/6] fixup! Fix dependent transaction failure --- src/NHibernate/AdoNet/ConnectionManager.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/NHibernate/AdoNet/ConnectionManager.cs b/src/NHibernate/AdoNet/ConnectionManager.cs index 80674df3cf5..f0b561c3db1 100644 --- a/src/NHibernate/AdoNet/ConnectionManager.cs +++ b/src/NHibernate/AdoNet/ConnectionManager.cs @@ -456,7 +456,13 @@ public DbCommand CreateCommand() public void EnlistIfRequired(System.Transactions.Transaction transaction) { if (transaction == _currentSystemTransaction) + { + // Short-circuit after having stored the transaction : they may be equal, but not the same reference. + // And the previous one may be an already disposed dependent clone, in which case we need to update + // our reference. + _currentSystemTransaction = transaction; return; + } _currentSystemTransaction = transaction; From 666e4f2be5e5915718fbe0c5441b7410b15c614b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Delaporte?= <12201973+fredericdelaporte@users.noreply.github.com> Date: Sun, 25 Aug 2019 18:17:12 +0200 Subject: [PATCH 3/6] fixup! Fix dependent transaction failure Fix chaining dependent transaction usage on the same session --- .../DistributedSystemTransactionFixture.cs | 73 +++++++++++++++++ .../SystemTransactionFixture.cs | 82 +++++++++++++++++++ .../DistributedSystemTransactionFixture.cs | 73 +++++++++++++++++ .../SystemTransactionFixture.cs | 82 +++++++++++++++++++ .../AdoNetWithSystemTransactionFactory.cs | 35 +++++++- 5 files changed, 342 insertions(+), 3 deletions(-) diff --git a/src/NHibernate.Test/Async/SystemTransactions/DistributedSystemTransactionFixture.cs b/src/NHibernate.Test/Async/SystemTransactions/DistributedSystemTransactionFixture.cs index 72998cf640f..dc91f5b0c37 100644 --- a/src/NHibernate.Test/Async/SystemTransactions/DistributedSystemTransactionFixture.cs +++ b/src/NHibernate.Test/Async/SystemTransactions/DistributedSystemTransactionFixture.cs @@ -722,6 +722,79 @@ public async Task CanUseDependentTransactionAsync(bool explicitFlush) sysTran.Transaction.Current = null; } + [Theory] + public async Task CanUseSessionWithManyDependentTransactionAsync(bool explicitFlush) + { + if (!TestDialect.SupportsDependentTransaction) + Assert.Ignore("Dialect does not support dependent transactions"); + IgnoreIfUnsupported(explicitFlush); + + try + { + using (var s = Sfi.WithOptions().ConnectionReleaseMode(ConnectionReleaseMode.OnClose).OpenSession()) + { + using (var committable = new CommittableTransaction()) + { + sysTran.Transaction.Current = committable; + using (var clone = committable.DependentClone(DependentCloneOption.RollbackIfNotComplete)) + { + sysTran.Transaction.Current = clone; + if (!AutoJoinTransaction) + s.JoinTransaction(); + // Acquire the connection + var count = await (s.Query().CountAsync()); + Assert.That(count, Is.EqualTo(0), "Unexpected initial entity count."); + clone.Complete(); + } + + using (var clone = committable.DependentClone(DependentCloneOption.RollbackIfNotComplete)) + { + sysTran.Transaction.Current = clone; + if (!AutoJoinTransaction) + s.JoinTransaction(); + await (s.SaveAsync(new Person())); + + if (explicitFlush) + await (s.FlushAsync()); + + clone.Complete(); + } + + using (var clone = committable.DependentClone(DependentCloneOption.RollbackIfNotComplete)) + { + sysTran.Transaction.Current = clone; + if (!AutoJoinTransaction) + s.JoinTransaction(); + var count = await (s.Query().CountAsync()); + Assert.That(count, Is.EqualTo(1), "Unexpected entity count after committed insert."); + clone.Complete(); + } + + sysTran.Transaction.Current = committable; + committable.Commit(); + } + } + } + finally + { + sysTran.Transaction.Current = null; + } + + await (DodgeTransactionCompletionDelayIfRequiredAsync()); + + using (var s = OpenSession()) + { + using (var tx = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled)) + { + if (!AutoJoinTransaction) + s.JoinTransaction(); + var count = await (s.Query().CountAsync()); + Assert.That(count, Is.EqualTo(1), "Unexpected entity count after global commit."); + tx.Complete(); + } + } + } + private Task DodgeTransactionCompletionDelayIfRequiredAsync(CancellationToken cancellationToken = default(CancellationToken)) { try diff --git a/src/NHibernate.Test/Async/SystemTransactions/SystemTransactionFixture.cs b/src/NHibernate.Test/Async/SystemTransactions/SystemTransactionFixture.cs index 1bea1e4997c..736af872ba2 100644 --- a/src/NHibernate.Test/Async/SystemTransactions/SystemTransactionFixture.cs +++ b/src/NHibernate.Test/Async/SystemTransactions/SystemTransactionFixture.cs @@ -562,6 +562,88 @@ public async Task CanUseDependentTransactionAsync(bool explicitFlush) sysTran.Transaction.Current = null; } } + + [Theory] + public async Task CanUseSessionWithManyDependentTransactionAsync(bool explicitFlush) + { + if (!TestDialect.SupportsDependentTransaction) + Assert.Ignore("Dialect does not support dependent transactions"); + IgnoreIfUnsupported(explicitFlush); + // ODBC with SQL-Server always causes system transactions to go distributed, which causes their transaction completion to run + // asynchronously. But ODBC enlistment also check the previous transaction in a way that do not guard against it + // being concurrently disposed of. See https://github.com/nhibernate/nhibernate-core/pull/1505 for more details. + if (Sfi.ConnectionProvider.Driver is OdbcDriver) + Assert.Ignore("ODBC sometimes fails on second scope by checking the previous transaction status, which may yield an object disposed exception"); + // SAP HANA & SQL Anywhere .Net providers always cause system transactions to be distributed, causing them to + // complete on concurrent threads. This creates race conditions when chaining scopes, the subsequent scope usage + // finding the connection still enlisted in the previous transaction, its complete being still not finished + // on its own thread. + if (Sfi.ConnectionProvider.Driver is HanaDriverBase || Sfi.ConnectionProvider.Driver is SapSQLAnywhere17Driver) + Assert.Ignore("SAP HANA and SQL Anywhere scope handling causes concurrency issues preventing chaining scope usages."); + + try + { + using (var s = WithOptions().ConnectionReleaseMode(ConnectionReleaseMode.OnClose).OpenSession()) + { + using (var committable = new CommittableTransaction()) + { + sysTran.Transaction.Current = committable; + using (var clone = committable.DependentClone(DependentCloneOption.RollbackIfNotComplete)) + { + sysTran.Transaction.Current = clone; + if (!AutoJoinTransaction) + s.JoinTransaction(); + // Acquire the connection + var count = await (s.Query().CountAsync()); + Assert.That(count, Is.EqualTo(0), "Unexpected initial entity count."); + clone.Complete(); + } + + using (var clone = committable.DependentClone(DependentCloneOption.RollbackIfNotComplete)) + { + sysTran.Transaction.Current = clone; + if (!AutoJoinTransaction) + s.JoinTransaction(); + await (s.SaveAsync(new Person())); + + if (explicitFlush) + await (s.FlushAsync()); + + clone.Complete(); + } + + using (var clone = committable.DependentClone(DependentCloneOption.RollbackIfNotComplete)) + { + sysTran.Transaction.Current = clone; + if (!AutoJoinTransaction) + s.JoinTransaction(); + var count = await (s.Query().CountAsync()); + Assert.That(count, Is.EqualTo(1), "Unexpected entity count after committed insert."); + clone.Complete(); + } + + sysTran.Transaction.Current = committable; + committable.Commit(); + } + } + } + finally + { + sysTran.Transaction.Current = null; + } + + using (var s = OpenSession()) + { + using (var tx = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled)) + { + if (!AutoJoinTransaction) + s.JoinTransaction(); + var count = await (s.Query().CountAsync()); + Assert.That(count, Is.EqualTo(1), "Unexpected entity count after global commit."); + tx.Complete(); + } + } + } } [TestFixture] diff --git a/src/NHibernate.Test/SystemTransactions/DistributedSystemTransactionFixture.cs b/src/NHibernate.Test/SystemTransactions/DistributedSystemTransactionFixture.cs index 22981640f9e..c17e8b3a28d 100644 --- a/src/NHibernate.Test/SystemTransactions/DistributedSystemTransactionFixture.cs +++ b/src/NHibernate.Test/SystemTransactions/DistributedSystemTransactionFixture.cs @@ -744,6 +744,79 @@ public void CanUseDependentTransaction(bool explicitFlush) sysTran.Transaction.Current = null; } + [Theory] + public void CanUseSessionWithManyDependentTransaction(bool explicitFlush) + { + if (!TestDialect.SupportsDependentTransaction) + Assert.Ignore("Dialect does not support dependent transactions"); + IgnoreIfUnsupported(explicitFlush); + + try + { + using (var s = Sfi.WithOptions().ConnectionReleaseMode(ConnectionReleaseMode.OnClose).OpenSession()) + { + using (var committable = new CommittableTransaction()) + { + sysTran.Transaction.Current = committable; + using (var clone = committable.DependentClone(DependentCloneOption.RollbackIfNotComplete)) + { + sysTran.Transaction.Current = clone; + if (!AutoJoinTransaction) + s.JoinTransaction(); + // Acquire the connection + var count = s.Query().Count(); + Assert.That(count, Is.EqualTo(0), "Unexpected initial entity count."); + clone.Complete(); + } + + using (var clone = committable.DependentClone(DependentCloneOption.RollbackIfNotComplete)) + { + sysTran.Transaction.Current = clone; + if (!AutoJoinTransaction) + s.JoinTransaction(); + s.Save(new Person()); + + if (explicitFlush) + s.Flush(); + + clone.Complete(); + } + + using (var clone = committable.DependentClone(DependentCloneOption.RollbackIfNotComplete)) + { + sysTran.Transaction.Current = clone; + if (!AutoJoinTransaction) + s.JoinTransaction(); + var count = s.Query().Count(); + Assert.That(count, Is.EqualTo(1), "Unexpected entity count after committed insert."); + clone.Complete(); + } + + sysTran.Transaction.Current = committable; + committable.Commit(); + } + } + } + finally + { + sysTran.Transaction.Current = null; + } + + DodgeTransactionCompletionDelayIfRequired(); + + using (var s = OpenSession()) + { + using (var tx = new TransactionScope()) + { + if (!AutoJoinTransaction) + s.JoinTransaction(); + var count = s.Query().Count(); + Assert.That(count, Is.EqualTo(1), "Unexpected entity count after global commit."); + tx.Complete(); + } + } + } + private void DodgeTransactionCompletionDelayIfRequired() { if (Sfi.ConnectionProvider.Driver.HasDelayedDistributedTransactionCompletion) diff --git a/src/NHibernate.Test/SystemTransactions/SystemTransactionFixture.cs b/src/NHibernate.Test/SystemTransactions/SystemTransactionFixture.cs index b79ad34fbb9..ddc3726531f 100644 --- a/src/NHibernate.Test/SystemTransactions/SystemTransactionFixture.cs +++ b/src/NHibernate.Test/SystemTransactions/SystemTransactionFixture.cs @@ -560,6 +560,88 @@ public void CanUseDependentTransaction(bool explicitFlush) sysTran.Transaction.Current = null; } } + + [Theory] + public void CanUseSessionWithManyDependentTransaction(bool explicitFlush) + { + if (!TestDialect.SupportsDependentTransaction) + Assert.Ignore("Dialect does not support dependent transactions"); + IgnoreIfUnsupported(explicitFlush); + // ODBC with SQL-Server always causes system transactions to go distributed, which causes their transaction completion to run + // asynchronously. But ODBC enlistment also check the previous transaction in a way that do not guard against it + // being concurrently disposed of. See https://github.com/nhibernate/nhibernate-core/pull/1505 for more details. + if (Sfi.ConnectionProvider.Driver is OdbcDriver) + Assert.Ignore("ODBC sometimes fails on second scope by checking the previous transaction status, which may yield an object disposed exception"); + // SAP HANA & SQL Anywhere .Net providers always cause system transactions to be distributed, causing them to + // complete on concurrent threads. This creates race conditions when chaining scopes, the subsequent scope usage + // finding the connection still enlisted in the previous transaction, its complete being still not finished + // on its own thread. + if (Sfi.ConnectionProvider.Driver is HanaDriverBase || Sfi.ConnectionProvider.Driver is SapSQLAnywhere17Driver) + Assert.Ignore("SAP HANA and SQL Anywhere scope handling causes concurrency issues preventing chaining scope usages."); + + try + { + using (var s = WithOptions().ConnectionReleaseMode(ConnectionReleaseMode.OnClose).OpenSession()) + { + using (var committable = new CommittableTransaction()) + { + sysTran.Transaction.Current = committable; + using (var clone = committable.DependentClone(DependentCloneOption.RollbackIfNotComplete)) + { + sysTran.Transaction.Current = clone; + if (!AutoJoinTransaction) + s.JoinTransaction(); + // Acquire the connection + var count = s.Query().Count(); + Assert.That(count, Is.EqualTo(0), "Unexpected initial entity count."); + clone.Complete(); + } + + using (var clone = committable.DependentClone(DependentCloneOption.RollbackIfNotComplete)) + { + sysTran.Transaction.Current = clone; + if (!AutoJoinTransaction) + s.JoinTransaction(); + s.Save(new Person()); + + if (explicitFlush) + s.Flush(); + + clone.Complete(); + } + + using (var clone = committable.DependentClone(DependentCloneOption.RollbackIfNotComplete)) + { + sysTran.Transaction.Current = clone; + if (!AutoJoinTransaction) + s.JoinTransaction(); + var count = s.Query().Count(); + Assert.That(count, Is.EqualTo(1), "Unexpected entity count after committed insert."); + clone.Complete(); + } + + sysTran.Transaction.Current = committable; + committable.Commit(); + } + } + } + finally + { + sysTran.Transaction.Current = null; + } + + using (var s = OpenSession()) + { + using (var tx = new TransactionScope()) + { + if (!AutoJoinTransaction) + s.JoinTransaction(); + var count = s.Query().Count(); + Assert.That(count, Is.EqualTo(1), "Unexpected entity count after global commit."); + tx.Complete(); + } + } + } } [TestFixture] diff --git a/src/NHibernate/Transaction/AdoNetWithSystemTransactionFactory.cs b/src/NHibernate/Transaction/AdoNetWithSystemTransactionFactory.cs index b1819d2937c..55d3efd5515 100644 --- a/src/NHibernate/Transaction/AdoNetWithSystemTransactionFactory.cs +++ b/src/NHibernate/Transaction/AdoNetWithSystemTransactionFactory.cs @@ -236,7 +236,9 @@ public virtual void Wait() // Remove the block then throw. Unlock(); throw new HibernateException( - $"Synchronization timeout for transaction completion. Either raise {Cfg.Environment.SystemTransactionCompletionLockTimeout}, or this may be a bug in NHibernate."); + $"Synchronization timeout for transaction completion. Either raise" + + $"{Cfg.Environment.SystemTransactionCompletionLockTimeout}, or check all scopes are properly" + + $"disposed and/or all direct System.Transaction.Current changes are properly managed."); } catch (HibernateException) { @@ -288,14 +290,41 @@ protected virtual void Unlock() if (status != TransactionStatus.Active || _preparing) return status; - // The clone status can be out of date when active and not in prepare phase, in case of rollback. + // The clone status can be out of date when active and not in prepare phase, in case of rollback or + // dependent clone usage. // In such case the original transaction is already disposed, and trying to check its status will // trigger a dispose exception. return _originalTransaction.TransactionInformation.Status; } catch (ObjectDisposedException ode) { - _logger.Warn(ode, "Enlisted transaction status is maybe wrongly active, original transaction being already disposed. Will assume neither active nor committed."); + // For ruling out the dependent clone case when possible, we check if the current transaction is + // equal to the context one (System.Transactions.Transaction does override equality for this), and + // in such case, we check the state of the current transaction instead. (The state of the current + // transaction if equal can only be the same, but it will be inaccessible in case of rollback, due + // to the current transaction being already disposed.) + // The current transaction may not be reachable during 2PC phases and transaction completion events, + // but in such cases the context transaction is either no more active or in prepare phase, which is + // already covered by _preparing test. + try + { + var currentTransaction = System.Transactions.Transaction.Current; + if (!ReferenceEquals(currentTransaction, _originalTransaction) && + currentTransaction == EnlistedTransaction) + return currentTransaction.TransactionInformation.Status; + } + catch (ObjectDisposedException) + { + // Just ignore that one, no use to log two dispose exceptions which are indeed the same. + } + catch (InvalidOperationException ioe) + { + _logger.Warn(ioe, "Attempting to dodge a disposed transaction trouble, current" + + "transaction was unreachable."); + } + + _logger.Warn(ode, "Enlisted transaction status is maybe wrongly active, original " + + "transaction being already disposed. Will assume neither active nor committed."); return null; } } From 384b48126603bdf6b9692aa337155f4160468656 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Delaporte?= <12201973+fredericdelaporte@users.noreply.github.com> Date: Sun, 25 Aug 2019 20:05:14 +0200 Subject: [PATCH 4/6] fixup! Fix dependent transaction failure --- .../DistributedSystemTransactionFixture.cs | 39 +++++++++++-------- .../DistributedSystemTransactionFixture.cs | 39 +++++++++++-------- 2 files changed, 44 insertions(+), 34 deletions(-) diff --git a/src/NHibernate.Test/Async/SystemTransactions/DistributedSystemTransactionFixture.cs b/src/NHibernate.Test/Async/SystemTransactions/DistributedSystemTransactionFixture.cs index dc91f5b0c37..e54fb83695a 100644 --- a/src/NHibernate.Test/Async/SystemTransactions/DistributedSystemTransactionFixture.cs +++ b/src/NHibernate.Test/Async/SystemTransactions/DistributedSystemTransactionFixture.cs @@ -696,30 +696,35 @@ public async Task CanUseDependentTransactionAsync(bool explicitFlush) Assert.Ignore("Dialect does not support dependent transactions"); IgnoreIfUnsupported(explicitFlush); - using (var committable = new CommittableTransaction()) + try { - sysTran.Transaction.Current = committable; - using (var clone = committable.DependentClone(DependentCloneOption.RollbackIfNotComplete)) + using (var committable = new CommittableTransaction()) { - sysTran.Transaction.Current = clone; - - using (var s = OpenSession()) + sysTran.Transaction.Current = committable; + using (var clone = committable.DependentClone(DependentCloneOption.RollbackIfNotComplete)) { - if (!AutoJoinTransaction) - s.JoinTransaction(); - await (s.SaveAsync(new Person())); + sysTran.Transaction.Current = clone; - if (explicitFlush) - await (s.FlushAsync()); - clone.Complete(); + using (var s = OpenSession()) + { + if (!AutoJoinTransaction) + s.JoinTransaction(); + await (s.SaveAsync(new Person())); + + if (explicitFlush) + await (s.FlushAsync()); + clone.Complete(); + } } - } - sysTran.Transaction.Current = committable; - committable.Commit(); + sysTran.Transaction.Current = committable; + committable.Commit(); + } + } + finally + { + sysTran.Transaction.Current = null; } - - sysTran.Transaction.Current = null; } [Theory] diff --git a/src/NHibernate.Test/SystemTransactions/DistributedSystemTransactionFixture.cs b/src/NHibernate.Test/SystemTransactions/DistributedSystemTransactionFixture.cs index c17e8b3a28d..728902ee7f7 100644 --- a/src/NHibernate.Test/SystemTransactions/DistributedSystemTransactionFixture.cs +++ b/src/NHibernate.Test/SystemTransactions/DistributedSystemTransactionFixture.cs @@ -718,30 +718,35 @@ public void CanUseDependentTransaction(bool explicitFlush) Assert.Ignore("Dialect does not support dependent transactions"); IgnoreIfUnsupported(explicitFlush); - using (var committable = new CommittableTransaction()) + try { - sysTran.Transaction.Current = committable; - using (var clone = committable.DependentClone(DependentCloneOption.RollbackIfNotComplete)) + using (var committable = new CommittableTransaction()) { - sysTran.Transaction.Current = clone; - - using (var s = OpenSession()) + sysTran.Transaction.Current = committable; + using (var clone = committable.DependentClone(DependentCloneOption.RollbackIfNotComplete)) { - if (!AutoJoinTransaction) - s.JoinTransaction(); - s.Save(new Person()); + sysTran.Transaction.Current = clone; - if (explicitFlush) - s.Flush(); - clone.Complete(); + using (var s = OpenSession()) + { + if (!AutoJoinTransaction) + s.JoinTransaction(); + s.Save(new Person()); + + if (explicitFlush) + s.Flush(); + clone.Complete(); + } } - } - sysTran.Transaction.Current = committable; - committable.Commit(); + sysTran.Transaction.Current = committable; + committable.Commit(); + } + } + finally + { + sysTran.Transaction.Current = null; } - - sysTran.Transaction.Current = null; } [Theory] From 6d150275f1762a4f37529f22ee5a4acc49e54d2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Delaporte?= <12201973+fredericdelaporte@users.noreply.github.com> Date: Sun, 25 Aug 2019 21:33:53 +0200 Subject: [PATCH 5/6] fixup! Fix dependent transaction failure --- src/AsyncGenerator.yml | 12 ++ .../DistributedSystemTransactionFixture.cs | 111 ---------------- .../SystemTransactionFixture.cs | 120 ------------------ 3 files changed, 12 insertions(+), 231 deletions(-) diff --git a/src/AsyncGenerator.yml b/src/AsyncGenerator.yml index b12437b419c..6d6dc306a75 100644 --- a/src/AsyncGenerator.yml +++ b/src/AsyncGenerator.yml @@ -183,6 +183,18 @@ applyChanges: true analyzation: methodConversion: + - conversion: Ignore + name: CanUseDependentTransaction + containingTypeName: DistributedSystemTransactionFixture + - conversion: Ignore + name: CanUseSessionWithManyDependentTransaction + containingTypeName: DistributedSystemTransactionFixture + - conversion: Ignore + name: CanUseDependentTransaction + containingTypeName: SystemTransactionFixture + - conversion: Ignore + name: CanUseSessionWithManyDependentTransaction + containingTypeName: SystemTransactionFixture - conversion: Copy name: AfterTransactionCompletionProcess_EvictsFromCache - conversion: Copy diff --git a/src/NHibernate.Test/Async/SystemTransactions/DistributedSystemTransactionFixture.cs b/src/NHibernate.Test/Async/SystemTransactions/DistributedSystemTransactionFixture.cs index e54fb83695a..242e8272814 100644 --- a/src/NHibernate.Test/Async/SystemTransactions/DistributedSystemTransactionFixture.cs +++ b/src/NHibernate.Test/Async/SystemTransactions/DistributedSystemTransactionFixture.cs @@ -689,117 +689,6 @@ public async Task EnforceConnectionUsageRulesOnTransactionCompletionAsync() Assert.That(interceptor.AfterException, Is.TypeOf()); } - [Theory] - public async Task CanUseDependentTransactionAsync(bool explicitFlush) - { - if (!TestDialect.SupportsDependentTransaction) - Assert.Ignore("Dialect does not support dependent transactions"); - IgnoreIfUnsupported(explicitFlush); - - try - { - using (var committable = new CommittableTransaction()) - { - sysTran.Transaction.Current = committable; - using (var clone = committable.DependentClone(DependentCloneOption.RollbackIfNotComplete)) - { - sysTran.Transaction.Current = clone; - - using (var s = OpenSession()) - { - if (!AutoJoinTransaction) - s.JoinTransaction(); - await (s.SaveAsync(new Person())); - - if (explicitFlush) - await (s.FlushAsync()); - clone.Complete(); - } - } - - sysTran.Transaction.Current = committable; - committable.Commit(); - } - } - finally - { - sysTran.Transaction.Current = null; - } - } - - [Theory] - public async Task CanUseSessionWithManyDependentTransactionAsync(bool explicitFlush) - { - if (!TestDialect.SupportsDependentTransaction) - Assert.Ignore("Dialect does not support dependent transactions"); - IgnoreIfUnsupported(explicitFlush); - - try - { - using (var s = Sfi.WithOptions().ConnectionReleaseMode(ConnectionReleaseMode.OnClose).OpenSession()) - { - using (var committable = new CommittableTransaction()) - { - sysTran.Transaction.Current = committable; - using (var clone = committable.DependentClone(DependentCloneOption.RollbackIfNotComplete)) - { - sysTran.Transaction.Current = clone; - if (!AutoJoinTransaction) - s.JoinTransaction(); - // Acquire the connection - var count = await (s.Query().CountAsync()); - Assert.That(count, Is.EqualTo(0), "Unexpected initial entity count."); - clone.Complete(); - } - - using (var clone = committable.DependentClone(DependentCloneOption.RollbackIfNotComplete)) - { - sysTran.Transaction.Current = clone; - if (!AutoJoinTransaction) - s.JoinTransaction(); - await (s.SaveAsync(new Person())); - - if (explicitFlush) - await (s.FlushAsync()); - - clone.Complete(); - } - - using (var clone = committable.DependentClone(DependentCloneOption.RollbackIfNotComplete)) - { - sysTran.Transaction.Current = clone; - if (!AutoJoinTransaction) - s.JoinTransaction(); - var count = await (s.Query().CountAsync()); - Assert.That(count, Is.EqualTo(1), "Unexpected entity count after committed insert."); - clone.Complete(); - } - - sysTran.Transaction.Current = committable; - committable.Commit(); - } - } - } - finally - { - sysTran.Transaction.Current = null; - } - - await (DodgeTransactionCompletionDelayIfRequiredAsync()); - - using (var s = OpenSession()) - { - using (var tx = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled)) - { - if (!AutoJoinTransaction) - s.JoinTransaction(); - var count = await (s.Query().CountAsync()); - Assert.That(count, Is.EqualTo(1), "Unexpected entity count after global commit."); - tx.Complete(); - } - } - } - private Task DodgeTransactionCompletionDelayIfRequiredAsync(CancellationToken cancellationToken = default(CancellationToken)) { try diff --git a/src/NHibernate.Test/Async/SystemTransactions/SystemTransactionFixture.cs b/src/NHibernate.Test/Async/SystemTransactions/SystemTransactionFixture.cs index 736af872ba2..ff8f792b6af 100644 --- a/src/NHibernate.Test/Async/SystemTransactions/SystemTransactionFixture.cs +++ b/src/NHibernate.Test/Async/SystemTransactions/SystemTransactionFixture.cs @@ -524,126 +524,6 @@ public async Task EnforceConnectionUsageRulesOnTransactionCompletionAsync() // Currently always forbidden, whatever UseConnectionOnSystemTransactionEvents. Assert.That(interceptor.AfterException, Is.TypeOf()); } - - [Theory] - public async Task CanUseDependentTransactionAsync(bool explicitFlush) - { - if (!TestDialect.SupportsDependentTransaction) - Assert.Ignore("Dialect does not support dependent transactions"); - IgnoreIfUnsupported(explicitFlush); - - try - { - using (var committable = new CommittableTransaction()) - { - sysTran.Transaction.Current = committable; - using (var clone = committable.DependentClone(DependentCloneOption.RollbackIfNotComplete)) - { - sysTran.Transaction.Current = clone; - - using (var s = OpenSession()) - { - if (!AutoJoinTransaction) - s.JoinTransaction(); - await (s.SaveAsync(new Person())); - - if (explicitFlush) - await (s.FlushAsync()); - clone.Complete(); - } - } - - sysTran.Transaction.Current = committable; - committable.Commit(); - } - } - finally - { - sysTran.Transaction.Current = null; - } - } - - [Theory] - public async Task CanUseSessionWithManyDependentTransactionAsync(bool explicitFlush) - { - if (!TestDialect.SupportsDependentTransaction) - Assert.Ignore("Dialect does not support dependent transactions"); - IgnoreIfUnsupported(explicitFlush); - // ODBC with SQL-Server always causes system transactions to go distributed, which causes their transaction completion to run - // asynchronously. But ODBC enlistment also check the previous transaction in a way that do not guard against it - // being concurrently disposed of. See https://github.com/nhibernate/nhibernate-core/pull/1505 for more details. - if (Sfi.ConnectionProvider.Driver is OdbcDriver) - Assert.Ignore("ODBC sometimes fails on second scope by checking the previous transaction status, which may yield an object disposed exception"); - // SAP HANA & SQL Anywhere .Net providers always cause system transactions to be distributed, causing them to - // complete on concurrent threads. This creates race conditions when chaining scopes, the subsequent scope usage - // finding the connection still enlisted in the previous transaction, its complete being still not finished - // on its own thread. - if (Sfi.ConnectionProvider.Driver is HanaDriverBase || Sfi.ConnectionProvider.Driver is SapSQLAnywhere17Driver) - Assert.Ignore("SAP HANA and SQL Anywhere scope handling causes concurrency issues preventing chaining scope usages."); - - try - { - using (var s = WithOptions().ConnectionReleaseMode(ConnectionReleaseMode.OnClose).OpenSession()) - { - using (var committable = new CommittableTransaction()) - { - sysTran.Transaction.Current = committable; - using (var clone = committable.DependentClone(DependentCloneOption.RollbackIfNotComplete)) - { - sysTran.Transaction.Current = clone; - if (!AutoJoinTransaction) - s.JoinTransaction(); - // Acquire the connection - var count = await (s.Query().CountAsync()); - Assert.That(count, Is.EqualTo(0), "Unexpected initial entity count."); - clone.Complete(); - } - - using (var clone = committable.DependentClone(DependentCloneOption.RollbackIfNotComplete)) - { - sysTran.Transaction.Current = clone; - if (!AutoJoinTransaction) - s.JoinTransaction(); - await (s.SaveAsync(new Person())); - - if (explicitFlush) - await (s.FlushAsync()); - - clone.Complete(); - } - - using (var clone = committable.DependentClone(DependentCloneOption.RollbackIfNotComplete)) - { - sysTran.Transaction.Current = clone; - if (!AutoJoinTransaction) - s.JoinTransaction(); - var count = await (s.Query().CountAsync()); - Assert.That(count, Is.EqualTo(1), "Unexpected entity count after committed insert."); - clone.Complete(); - } - - sysTran.Transaction.Current = committable; - committable.Commit(); - } - } - } - finally - { - sysTran.Transaction.Current = null; - } - - using (var s = OpenSession()) - { - using (var tx = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled)) - { - if (!AutoJoinTransaction) - s.JoinTransaction(); - var count = await (s.Query().CountAsync()); - Assert.That(count, Is.EqualTo(1), "Unexpected entity count after global commit."); - tx.Complete(); - } - } - } } [TestFixture] From e19695719c5895d7d9a2f14c0cd7c6d98e1cd643 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Delaporte?= <12201973+fredericdelaporte@users.noreply.github.com> Date: Tue, 3 Sep 2019 22:14:02 +0200 Subject: [PATCH 6/6] fixup! Fix dependent transaction failure Remove sysTran --- .../DistributedSystemTransactionFixture.cs | 8 ++---- .../SystemTransactionFixture.cs | 1 - .../DistributedSystemTransactionFixture.cs | 28 +++++++++---------- .../SystemTransactionFixture.cs | 21 +++++++------- 4 files changed, 26 insertions(+), 32 deletions(-) diff --git a/src/NHibernate.Test/Async/SystemTransactions/DistributedSystemTransactionFixture.cs b/src/NHibernate.Test/Async/SystemTransactions/DistributedSystemTransactionFixture.cs index 242e8272814..7f999d0f1b9 100644 --- a/src/NHibernate.Test/Async/SystemTransactions/DistributedSystemTransactionFixture.cs +++ b/src/NHibernate.Test/Async/SystemTransactions/DistributedSystemTransactionFixture.cs @@ -17,8 +17,6 @@ using NHibernate.Engine; using NHibernate.Test.TransactionTest; using NUnit.Framework; - -using sysTran = System.Transactions; using NHibernate.Linq; namespace NHibernate.Test.SystemTransactions @@ -49,7 +47,7 @@ public async Task SupportsEnlistingInDistributedAsync() Assert.AreNotEqual( Guid.Empty, - sysTran.Transaction.Current.TransactionInformation.DistributedIdentifier, + System.Transactions.Transaction.Current.TransactionInformation.DistributedIdentifier, "Transaction lacks a distributed identifier"); using (var s = OpenSession()) @@ -76,7 +74,7 @@ public async Task SupportsPromotingToDistributedAsync() "Failure promoting the transaction to distributed while already having enlisted a connection."); Assert.AreNotEqual( Guid.Empty, - sysTran.Transaction.Current.TransactionInformation.DistributedIdentifier, + System.Transactions.Transaction.Current.TransactionInformation.DistributedIdentifier, "Transaction lacks a distributed identifier"); } } @@ -717,7 +715,7 @@ public class ForceEscalationToDistributedTx : IEnlistmentNotification public static void Escalate(bool shouldRollBack = false) { var force = new ForceEscalationToDistributedTx(shouldRollBack); - sysTran.Transaction.Current.EnlistDurable(Guid.NewGuid(), force, EnlistmentOptions.None); + System.Transactions.Transaction.Current.EnlistDurable(Guid.NewGuid(), force, EnlistmentOptions.None); } private ForceEscalationToDistributedTx(bool shouldRollBack) diff --git a/src/NHibernate.Test/Async/SystemTransactions/SystemTransactionFixture.cs b/src/NHibernate.Test/Async/SystemTransactions/SystemTransactionFixture.cs index ff8f792b6af..0b93a44b137 100644 --- a/src/NHibernate.Test/Async/SystemTransactions/SystemTransactionFixture.cs +++ b/src/NHibernate.Test/Async/SystemTransactions/SystemTransactionFixture.cs @@ -18,7 +18,6 @@ using NHibernate.Engine; using NHibernate.Test.TransactionTest; using NUnit.Framework; -using sysTran = System.Transactions; using NHibernate.Linq; namespace NHibernate.Test.SystemTransactions diff --git a/src/NHibernate.Test/SystemTransactions/DistributedSystemTransactionFixture.cs b/src/NHibernate.Test/SystemTransactions/DistributedSystemTransactionFixture.cs index 728902ee7f7..b7d931e0809 100644 --- a/src/NHibernate.Test/SystemTransactions/DistributedSystemTransactionFixture.cs +++ b/src/NHibernate.Test/SystemTransactions/DistributedSystemTransactionFixture.cs @@ -8,8 +8,6 @@ using NHibernate.Test.TransactionTest; using NUnit.Framework; -using sysTran = System.Transactions; - namespace NHibernate.Test.SystemTransactions { [TestFixture] @@ -37,7 +35,7 @@ public void SupportsEnlistingInDistributed() Assert.AreNotEqual( Guid.Empty, - sysTran.Transaction.Current.TransactionInformation.DistributedIdentifier, + System.Transactions.Transaction.Current.TransactionInformation.DistributedIdentifier, "Transaction lacks a distributed identifier"); using (var s = OpenSession()) @@ -64,7 +62,7 @@ public void SupportsPromotingToDistributed() "Failure promoting the transaction to distributed while already having enlisted a connection."); Assert.AreNotEqual( Guid.Empty, - sysTran.Transaction.Current.TransactionInformation.DistributedIdentifier, + System.Transactions.Transaction.Current.TransactionInformation.DistributedIdentifier, "Transaction lacks a distributed identifier"); } } @@ -722,10 +720,10 @@ public void CanUseDependentTransaction(bool explicitFlush) { using (var committable = new CommittableTransaction()) { - sysTran.Transaction.Current = committable; + System.Transactions.Transaction.Current = committable; using (var clone = committable.DependentClone(DependentCloneOption.RollbackIfNotComplete)) { - sysTran.Transaction.Current = clone; + System.Transactions.Transaction.Current = clone; using (var s = OpenSession()) { @@ -739,13 +737,13 @@ public void CanUseDependentTransaction(bool explicitFlush) } } - sysTran.Transaction.Current = committable; + System.Transactions.Transaction.Current = committable; committable.Commit(); } } finally { - sysTran.Transaction.Current = null; + System.Transactions.Transaction.Current = null; } } @@ -762,10 +760,10 @@ public void CanUseSessionWithManyDependentTransaction(bool explicitFlush) { using (var committable = new CommittableTransaction()) { - sysTran.Transaction.Current = committable; + System.Transactions.Transaction.Current = committable; using (var clone = committable.DependentClone(DependentCloneOption.RollbackIfNotComplete)) { - sysTran.Transaction.Current = clone; + System.Transactions.Transaction.Current = clone; if (!AutoJoinTransaction) s.JoinTransaction(); // Acquire the connection @@ -776,7 +774,7 @@ public void CanUseSessionWithManyDependentTransaction(bool explicitFlush) using (var clone = committable.DependentClone(DependentCloneOption.RollbackIfNotComplete)) { - sysTran.Transaction.Current = clone; + System.Transactions.Transaction.Current = clone; if (!AutoJoinTransaction) s.JoinTransaction(); s.Save(new Person()); @@ -789,7 +787,7 @@ public void CanUseSessionWithManyDependentTransaction(bool explicitFlush) using (var clone = committable.DependentClone(DependentCloneOption.RollbackIfNotComplete)) { - sysTran.Transaction.Current = clone; + System.Transactions.Transaction.Current = clone; if (!AutoJoinTransaction) s.JoinTransaction(); var count = s.Query().Count(); @@ -797,14 +795,14 @@ public void CanUseSessionWithManyDependentTransaction(bool explicitFlush) clone.Complete(); } - sysTran.Transaction.Current = committable; + System.Transactions.Transaction.Current = committable; committable.Commit(); } } } finally { - sysTran.Transaction.Current = null; + System.Transactions.Transaction.Current = null; } DodgeTransactionCompletionDelayIfRequired(); @@ -836,7 +834,7 @@ public class ForceEscalationToDistributedTx : IEnlistmentNotification public static void Escalate(bool shouldRollBack = false) { var force = new ForceEscalationToDistributedTx(shouldRollBack); - sysTran.Transaction.Current.EnlistDurable(Guid.NewGuid(), force, EnlistmentOptions.None); + System.Transactions.Transaction.Current.EnlistDurable(Guid.NewGuid(), force, EnlistmentOptions.None); } private ForceEscalationToDistributedTx(bool shouldRollBack) diff --git a/src/NHibernate.Test/SystemTransactions/SystemTransactionFixture.cs b/src/NHibernate.Test/SystemTransactions/SystemTransactionFixture.cs index ddc3726531f..35b062b5f27 100644 --- a/src/NHibernate.Test/SystemTransactions/SystemTransactionFixture.cs +++ b/src/NHibernate.Test/SystemTransactions/SystemTransactionFixture.cs @@ -8,7 +8,6 @@ using NHibernate.Engine; using NHibernate.Test.TransactionTest; using NUnit.Framework; -using sysTran = System.Transactions; namespace NHibernate.Test.SystemTransactions { @@ -534,10 +533,10 @@ public void CanUseDependentTransaction(bool explicitFlush) { using (var committable = new CommittableTransaction()) { - sysTran.Transaction.Current = committable; + System.Transactions.Transaction.Current = committable; using (var clone = committable.DependentClone(DependentCloneOption.RollbackIfNotComplete)) { - sysTran.Transaction.Current = clone; + System.Transactions.Transaction.Current = clone; using (var s = OpenSession()) { @@ -551,13 +550,13 @@ public void CanUseDependentTransaction(bool explicitFlush) } } - sysTran.Transaction.Current = committable; + System.Transactions.Transaction.Current = committable; committable.Commit(); } } finally { - sysTran.Transaction.Current = null; + System.Transactions.Transaction.Current = null; } } @@ -585,10 +584,10 @@ public void CanUseSessionWithManyDependentTransaction(bool explicitFlush) { using (var committable = new CommittableTransaction()) { - sysTran.Transaction.Current = committable; + System.Transactions.Transaction.Current = committable; using (var clone = committable.DependentClone(DependentCloneOption.RollbackIfNotComplete)) { - sysTran.Transaction.Current = clone; + System.Transactions.Transaction.Current = clone; if (!AutoJoinTransaction) s.JoinTransaction(); // Acquire the connection @@ -599,7 +598,7 @@ public void CanUseSessionWithManyDependentTransaction(bool explicitFlush) using (var clone = committable.DependentClone(DependentCloneOption.RollbackIfNotComplete)) { - sysTran.Transaction.Current = clone; + System.Transactions.Transaction.Current = clone; if (!AutoJoinTransaction) s.JoinTransaction(); s.Save(new Person()); @@ -612,7 +611,7 @@ public void CanUseSessionWithManyDependentTransaction(bool explicitFlush) using (var clone = committable.DependentClone(DependentCloneOption.RollbackIfNotComplete)) { - sysTran.Transaction.Current = clone; + System.Transactions.Transaction.Current = clone; if (!AutoJoinTransaction) s.JoinTransaction(); var count = s.Query().Count(); @@ -620,14 +619,14 @@ public void CanUseSessionWithManyDependentTransaction(bool explicitFlush) clone.Complete(); } - sysTran.Transaction.Current = committable; + System.Transactions.Transaction.Current = committable; committable.Commit(); } } } finally { - sysTran.Transaction.Current = null; + System.Transactions.Transaction.Current = null; } using (var s = OpenSession())