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 27b60a58a4 IGNITE-20832 .NET: Improve retry mechanism (#2842) 27b60a58a4 is described below commit 27b60a58a4c993a2379baabb273e98e1f770b453 Author: Pavel Tupitsyn <ptupit...@apache.org> AuthorDate: Thu Nov 16 16:07:05 2023 +0200 IGNITE-20832 .NET: Improve retry mechanism (#2842) `TestDroppedConnectionIsRestoredOnDemand` was flaky because in some cases we failed to retry a broken connection error. * Close client socket immediately on write error * Improve `ShouldRetry` logic --- .../dotnet/Apache.Ignite.Tests/ReconnectTests.cs | 7 ++++++- .../Apache.Ignite/Internal/ClientFailoverSocket.cs | 9 +++++++-- .../dotnet/Apache.Ignite/Internal/ClientSocket.cs | 16 +++++++++++++++- 3 files changed, 28 insertions(+), 4 deletions(-) diff --git a/modules/platforms/dotnet/Apache.Ignite.Tests/ReconnectTests.cs b/modules/platforms/dotnet/Apache.Ignite.Tests/ReconnectTests.cs index 695377ded0..60767f09fd 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Tests/ReconnectTests.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Tests/ReconnectTests.cs @@ -70,8 +70,13 @@ public class ReconnectTests [Test] public async Task TestDroppedConnectionIsRestoredOnDemand() { + var cfg = new IgniteClientConfiguration + { + Logger = new ConsoleLogger { MinLevel = LogLevel.Debug } + }; + using var server = new FakeServer(); - using var client = await server.ConnectClientAsync(); + using var client = await server.ConnectClientAsync(cfg); Assert.DoesNotThrowAsync(async () => await client.Tables.GetTablesAsync()); diff --git a/modules/platforms/dotnet/Apache.Ignite/Internal/ClientFailoverSocket.cs b/modules/platforms/dotnet/Apache.Ignite/Internal/ClientFailoverSocket.cs index 172bf4a1c8..74a5016bcb 100644 --- a/modules/platforms/dotnet/Apache.Ignite/Internal/ClientFailoverSocket.cs +++ b/modules/platforms/dotnet/Apache.Ignite/Internal/ClientFailoverSocket.cs @@ -555,14 +555,14 @@ namespace Apache.Ignite.Internal { var e = exception; - while (e != null && !(e is SocketException)) + while (e != null && !IsConnectionError(e)) { e = e.InnerException; } if (e == null) { - // Only retry socket exceptions. + // Only retry connection errors. return false; } @@ -582,6 +582,11 @@ namespace Apache.Ignite.Internal var ctx = new RetryPolicyContext(new(Configuration), publicOpType.Value, attempt, exception); return retryPolicy.ShouldRetry(ctx); + + static bool IsConnectionError(Exception e) => + e is SocketException + or IOException + or IgniteClientConnectionException { Code: ErrorGroups.Client.Connection }; } /// <summary> diff --git a/modules/platforms/dotnet/Apache.Ignite/Internal/ClientSocket.cs b/modules/platforms/dotnet/Apache.Ignite/Internal/ClientSocket.cs index a0801138b0..dc24fcc5da 100644 --- a/modules/platforms/dotnet/Apache.Ignite/Internal/ClientSocket.cs +++ b/modules/platforms/dotnet/Apache.Ignite/Internal/ClientSocket.cs @@ -496,7 +496,7 @@ namespace Apache.Ignite.Internal { // Disconnected. throw new IgniteClientConnectionException( - ErrorGroups.Client.Protocol, + ErrorGroups.Client.Connection, "Connection lost (failed to read data from socket)", new SocketException((int) SocketError.ConnectionAborted)); } @@ -617,6 +617,10 @@ namespace Apache.Ignite.Internal sslStream.SslProtocol) : null; + [SuppressMessage( + "Microsoft.Design", + "CA1031:DoNotCatchGeneralExceptionTypes", + Justification = "Any exception during socket write should be handled to close the socket.")] private async ValueTask SendRequestAsync(PooledArrayBuffer? request, ClientOp op, long requestId) { // Reset heartbeat timer - don't sent heartbeats when connection is active anyway. @@ -664,6 +668,16 @@ namespace Apache.Ignite.Internal Metrics.RequestsSent.Add(1); } + catch (Exception e) + { + var message = "Exception while writing to socket, connection closed: " + e.Message; + + _logger?.Error(e, message); + var connEx = new IgniteClientConnectionException(ErrorGroups.Client.Connection, message, new SocketException()); + + Dispose(connEx); + throw connEx; + } finally { _sendLock.Release();