Пример #1
0
        public void Distributed_rollback()
        {
            var disposedCalled = false;
            var tx             = new TransactionScope();

            try
            {
                using (var conn1 = OpenConnection(ConnectionStringEnlistOn))
                {
                    Assert.That(conn1.ExecuteNonQuery(@"INSERT INTO data (name) VALUES ('test1')"), Is.EqualTo(1), "Unexpected first insert rowcount");

                    EnlistResource.EscalateToDistributed(true);
                    AssertHasDistributedIdentifier();
                    tx.Complete();
                }
                disposedCalled = true;
                Assert.That(() => tx.Dispose(), Throws.TypeOf <TransactionAbortedException>());
                // TODO: There may be a race condition here, where the prepared transaction above still hasn't completed.
                AssertNoDistributedIdentifier();
                AssertNoPreparedTransactions();
                AssertNumberOfRows(0);
            }
            finally
            {
                if (!disposedCalled)
                {
                    tx.Dispose();
                }
            }
        }
 public void NonDistributedInDoubt()
 {
     try
     {
         using (var scope = new TransactionScope())
         {
             _log.InfoFormat(
                 "Scope opened, id {0}, distributed id {1}",
                 SysTran.Current.TransactionInformation.LocalIdentifier,
                 SysTran.Current.TransactionInformation.DistributedIdentifier);
             // Simulate a simple connection: durable resource supporting single phase.
             // (Note that SQL Server 2005 and above use IPromotableSinglePhaseNotification
             // for delegating the resource management to the SQL server.)
             EnlistResource.EnlistInDoubtDurable();
             _log.InfoFormat(
                 "Fake connection opened, scope id {0} and distributed id {1}",
                 SysTran.Current.TransactionInformation.LocalIdentifier,
                 SysTran.Current.TransactionInformation.DistributedIdentifier);
             // Simulate NHibernate : volatile no single phase support + enlist in prepare option
             EnlistResource.EnlistWithPrepareEnlistmentVolatile();
             _log.InfoFormat(
                 "Fake session opened, scope id {0} and distributed id {1}",
                 SysTran.Current.TransactionInformation.LocalIdentifier,
                 SysTran.Current.TransactionInformation.DistributedIdentifier);
             scope.Complete();
             _log.Info("Scope completed");
         }
     }
     catch (TransactionInDoubtException)
     {
         // expected
     }
     _log.Info("Scope disposed");
 }
 public void NonDistributedNpgsqlCommit([Values(false, true)] bool enlistInPrepare)
 {
     using (var scope = new TransactionScope())
     {
         _log.InfoFormat(
             "Scope opened, id {0}, distributed id {1}",
             SysTran.Current.TransactionInformation.LocalIdentifier,
             SysTran.Current.TransactionInformation.DistributedIdentifier);
         // Simulate a Npgsql connection: as of Npgsql 3.2.4, volatile resource with single phase support
         EnlistResource.EnlistVolatile(false, true);
         _log.InfoFormat(
             "Fake connection opened, scope id {0} and distributed id {1}",
             SysTran.Current.TransactionInformation.LocalIdentifier,
             SysTran.Current.TransactionInformation.DistributedIdentifier);
         // Simulate NHibernate : volatile no single phase support
         if (enlistInPrepare)
         {
             EnlistResource.EnlistWithPrepareEnlistmentVolatile();
         }
         else
         {
             EnlistResource.EnlistVolatile();
         }
         _log.InfoFormat(
             "Fake session opened, scope id {0} and distributed id {1}",
             SysTran.Current.TransactionInformation.LocalIdentifier,
             SysTran.Current.TransactionInformation.DistributedIdentifier);
         scope.Complete();
         _log.Info("Scope completed");
     }
     _log.Info("Scope disposed");
 }
 public void NonDistributedCommit([Values(false, true)] bool enlistInPrepare)
 {
     using (var scope = new TransactionScope())
     {
         _log.InfoFormat(
             "Scope opened, id {0}, distributed id {1}",
             SysTran.Current.TransactionInformation.LocalIdentifier,
             SysTran.Current.TransactionInformation.DistributedIdentifier);
         // Simulate a simple connection: durable resource supporting single phase.
         // (Note that SQL Server 2005 and above use IPromotableSinglePhaseNotification
         // for delegating the resource management to the SQL server.)
         EnlistResource.EnlistDurable(false, true);
         _log.InfoFormat(
             "Fake connection opened, scope id {0} and distributed id {1}",
             SysTran.Current.TransactionInformation.LocalIdentifier,
             SysTran.Current.TransactionInformation.DistributedIdentifier);
         // Simulate NHibernate : volatile no single phase support
         if (enlistInPrepare)
         {
             EnlistResource.EnlistWithPrepareEnlistmentVolatile();
         }
         else
         {
             EnlistResource.EnlistVolatile();
         }
         _log.InfoFormat(
             "Fake session opened, scope id {0} and distributed id {1}",
             SysTran.Current.TransactionInformation.LocalIdentifier,
             SysTran.Current.TransactionInformation.DistributedIdentifier);
         scope.Complete();
         _log.Info("Scope completed");
     }
     _log.Info("Scope disposed");
 }
 public void DistributedFailureInSecondPhase()
 {
     using (var scope = new TransactionScope(TransactionScopeOption.Required, TimeSpan.FromSeconds(5)))
     {
         _log.InfoFormat(
             "Scope opened, id {0}, distributed id {1}",
             SysTran.Current.TransactionInformation.LocalIdentifier,
             SysTran.Current.TransactionInformation.DistributedIdentifier);
         // Simulate a failing connection
         EnlistResource.EnlistSecondPhaseFailingDurable();
         _log.InfoFormat(
             "Fake connection opened, scope id {0} and distributed id {1}",
             SysTran.Current.TransactionInformation.LocalIdentifier,
             SysTran.Current.TransactionInformation.DistributedIdentifier);
         // Simulate another resource, not even supporting single phase
         EnlistResource.EnlistDurable();
         _log.InfoFormat(
             "Fake other resource, scope id {0} and distributed id {1}",
             SysTran.Current.TransactionInformation.LocalIdentifier,
             SysTran.Current.TransactionInformation.DistributedIdentifier);
         // Simulate NHibernate : volatile no single phase support + enlist in prepare option
         EnlistResource.EnlistWithPrepareEnlistmentVolatile();
         _log.InfoFormat(
             "Fake session opened, scope id {0} and distributed id {1}",
             SysTran.Current.TransactionInformation.LocalIdentifier,
             SysTran.Current.TransactionInformation.DistributedIdentifier);
         scope.Complete();
         _log.Info("Scope completed");
     }
     _log.Info("Scope disposed");
 }
        public void DistributedRollback([Values(false, true)] bool fromConnection, [Values(false, true)] bool fromOther, [Values(false, true)] bool fromSession)
        {
            var shouldFail = fromConnection || fromSession || fromOther;

            try
            {
                using (var scope = new TransactionScope())
                {
                    _log.InfoFormat(
                        "Scope opened, id {0}, distributed id {1}",
                        SysTran.Current.TransactionInformation.LocalIdentifier,
                        SysTran.Current.TransactionInformation.DistributedIdentifier);
                    // Simulate a simple connection: durable resource supporting single phase.
                    // (Note that SQL Server 2005 and above use IPromotableSinglePhaseNotification
                    // for delegating the resource management to the SQL server.)
                    EnlistResource.EnlistDurable(fromConnection, true);
                    _log.InfoFormat(
                        "Fake connection opened, scope id {0} and distributed id {1}",
                        SysTran.Current.TransactionInformation.LocalIdentifier,
                        SysTran.Current.TransactionInformation.DistributedIdentifier);
                    // Simulate another resource, not even supporting single phase
                    EnlistResource.EnlistDurable(fromOther);
                    _log.InfoFormat(
                        "Fake other resource, scope id {0} and distributed id {1}",
                        SysTran.Current.TransactionInformation.LocalIdentifier,
                        SysTran.Current.TransactionInformation.DistributedIdentifier);
                    // Simulate NHibernate : volatile no single phase support + enlist in prepare option
                    EnlistResource.EnlistWithPrepareEnlistmentVolatile(fromSession);
                    _log.InfoFormat(
                        "Fake session opened, scope id {0} and distributed id {1}",
                        SysTran.Current.TransactionInformation.LocalIdentifier,
                        SysTran.Current.TransactionInformation.DistributedIdentifier);
                    if (shouldFail)
                    {
                        scope.Complete();
                        _log.Info("Scope completed");
                    }
                    else
                    {
                        _log.Info("Scope not completed for triggering rollback");
                    }
                }
            }
            catch (TransactionAbortedException)
            {
                if (!shouldFail)
                {
                    throw;
                }
            }
            _log.Info("Scope disposed");
        }
        public void DistributedNpgsqlRollback([Values(false, true)] bool fromConnection, [Values(false, true)] bool fromOther, [Values(false, true)] bool fromSession)
        {
            var shouldFail = fromConnection || fromSession || fromOther;

            try
            {
                using (var scope = new TransactionScope())
                {
                    _log.InfoFormat(
                        "Scope opened, id {0}, distributed id {1}",
                        SysTran.Current.TransactionInformation.LocalIdentifier,
                        SysTran.Current.TransactionInformation.DistributedIdentifier);
                    // Simulate a Npgsql connection: as of Npgsql 3.2.4, volatile resource with single phase support
                    EnlistResource.EnlistVolatile(fromConnection, true);
                    _log.InfoFormat(
                        "Fake connection opened, scope id {0} and distributed id {1}",
                        SysTran.Current.TransactionInformation.LocalIdentifier,
                        SysTran.Current.TransactionInformation.DistributedIdentifier);
                    // Simulate another resource, not even supporting single phase (required for going distributed with "Npgsql")
                    EnlistResource.EnlistDurable(fromOther);
                    _log.InfoFormat(
                        "Fake other resource, scope id {0} and distributed id {1}",
                        SysTran.Current.TransactionInformation.LocalIdentifier,
                        SysTran.Current.TransactionInformation.DistributedIdentifier);
                    // Simulate NHibernate : volatile no single phase support + enlist in prepare option
                    EnlistResource.EnlistWithPrepareEnlistmentVolatile(fromSession);
                    _log.InfoFormat(
                        "Fake session opened, scope id {0} and distributed id {1}",
                        SysTran.Current.TransactionInformation.LocalIdentifier,
                        SysTran.Current.TransactionInformation.DistributedIdentifier);
                    if (shouldFail)
                    {
                        scope.Complete();
                        _log.Info("Scope completed");
                    }
                    else
                    {
                        _log.Info("Scope not completed for triggering rollback");
                    }
                }
            }
            catch (TransactionAbortedException)
            {
                if (!shouldFail)
                {
                    throw;
                }
            }
            _log.Info("Scope disposed");
        }
Пример #8
0
        public void Connection_reuse_race_after_rollback([Values(false, true)] bool distributed)
        {
            for (var i = 1; i <= 100; i++)
            {
                var eventQueue = new ConcurrentQueue <TransactionEvent>();
                try
                {
                    using (var conn1 = OpenConnection(ConnectionStringEnlistOff))
                    {
                        using (new TransactionScope())
                        {
                            conn1.EnlistTransaction(Transaction.Current);
                            eventQueue.Enqueue(new TransactionEvent("Scope started, connection enlisted"));

                            if (distributed)
                            {
                                EnlistResource.EscalateToDistributed(eventQueue);
                                AssertHasDistributedIdentifier();
                            }
                            else
                            {
                                EnlistResource.EnlistVolatile(eventQueue);
                                AssertNoDistributedIdentifier();
                            }

                            Assert.That(conn1.ExecuteNonQuery(@"INSERT INTO data (name) VALUES ('test1')"), Is.EqualTo(1), "Unexpected first insert rowcount");
                            eventQueue.Enqueue(new TransactionEvent("Insert done"));

                            eventQueue.Enqueue(new TransactionEvent("Scope not completed"));
                        }
                        eventQueue.Enqueue(new TransactionEvent("Scope disposed"));
                        conn1.EnlistTransaction(null);
                        eventQueue.Enqueue(new TransactionEvent("Connection enlisted with null"));
                        Assert.DoesNotThrow(() => conn1.ExecuteScalar(@"SELECT COUNT(*) FROM data"));
                    }
                }
                catch (Exception ex)
                {
                    Assert.Fail(
                        @"Failed at iteration {0}.
Events:
{1}
Exception {2}",
                        i, FormatEventQueue(eventQueue), ex);
                }
            }
        }
Пример #9
0
            static void Enlist(bool durable, bool shouldRollBack, ConcurrentQueue <TransactionEvent>?eventQueue)
            {
                Counter++;

                var name     = $"{(durable ? "Durable" : "Volatile")} resource {Counter}";
                var resource = new EnlistResource(shouldRollBack, name, eventQueue);

                if (durable)
                {
                    Transaction.Current !.EnlistDurable(Guid.NewGuid(), resource, EnlistmentOptions.None);
                }
                else
                {
                    Transaction.Current !.EnlistVolatile(resource, EnlistmentOptions.None);
                }

                Transaction.Current.TransactionCompleted += resource.Current_TransactionCompleted !;

                eventQueue?.Enqueue(new TransactionEvent(name + ": enlisted"));
            }
            private static void Enlist(bool durable, bool supportsSinglePhase, bool shouldRollBack, bool inDoubt = false,
                                       bool failInSecondPhase = false, bool enlistInPrepareOption = false)
            {
                Counter++;

                var            name = $"{(durable ? "Durable" : "Volatile")} resource {Counter}";
                EnlistResource resource;
                var            options = enlistInPrepareOption ? EnlistmentOptions.EnlistDuringPrepareRequired : EnlistmentOptions.None;

                if (supportsSinglePhase)
                {
                    var spResource = new EnlistSinglePhaseResource(shouldRollBack, name, inDoubt, failInSecondPhase);
                    resource = spResource;
                    if (durable)
                    {
                        SysTran.Current.EnlistDurable(Guid.NewGuid(), spResource, options);
                    }
                    else
                    {
                        SysTran.Current.EnlistVolatile(spResource, options);
                    }
                }
                else
                {
                    resource = new EnlistResource(shouldRollBack, name, inDoubt, failInSecondPhase);
                    // Not duplicate code with above, that is not the same overload which ends up called.
                    if (durable)
                    {
                        SysTran.Current.EnlistDurable(Guid.NewGuid(), resource, options);
                    }
                    else
                    {
                        SysTran.Current.EnlistVolatile(resource, options);
                    }
                }

                SysTran.Current.TransactionCompleted += resource.Current_TransactionCompleted;

                _log.Info(name + ": enlisted");
            }
Пример #11
0
        public void Transaction_race([Values(false, true)] bool distributed)
        {
            for (var i = 1; i <= 100; i++)
            {
                var eventQueue = new ConcurrentQueue <TransactionEvent>();
                try
                {
                    using (var tx = new TransactionScope())
                        using (var conn1 = OpenConnection(ConnectionStringEnlistOn))
                        {
                            eventQueue.Enqueue(new TransactionEvent("Scope started, connection enlisted"));
                            Assert.That(conn1.ExecuteNonQuery(@"INSERT INTO data (name) VALUES ('test1')"), Is.EqualTo(1), "Unexpected first insert rowcount");
                            eventQueue.Enqueue(new TransactionEvent("Insert done"));

                            if (distributed)
                            {
                                EnlistResource.EscalateToDistributed(eventQueue);
                                AssertHasDistributedIdentifier();
                            }
                            else
                            {
                                EnlistResource.EnlistVolatile(eventQueue);
                                AssertNoDistributedIdentifier();
                            }

                            tx.Complete();
                            eventQueue.Enqueue(new TransactionEvent("Scope completed"));
                        }
                    eventQueue.Enqueue(new TransactionEvent("Scope disposed"));
                    AssertNoDistributedIdentifier();
                    if (distributed)
                    {
                        // There may be a race condition here, where the prepared transaction above still hasn't completed.
                        // This is by design of MS DTC. Giving it up to 100ms to complete. If it proves flaky, raise
                        // maxLoop.
                        const int maxLoop = 20;
                        for (var j = 0; j < maxLoop; j++)
                        {
                            Thread.Sleep(10);
                            try
                            {
                                AssertNumberOfRows(i);
                                break;
                            }
                            catch
                            {
                                if (j == maxLoop - 1)
                                {
                                    throw;
                                }
                            }
                        }
                    }
                    else
                    {
                        AssertNumberOfRows(i);
                    }
                }
                catch (Exception ex)
                {
                    Assert.Fail(
                        @"Failed at iteration {0}.
Events:
{1}
Exception {2}",
                        i, FormatEventQueue(eventQueue), ex);
                }
            }
        }
Пример #12
0
        public void Connection_reuse_race_chaining_transaction([Values(false, true)] bool distributed)
        {
            for (var i = 1; i <= 100; i++)
            {
                var eventQueue = new ConcurrentQueue <TransactionEvent>();
                try
                {
                    using (var conn1 = OpenConnection(ConnectionStringEnlistOff))
                    {
                        using (var scope = new TransactionScope())
                        {
                            eventQueue.Enqueue(new TransactionEvent("First scope started"));
                            conn1.EnlistTransaction(Transaction.Current);
                            eventQueue.Enqueue(new TransactionEvent("First scope, connection enlisted"));

                            if (distributed)
                            {
                                EnlistResource.EscalateToDistributed(eventQueue);
                                AssertHasDistributedIdentifier();
                            }
                            else
                            {
                                EnlistResource.EnlistVolatile(eventQueue);
                                AssertNoDistributedIdentifier();
                            }

                            Assert.That(conn1.ExecuteNonQuery(@"INSERT INTO data (name) VALUES ('test1')"), Is.EqualTo(1), "Unexpected first insert rowcount");
                            eventQueue.Enqueue(new TransactionEvent("First insert done"));

                            scope.Complete();
                            eventQueue.Enqueue(new TransactionEvent("First scope completed"));
                        }
                        eventQueue.Enqueue(new TransactionEvent("First scope disposed"));

                        using (var scope = new TransactionScope())
                        {
                            eventQueue.Enqueue(new TransactionEvent("Second scope started"));
                            conn1.EnlistTransaction(Transaction.Current);
                            eventQueue.Enqueue(new TransactionEvent("Second scope, connection enlisted"));

                            if (distributed)
                            {
                                EnlistResource.EscalateToDistributed(eventQueue);
                                AssertHasDistributedIdentifier();
                            }
                            else
                            {
                                EnlistResource.EnlistVolatile(eventQueue);
                                AssertNoDistributedIdentifier();
                            }

                            Assert.That(conn1.ExecuteNonQuery(@"INSERT INTO data (name) VALUES ('test1')"), Is.EqualTo(1), "Unexpected second insert rowcount");
                            eventQueue.Enqueue(new TransactionEvent("Second insert done"));

                            scope.Complete();
                            eventQueue.Enqueue(new TransactionEvent("Second scope completed"));
                        }
                        eventQueue.Enqueue(new TransactionEvent("Second scope disposed"));
                    }
                }
                catch (Exception ex)
                {
                    Assert.Fail(
                        @"Failed at iteration {0}.
Events:
{1}
Exception {2}",
                        i, FormatEventQueue(eventQueue), ex);
                }
            }
        }