This is an automated email from the ASF dual-hosted git repository.

ptupitsyn pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/ignite-3.git


The following commit(s) were added to refs/heads/main by this push:
     new 5d05ac362a IGNITE-18513 .NET: Ignore commit/rollback on completed tx 
(#1769)
5d05ac362a is described below

commit 5d05ac362a746f996b19f36e8ee108febbf338b5
Author: Pavel Tupitsyn <ptupit...@apache.org>
AuthorDate: Thu Mar 9 21:04:15 2023 +0300

    IGNITE-18513 .NET: Ignore commit/rollback on completed tx (#1769)
    
    * Do not throw exception on `Commit` & `Rollback` when transaction is 
already completed (committed or rolled back).
    * Add `TroString` override.
---
 .../Transactions/TransactionsTests.cs              | 36 ++++++++---
 .../Internal/Transactions/Transaction.cs           | 71 ++++++++--------------
 2 files changed, 53 insertions(+), 54 deletions(-)

diff --git 
a/modules/platforms/dotnet/Apache.Ignite.Tests/Transactions/TransactionsTests.cs
 
b/modules/platforms/dotnet/Apache.Ignite.Tests/Transactions/TransactionsTests.cs
index c7a0c3a699..ac85e20f77 100644
--- 
a/modules/platforms/dotnet/Apache.Ignite.Tests/Transactions/TransactionsTests.cs
+++ 
b/modules/platforms/dotnet/Apache.Ignite.Tests/Transactions/TransactionsTests.cs
@@ -19,6 +19,7 @@ namespace Apache.Ignite.Tests.Transactions
 {
     using System;
     using System.Linq;
+    using System.Text.RegularExpressions;
     using System.Threading.Tasks;
     using System.Transactions;
     using Ignite.Transactions;
@@ -119,23 +120,23 @@ namespace Apache.Ignite.Tests.Transactions
         }
 
         [Test]
-        public async Task TestCommitRollbackSameTxThrows()
+        public async Task TestCommitAfterRollbackIsIgnored()
         {
             await using var tx = await Client.Transactions.BeginAsync();
             await tx.CommitAsync();
+            await tx.RollbackAsync();
 
-            var ex = Assert.ThrowsAsync<TransactionException>(async () => 
await tx.RollbackAsync());
-            Assert.AreEqual("Transaction is already committed.", ex?.Message);
+            StringAssert.Contains("State = Committed", tx.ToString());
         }
 
         [Test]
-        public async Task TestRollbackCommitSameTxThrows()
+        public async Task TestRollbackAfterCommitIsIgnored()
         {
             await using var tx = await Client.Transactions.BeginAsync();
             await tx.RollbackAsync();
+            await tx.CommitAsync();
 
-            var ex = Assert.ThrowsAsync<TransactionException>(async () => 
await tx.CommitAsync());
-            Assert.AreEqual("Transaction is already rolled back.", 
ex?.Message);
+            StringAssert.Contains("State = RolledBack", tx.ToString());
         }
 
         [Test]
@@ -145,9 +146,9 @@ namespace Apache.Ignite.Tests.Transactions
 
             await tx.DisposeAsync();
             await tx.DisposeAsync();
+            await tx.CommitAsync();
 
-            var ex = Assert.ThrowsAsync<TransactionException>(async () => 
await tx.CommitAsync());
-            Assert.AreEqual("Transaction is already rolled back.", 
ex?.Message);
+            StringAssert.Contains("State = RolledBack", tx.ToString());
         }
 
         [Test]
@@ -250,6 +251,7 @@ namespace Apache.Ignite.Tests.Transactions
             await using var tx = await Client.Transactions.BeginAsync(new 
TransactionOptions { ReadOnly = true });
 
             Assert.IsTrue(tx.IsReadOnly);
+            StringAssert.Contains("State = Open, IsReadOnly = True", 
tx.ToString());
         }
 
         [Test]
@@ -258,6 +260,24 @@ namespace Apache.Ignite.Tests.Transactions
             await using var tx = await Client.Transactions.BeginAsync();
 
             Assert.IsFalse(tx.IsReadOnly);
+            StringAssert.Contains("State = Open, IsReadOnly = False", 
tx.ToString());
+        }
+
+        [Test]
+        public async Task TestToString()
+        {
+            await using var tx1 = await Client.Transactions.BeginAsync();
+            await using var tx2 = await 
Client.Transactions.BeginAsync(new(ReadOnly: true));
+            await using var tx3 = await Client.Transactions.BeginAsync();
+
+            await tx2.RollbackAsync();
+            await tx3.CommitAsync();
+
+            var id = int.Parse(Regex.Match(tx1.ToString()!, @"\d+").Value);
+
+            Assert.AreEqual($"Transaction {{ Id = {id}, State = Open, 
IsReadOnly = False }}", tx1.ToString());
+            Assert.AreEqual($"Transaction {{ Id = {id + 1}, State = 
RolledBack, IsReadOnly = True }}", tx2.ToString());
+            Assert.AreEqual($"Transaction {{ Id = {id + 2}, State = Committed, 
IsReadOnly = False }}", tx3.ToString());
         }
 
         private class CustomTx : ITransaction
diff --git 
a/modules/platforms/dotnet/Apache.Ignite/Internal/Transactions/Transaction.cs 
b/modules/platforms/dotnet/Apache.Ignite/Internal/Transactions/Transaction.cs
index 312d149392..1a9676002f 100644
--- 
a/modules/platforms/dotnet/Apache.Ignite/Internal/Transactions/Transaction.cs
+++ 
b/modules/platforms/dotnet/Apache.Ignite/Internal/Transactions/Transaction.cs
@@ -19,7 +19,6 @@ namespace Apache.Ignite.Internal.Transactions
 {
     using System.Threading;
     using System.Threading.Tasks;
-    using System.Transactions;
     using Ignite.Transactions;
     using Proto;
     using Proto.MsgPack;
@@ -77,41 +76,46 @@ namespace Apache.Ignite.Internal.Transactions
         /// <inheritdoc/>
         public async Task CommitAsync()
         {
-            SetState(StateCommitted);
-
-            using var writer = ProtoCommon.GetMessageWriter();
-            Write(writer.MessageWriter);
+            if (TrySetState(StateCommitted))
+            {
+                using var writer = ProtoCommon.GetMessageWriter();
+                Write(writer.MessageWriter);
 
-            await Socket.DoOutInOpAsync(ClientOp.TxCommit, 
writer).ConfigureAwait(false);
+                await Socket.DoOutInOpAsync(ClientOp.TxCommit, 
writer).ConfigureAwait(false);
+            }
         }
 
         /// <inheritdoc/>
-        public async Task RollbackAsync()
-        {
-            SetState(StateRolledBack);
+        public async Task RollbackAsync() => await 
RollbackAsyncInternal().ConfigureAwait(false);
 
-            await RollbackAsyncInternal().ConfigureAwait(false);
-        }
+        /// <inheritdoc/>
+        public async ValueTask DisposeAsync() => await 
RollbackAsyncInternal().ConfigureAwait(false);
 
         /// <inheritdoc/>
-        public async ValueTask DisposeAsync()
+        public override string ToString()
         {
-            // Roll back if the transaction is still open, otherwise do 
nothing.
-            if (TrySetState(StateRolledBack))
+            var state = _state switch
             {
-                await RollbackAsyncInternal().ConfigureAwait(false);
-            }
+                StateOpen => "Open",
+                StateCommitted => "Committed",
+                _ => "RolledBack"
+            };
+
+            return $"Transaction {{ Id = {Id}, State = {state}, IsReadOnly = 
{IsReadOnly} }}";
         }
 
         /// <summary>
         /// Rolls back the transaction without state check.
         /// </summary>
-        private async Task RollbackAsyncInternal()
+        private async ValueTask RollbackAsyncInternal()
         {
-            using var writer = ProtoCommon.GetMessageWriter();
-            Write(writer.MessageWriter);
+            if (TrySetState(StateRolledBack))
+            {
+                using var writer = ProtoCommon.GetMessageWriter();
+                Write(writer.MessageWriter);
 
-            await Socket.DoOutInOpAsync(ClientOp.TxRollback, 
writer).ConfigureAwait(false);
+                await Socket.DoOutInOpAsync(ClientOp.TxRollback, 
writer).ConfigureAwait(false);
+            }
         }
 
         /// <summary>
@@ -119,32 +123,7 @@ namespace Apache.Ignite.Internal.Transactions
         /// </summary>
         /// <param name="state">State to set.</param>
         /// <returns>True when specified state was set successfully; false 
otherwise.</returns>
-        private bool TrySetState(int state)
-        {
-            return Interlocked.CompareExchange(ref _state, state, StateOpen) 
== StateOpen;
-        }
-
-        /// <summary>
-        /// Sets the specified state. Throws <see 
cref="TransactionException"/> when current state is different from
-        /// <see cref="StateOpen"/>.
-        /// </summary>
-        /// <param name="state">State to set.</param>
-        /// <exception cref="TransactionException">When current state is not 
<see cref="StateOpen"/>.</exception>
-        private void SetState(int state)
-        {
-            var oldState = Interlocked.CompareExchange(ref _state, state, 
StateOpen);
-
-            if (oldState == StateOpen)
-            {
-                return;
-            }
-
-            var message = oldState == StateCommitted
-                ? "Transaction is already committed."
-                : "Transaction is already rolled back.";
-
-            throw new TransactionException(message);
-        }
+        private bool TrySetState(int state) => Interlocked.CompareExchange(ref 
_state, state, StateOpen) == StateOpen;
 
         /// <summary>
         /// Writes the transaction.

Reply via email to