You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by pt...@apache.org on 2023/02/06 08:53:37 UTC
[ignite-3] branch main updated: IGNITE-18696 .NET: Add read-only transactions (#1634)
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 fd16bed21c IGNITE-18696 .NET: Add read-only transactions (#1634)
fd16bed21c is described below
commit fd16bed21ccf99df3a187c0779942fb6b027cf7c
Author: Pavel Tupitsyn <pt...@apache.org>
AuthorDate: Mon Feb 6 10:53:31 2023 +0200
IGNITE-18696 .NET: Add read-only transactions (#1634)
---
.../org/apache/ignite/tx/IgniteTransactions.java | 3 ++
.../dotnet/Apache.Ignite.Tests/.editorconfig | 2 +-
.../Transactions/TransactionsTests.cs | 58 ++++++++++++++++++++++
.../Internal/Transactions/Transactions.cs | 6 +--
.../Apache.Ignite/Transactions/ITransaction.cs | 2 +-
.../Apache.Ignite/Transactions/ITransactions.cs | 11 +++-
.../{ITransactions.cs => TransactionOptions.cs} | 25 ++++------
7 files changed, 84 insertions(+), 23 deletions(-)
diff --git a/modules/api/src/main/java/org/apache/ignite/tx/IgniteTransactions.java b/modules/api/src/main/java/org/apache/ignite/tx/IgniteTransactions.java
index f4b90ea139..d2ba3b74fc 100644
--- a/modules/api/src/main/java/org/apache/ignite/tx/IgniteTransactions.java
+++ b/modules/api/src/main/java/org/apache/ignite/tx/IgniteTransactions.java
@@ -73,6 +73,9 @@ public interface IgniteTransactions {
/**
* Returns a decorated {@code IgniteTransactions} instance that will start read-only transactions.
*
+ * <p>Read-only transactions provide a snapshot view of data at a certain point in time.
+ * They are lock-free and perform better than normal transactions, but do not permit data modifications.
+ *
* @return Decorated {@code IgniteTransactions} instance that will start read-only transactions.
*/
IgniteTransactions readOnly();
diff --git a/modules/platforms/dotnet/Apache.Ignite.Tests/.editorconfig b/modules/platforms/dotnet/Apache.Ignite.Tests/.editorconfig
index 5cfad90769..3a49c19db7 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Tests/.editorconfig
+++ b/modules/platforms/dotnet/Apache.Ignite.Tests/.editorconfig
@@ -40,4 +40,4 @@ dotnet_diagnostic.CA1508.severity = none # Avoid dead conditional code
dotnet_diagnostic.CA1305.severity = none # Specify IFormatProvider
dotnet_diagnostic.CA1819.severity = none # Properties should not return arrays
dotnet_diagnostic.CA1812.severity = none # Avoid uninstantiated internal classes
-
+dotnet_diagnostic.CA5394.severity = none # Use secure random
diff --git a/modules/platforms/dotnet/Apache.Ignite.Tests/Transactions/TransactionsTests.cs b/modules/platforms/dotnet/Apache.Ignite.Tests/Transactions/TransactionsTests.cs
index 937ab937f5..694af4d5f5 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Tests/Transactions/TransactionsTests.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Tests/Transactions/TransactionsTests.cs
@@ -17,11 +17,15 @@
namespace Apache.Ignite.Tests.Transactions
{
+ using System;
+ using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Threading.Tasks;
using System.Transactions;
using Ignite.Transactions;
using NUnit.Framework;
+ using Table;
+ using TransactionOptions = Ignite.Transactions.TransactionOptions;
/// <summary>
/// Tests for <see cref="ITransactions"/> and <see cref="ITransaction"/>.
@@ -187,6 +191,60 @@ namespace Apache.Ignite.Tests.Transactions
Assert.AreEqual("Specified transaction belongs to a different IgniteClient instance.", ex!.Message);
}
+ [Test]
+ public async Task TestReadOnlyTxSeesOldDataAfterUpdate()
+ {
+ var key = Random.Shared.NextInt64(1000, long.MaxValue);
+ var keyPoco = new Poco { Key = key };
+
+ await PocoView.UpsertAsync(null, new Poco { Key = key, Val = "11" });
+
+ await using var tx = await Client.Transactions.BeginAsync(new TransactionOptions { ReadOnly = true });
+ Assert.AreEqual("11", (await PocoView.GetAsync(tx, keyPoco)).Value.Val);
+
+ // Update data in a different tx.
+ await using (var tx2 = await Client.Transactions.BeginAsync())
+ {
+ await PocoView.UpsertAsync(null, new Poco { Key = key, Val = "22" });
+ await tx2.CommitAsync();
+ }
+
+ // Old tx sees old data.
+ Assert.AreEqual("11", (await PocoView.GetAsync(tx, keyPoco)).Value.Val);
+
+ // New tx sees new data
+ await using var tx3 = await Client.Transactions.BeginAsync(new TransactionOptions { ReadOnly = true });
+ Assert.AreEqual("22", (await PocoView.GetAsync(tx3, keyPoco)).Value.Val);
+ }
+
+ [Test]
+ public async Task TestUpdateInReadOnlyTxThrows()
+ {
+ await using var tx = await Client.Transactions.BeginAsync(new TransactionOptions { ReadOnly = true });
+ var ex = Assert.ThrowsAsync<Tx.TransactionException>(async () => await TupleView.UpsertAsync(tx, GetTuple(1, "1")));
+
+ Assert.AreEqual(ErrorGroups.Transactions.TxFailedReadWriteOperation, ex!.Code, ex.Message);
+ StringAssert.Contains("Failed to enlist read-write operation into read-only transaction", ex.Message);
+ }
+
+ [Test]
+ public async Task TestCommitRollbackReadOnlyTxDoesNothing([Values(true, false)] bool commit)
+ {
+ await using var tx = await Client.Transactions.BeginAsync(new TransactionOptions { ReadOnly = true });
+ var res = await PocoView.GetAsync(tx, new Poco { Key = 123 });
+
+ if (commit)
+ {
+ await tx.CommitAsync();
+ }
+ else
+ {
+ await tx.RollbackAsync();
+ }
+
+ Assert.IsFalse(res.HasValue);
+ }
+
private class CustomTx : ITransaction
{
public ValueTask DisposeAsync()
diff --git a/modules/platforms/dotnet/Apache.Ignite/Internal/Transactions/Transactions.cs b/modules/platforms/dotnet/Apache.Ignite/Internal/Transactions/Transactions.cs
index a096cdc7dc..2ea139d736 100644
--- a/modules/platforms/dotnet/Apache.Ignite/Internal/Transactions/Transactions.cs
+++ b/modules/platforms/dotnet/Apache.Ignite/Internal/Transactions/Transactions.cs
@@ -39,12 +39,10 @@ namespace Apache.Ignite.Internal.Transactions
}
/// <inheritdoc/>
- public async Task<ITransaction> BeginAsync()
+ public async Task<ITransaction> BeginAsync(TransactionOptions options)
{
using var writer = ProtoCommon.GetMessageWriter();
-
- // TODO: IGNITE-18696
- writer.MessageWriter.Write(false); // Read-only.
+ writer.MessageWriter.Write(options.ReadOnly);
// Transaction and all corresponding operations must be performed using the same connection.
var (resBuf, socket) = await _socket.DoOutInOpAndGetSocketAsync(ClientOp.TxBegin, request: writer).ConfigureAwait(false);
diff --git a/modules/platforms/dotnet/Apache.Ignite/Transactions/ITransaction.cs b/modules/platforms/dotnet/Apache.Ignite/Transactions/ITransaction.cs
index e5c4cea932..36d36f59da 100644
--- a/modules/platforms/dotnet/Apache.Ignite/Transactions/ITransaction.cs
+++ b/modules/platforms/dotnet/Apache.Ignite/Transactions/ITransaction.cs
@@ -23,7 +23,7 @@ namespace Apache.Ignite.Transactions
/// <summary>
/// Ignite transaction.
/// <para />
- /// Use <see cref="ITransactions.BeginAsync"/> to start a new transaction.
+ /// Use <see cref="ITransactions.BeginAsync()"/> to start a new transaction.
/// </summary>
public interface ITransaction : IAsyncDisposable
{
diff --git a/modules/platforms/dotnet/Apache.Ignite/Transactions/ITransactions.cs b/modules/platforms/dotnet/Apache.Ignite/Transactions/ITransactions.cs
index f2904e7edf..6914d1a7da 100644
--- a/modules/platforms/dotnet/Apache.Ignite/Transactions/ITransactions.cs
+++ b/modules/platforms/dotnet/Apache.Ignite/Transactions/ITransactions.cs
@@ -1,4 +1,4 @@
-/*
+ /*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
@@ -27,7 +27,14 @@ namespace Apache.Ignite.Transactions
/// <summary>
/// Starts a new transaction.
/// </summary>
+ /// <param name="options">Transaction options.</param>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
- Task<ITransaction> BeginAsync();
+ Task<ITransaction> BeginAsync(TransactionOptions options);
+
+ /// <summary>
+ /// Starts a new transaction.
+ /// </summary>
+ /// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
+ Task<ITransaction> BeginAsync() => BeginAsync(default);
}
}
diff --git a/modules/platforms/dotnet/Apache.Ignite/Transactions/ITransactions.cs b/modules/platforms/dotnet/Apache.Ignite/Transactions/TransactionOptions.cs
similarity index 64%
copy from modules/platforms/dotnet/Apache.Ignite/Transactions/ITransactions.cs
copy to modules/platforms/dotnet/Apache.Ignite/Transactions/TransactionOptions.cs
index f2904e7edf..acc005bdcc 100644
--- a/modules/platforms/dotnet/Apache.Ignite/Transactions/ITransactions.cs
+++ b/modules/platforms/dotnet/Apache.Ignite/Transactions/TransactionOptions.cs
@@ -15,19 +15,14 @@
* limitations under the License.
*/
-namespace Apache.Ignite.Transactions
-{
- using System.Threading.Tasks;
+namespace Apache.Ignite.Transactions;
- /// <summary>
- /// Ignite transactions API.
- /// </summary>
- public interface ITransactions
- {
- /// <summary>
- /// Starts a new transaction.
- /// </summary>
- /// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
- Task<ITransaction> BeginAsync();
- }
-}
+/// <summary>
+/// Ignite transaction options.
+/// </summary>
+/// <param name="ReadOnly">
+/// Whether to start a read-only transaction.
+/// Read-only transactions provide a snapshot view of data at a certain point in time.
+/// They are lock-free and perform better than normal transactions, but do not permit data modifications.
+/// </param>
+public readonly record struct TransactionOptions(bool ReadOnly);