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 aebca24f0a IGNITE-20468 .NET: Fix test timeouts (#2626)
aebca24f0a is described below

commit aebca24f0a39aa195cea97e540419482f1753fb8
Author: Pavel Tupitsyn <ptupit...@apache.org>
AuthorDate: Tue Sep 26 17:40:31 2023 +0300

    IGNITE-20468 .NET: Fix test timeouts (#2626)
    
    Improve .NET tests reliability and observability:
    
    * Fix `IgniteProxy` - use two threads for bi-directional communication (it 
was fully sequential, causing heartbeat timeouts when some other operation 
takes a few seconds)
    * Fix `IgniteServerBase` dispose logic
    * Add global 15 second timeout for all tests to avoid waiting forever
    * Enable client-side logging in base test class
    
    `TestExecuteColocated` is still flaky because of a known deadlock issue in 
`SchemaRegistry`: https://issues.apache.org/jira/browse/IGNITE-17931
---
 .../Apache.Ignite.Tests/Apache.Ignite.Tests.csproj |  7 ++
 .../Apache.Ignite.Tests/IgniteClientExtensions.cs  |  2 +-
 .../dotnet/Apache.Ignite.Tests/IgniteProxy.cs      | 90 ++++++++++++++++------
 .../dotnet/Apache.Ignite.Tests/IgniteServerBase.cs | 37 +++++----
 .../dotnet/Apache.Ignite.Tests/IgniteTestsBase.cs  | 17 +++-
 .../dotnet/Apache.Ignite.Tests/ReconnectTests.cs   |  3 +-
 .../Apache.Ignite.Tests/Table/DataStreamerTests.cs |  2 +-
 .../Table/RecordViewCustomMappingTest.cs           |  2 +-
 .../Table/RecordViewDefaultMappingTest.cs          |  2 +-
 9 files changed, 112 insertions(+), 50 deletions(-)

diff --git 
a/modules/platforms/dotnet/Apache.Ignite.Tests/Apache.Ignite.Tests.csproj 
b/modules/platforms/dotnet/Apache.Ignite.Tests/Apache.Ignite.Tests.csproj
index 232c2508e4..0e776fe96c 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Tests/Apache.Ignite.Tests.csproj
+++ b/modules/platforms/dotnet/Apache.Ignite.Tests/Apache.Ignite.Tests.csproj
@@ -49,4 +49,11 @@
       </None>
     </ItemGroup>
 
+    <ItemGroup>
+        <AssemblyAttribute Include="NUnit.Framework.TimeoutAttribute">
+            <_Parameter1>15000</_Parameter1>
+            <_Parameter1_TypeName>System.Int32</_Parameter1_TypeName>
+        </AssemblyAttribute>
+    </ItemGroup>
+
 </Project>
diff --git 
a/modules/platforms/dotnet/Apache.Ignite.Tests/IgniteClientExtensions.cs 
b/modules/platforms/dotnet/Apache.Ignite.Tests/IgniteClientExtensions.cs
index 1f32a30cc0..b50800411c 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Tests/IgniteClientExtensions.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Tests/IgniteClientExtensions.cs
@@ -28,7 +28,7 @@ public static class IgniteClientExtensions
     /// <param name="client">Client.</param>
     /// <param name="count">Connection count.</param>
     /// <param name="timeoutMs">Timeout.</param>
-    public static void WaitForConnections(this IIgniteClient client, int 
count, int timeoutMs = 5000) =>
+    public static void WaitForConnections(this IIgniteClient client, int 
count, int timeoutMs = 15_000) =>
         TestUtils.WaitForCondition(
             condition: () => client.GetConnections().Count == count,
             timeoutMs: timeoutMs,
diff --git a/modules/platforms/dotnet/Apache.Ignite.Tests/IgniteProxy.cs 
b/modules/platforms/dotnet/Apache.Ignite.Tests/IgniteProxy.cs
index 57449fb49e..979c455ef7 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Tests/IgniteProxy.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Tests/IgniteProxy.cs
@@ -24,6 +24,7 @@ using System.Linq;
 using System.Net;
 using System.Net.Sockets;
 using System.Threading;
+using System.Threading.Tasks;
 using Internal.Proto;
 
 /// <summary>
@@ -55,47 +56,86 @@ public sealed class IgniteProxy : IgniteServerBase
 
     protected override void Handle(Socket handler, CancellationToken 
cancellationToken)
     {
-        int messageCount = 0;
-
-        while (!cancellationToken.IsCancellationRequested)
+        // Handshake.
         {
-            if (messageCount == 0)
-            {
-                // Forward magic from client to server.
-                using var magic = ReceiveBytes(handler, 4);
-                _socket.Send(magic.AsMemory().Span);
-            }
+            // Forward magic from client to server.
+            using var magic = ReceiveBytes(handler, 4);
+            _socket.Send(magic.AsMemory().Span);
 
-            // Receive from client.
+            // Receive handshake from client.
             var msgSize = ReceiveMessageSize(handler);
             using var msg = ReceiveBytes(handler, msgSize);
 
-            if (messageCount > 0)
-            {
-                _ops.Enqueue((ClientOp)msg.GetReader().ReadInt32());
-            }
-
-            // Forward to server.
+            // Forward handshake to server.
             
_socket.Send(BitConverter.GetBytes(IPAddress.HostToNetworkOrder(msgSize)));
             _socket.Send(msg.AsMemory().Span);
 
-            // Receive from server.
-            if (messageCount == 0)
-            {
-                // Forward magic from server to client.
-                using var serverMagic = ReceiveBytes(_socket, 4);
-                handler.Send(serverMagic.AsMemory().Span);
-            }
+            // Forward magic from server to client.
+            using var serverMagic = ReceiveBytes(_socket, 4);
+            handler.Send(serverMagic.AsMemory().Span);
 
+            // Receive handshake from server.
             var serverMsgSize = ReceiveMessageSize(_socket);
             using var serverMsg = ReceiveBytes(_socket, serverMsgSize);
 
             // Forward to client.
             
handler.Send(BitConverter.GetBytes(IPAddress.HostToNetworkOrder(serverMsgSize)));
             handler.Send(serverMsg.AsMemory().Span);
-
-            messageCount++;
         }
+
+        // Separate relay loops for each direction: don't block heartbeats 
while some request is being processed.
+        // Client -> Server.
+        var clientToServerRelay = Task.Run(
+            () =>
+            {
+                while (!cancellationToken.IsCancellationRequested)
+                {
+                    try
+                    {
+                        // Receive from client.
+                        var msgSize = ReceiveMessageSize(handler);
+                        using var msg = ReceiveBytes(handler, msgSize);
+                        _ops.Enqueue((ClientOp)msg.GetReader().ReadInt32());
+
+                        // Forward to server.
+                        
_socket.Send(BitConverter.GetBytes(IPAddress.HostToNetworkOrder(msgSize)));
+                        _socket.Send(msg.AsMemory().Span);
+                    }
+                    catch (Exception e)
+                    {
+                        Console.WriteLine($"Error in IgniteProxy Client -> 
Server relay (lastOp = {_ops.Last()}: {e}");
+                        throw;
+                    }
+                }
+            },
+            cancellationToken);
+
+        // Server -> Client.
+        var serverToClientRelay = Task.Run(
+            () =>
+            {
+                while (!cancellationToken.IsCancellationRequested)
+                {
+                    try
+                    {
+                        // Receive from server.
+                        var serverMsgSize = ReceiveMessageSize(_socket);
+                        using var serverMsg = ReceiveBytes(_socket, 
serverMsgSize);
+
+                        // Forward to client.
+                        
handler.Send(BitConverter.GetBytes(IPAddress.HostToNetworkOrder(serverMsgSize)));
+                        handler.Send(serverMsg.AsMemory().Span);
+                    }
+                    catch (Exception e)
+                    {
+                        Console.WriteLine($"Error in IgniteProxy Server -> 
Client relay (lastOp = {_ops.Last()}: {e}");
+                        throw;
+                    }
+                }
+            },
+            cancellationToken);
+
+        Task.WhenAll(clientToServerRelay, 
serverToClientRelay).Wait(millisecondsTimeout: 5000, cancellationToken);
     }
 
     protected override void Dispose(bool disposing)
diff --git a/modules/platforms/dotnet/Apache.Ignite.Tests/IgniteServerBase.cs 
b/modules/platforms/dotnet/Apache.Ignite.Tests/IgniteServerBase.cs
index 961ba14280..f9efb1565a 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Tests/IgniteServerBase.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Tests/IgniteServerBase.cs
@@ -70,24 +70,7 @@ public abstract class IgniteServerBase : IDisposable
 
     public void Dispose()
     {
-        lock (_disposeSyncRoot)
-        {
-            if (_disposed)
-            {
-                return;
-            }
-
-            _cts.Cancel();
-            _handler?.Dispose();
-            _listener.Disconnect(false);
-            _listener.Dispose();
-            _cts.Dispose();
-
-            Dispose(true);
-            GC.SuppressFinalize(this);
-
-            _disposed = true;
-        }
+        Dispose(true);
     }
 
     internal static int ReceiveMessageSize(Socket handler)
@@ -125,7 +108,23 @@ public abstract class IgniteServerBase : IDisposable
     {
         if (disposing)
         {
-            // No-op.
+            lock (_disposeSyncRoot)
+            {
+                if (_disposed)
+                {
+                    return;
+                }
+
+                _cts.Cancel();
+                _handler?.Dispose();
+                _listener.Disconnect(false);
+                _listener.Dispose();
+                _cts.Dispose();
+
+                GC.SuppressFinalize(this);
+
+                _disposed = true;
+            }
         }
     }
 
diff --git a/modules/platforms/dotnet/Apache.Ignite.Tests/IgniteTestsBase.cs 
b/modules/platforms/dotnet/Apache.Ignite.Tests/IgniteTestsBase.cs
index 4de5693910..c5f46c5953 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Tests/IgniteTestsBase.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Tests/IgniteTestsBase.cs
@@ -29,6 +29,8 @@ namespace Apache.Ignite.Tests
 
     /// <summary>
     /// Base class for client tests.
+    /// <para />
+    /// NOTE: Timeout is set for the entire assembly in csproj file.
     /// </summary>
     public class IgniteTestsBase
     {
@@ -124,13 +126,23 @@ namespace Apache.Ignite.Tests
             _eventListener.Dispose();
         }
 
+        [SetUp]
+        public void SetUp()
+        {
+            Console.WriteLine("SetUp: " + 
TestContext.CurrentContext.Test.Name);
+        }
+
         [TearDown]
         public void TearDown()
         {
+            Console.WriteLine("TearDown start: " + 
TestContext.CurrentContext.Test.Name);
+
             CheckPooledBufferLeak();
 
             _disposables.ForEach(x => x.Dispose());
             _disposables.Clear();
+
+            Console.WriteLine("TearDown end: " + 
TestContext.CurrentContext.Test.Name);
         }
 
         internal static string 
GetRequestTargetNodeName(IEnumerable<IgniteProxy> proxies, ClientOp op)
@@ -172,7 +184,10 @@ namespace Apache.Ignite.Tests
         };
 
         protected static IgniteClientConfiguration 
GetConfig(IEnumerable<IgniteProxy> proxies) =>
-            new(proxies.Select(x => x.Endpoint).ToArray());
+            new(proxies.Select(x => x.Endpoint).ToArray())
+            {
+                Logger = new ConsoleLogger { MinLevel = LogLevel.Trace }
+            };
 
         protected List<IgniteProxy> GetProxies()
         {
diff --git a/modules/platforms/dotnet/Apache.Ignite.Tests/ReconnectTests.cs 
b/modules/platforms/dotnet/Apache.Ignite.Tests/ReconnectTests.cs
index 23b22e563f..695377ded0 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Tests/ReconnectTests.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Tests/ReconnectTests.cs
@@ -86,7 +86,8 @@ public class ReconnectTests
         var cfg = new IgniteClientConfiguration
         {
             HeartbeatInterval = TimeSpan.FromMilliseconds(100),
-            ReconnectInterval = TimeSpan.FromMilliseconds(300)
+            ReconnectInterval = TimeSpan.FromMilliseconds(300),
+            Logger = new ConsoleLogger { MinLevel = LogLevel.Trace }
         };
 
         using var servers = FakeServerGroup.Create(10);
diff --git 
a/modules/platforms/dotnet/Apache.Ignite.Tests/Table/DataStreamerTests.cs 
b/modules/platforms/dotnet/Apache.Ignite.Tests/Table/DataStreamerTests.cs
index 5d8c0f1bf6..2ace822027 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Tests/Table/DataStreamerTests.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Tests/Table/DataStreamerTests.cs
@@ -37,7 +37,7 @@ public class DataStreamerTests : IgniteTestsBase
     private const int Count = 100;
 
     [SetUp]
-    public async Task SetUp() =>
+    public async Task DeleteAll() =>
         await TupleView.DeleteAllAsync(null, Enumerable.Range(0, 
Count).Select(x => GetTuple(x)));
 
     [Test]
diff --git 
a/modules/platforms/dotnet/Apache.Ignite.Tests/Table/RecordViewCustomMappingTest.cs
 
b/modules/platforms/dotnet/Apache.Ignite.Tests/Table/RecordViewCustomMappingTest.cs
index 5f5a7bc4d6..5286e5a5ad 100644
--- 
a/modules/platforms/dotnet/Apache.Ignite.Tests/Table/RecordViewCustomMappingTest.cs
+++ 
b/modules/platforms/dotnet/Apache.Ignite.Tests/Table/RecordViewCustomMappingTest.cs
@@ -37,7 +37,7 @@ public class RecordViewCustomMappingTest : IgniteTestsBase
     private const string Val = "val1";
 
     [SetUp]
-    public async Task SetUp()
+    public async Task InsertData()
     {
         await Table.RecordBinaryView.UpsertAsync(null, GetTuple(Key, Val));
     }
diff --git 
a/modules/platforms/dotnet/Apache.Ignite.Tests/Table/RecordViewDefaultMappingTest.cs
 
b/modules/platforms/dotnet/Apache.Ignite.Tests/Table/RecordViewDefaultMappingTest.cs
index 39eb7dd43a..a71c7a2fd4 100644
--- 
a/modules/platforms/dotnet/Apache.Ignite.Tests/Table/RecordViewDefaultMappingTest.cs
+++ 
b/modules/platforms/dotnet/Apache.Ignite.Tests/Table/RecordViewDefaultMappingTest.cs
@@ -30,7 +30,7 @@ namespace Apache.Ignite.Tests.Table
     public class RecordViewDefaultMappingTest : IgniteTestsBase
     {
         [SetUp]
-        public async Task SetUp()
+        public async Task InsertData()
         {
             await Table.RecordBinaryView.UpsertAsync(null, GetTuple(1, "2"));
         }

Reply via email to