From fccee1ef4ecfb351b8ddcaf757e890aa78666dbf Mon Sep 17 00:00:00 2001 From: deniaa Date: Mon, 5 Aug 2024 14:35:22 +0500 Subject: [PATCH 1/2] After commit 555b28f0a90cc03f36a66f53271d9fc321f62905 we encountered a lot of the ConnectionTimeout errors in case of slow networks, VPNs etc. So from now on the last attempt to make a request will be with timeout "AlmostInfinity" (not "RealInfinity" = null as before). --- .../Sending/RequestSender_Tests.cs | 14 ++++++---- .../ForkingRequestStrategy_Tests.cs | 27 ++++++++++++++++--- .../ParallelRequestStrategy_Tests.cs | 5 ++-- .../SequentialRequestStrategy_Tests.cs | 15 ++++++++++- .../SingleReplicaRequestStrategy_Tests.cs | 3 ++- .../Misc/ClusterClientConstants.cs | 13 +++++++++ .../Misc/TimeSpanExtensions.cs | 14 ++++++++++ .../PublicAPI.Unshipped.txt | 4 +++ .../Strategies/ForkingRequestStrategy.cs | 7 ++++- .../Strategies/ParallelRequestStrategy.cs | 4 ++- .../Strategies/SequentialRequestStrategy.cs | 8 ++++-- .../SingleReplicaRequestStrategy.cs | 3 ++- .../Transport/ConnectionAttemptsTransport.cs | 3 ++- 13 files changed, 102 insertions(+), 18 deletions(-) create mode 100644 Vostok.ClusterClient.Core/Misc/ClusterClientConstants.cs create mode 100644 Vostok.ClusterClient.Core/Misc/TimeSpanExtensions.cs diff --git a/Vostok.ClusterClient.Core.Tests/Sending/RequestSender_Tests.cs b/Vostok.ClusterClient.Core.Tests/Sending/RequestSender_Tests.cs index bc59ee17..4c28f088 100644 --- a/Vostok.ClusterClient.Core.Tests/Sending/RequestSender_Tests.cs +++ b/Vostok.ClusterClient.Core.Tests/Sending/RequestSender_Tests.cs @@ -92,7 +92,7 @@ public void Should_send_request_with_transport_when_request_conversion_succeeds( Send(tokenSource.Token); - transport.Received(1).SendAsync(absoluteRequest, connectionTimeout, Arg.Any(), tokenSource.Token); + transport.Received(1).SendAsync(absoluteRequest, Arg.Any(), Arg.Any(), tokenSource.Token); } [Test] @@ -105,14 +105,18 @@ public void Should_pass_connection_timeout() transport.Received(1).SendAsync(absoluteRequest, connectionTimeout, Arg.Any(), Arg.Any()); } - [Test] - public void Should_pass_null_connection_timeout_when_it_is_greater_than_full_timeout() + [TestCase(5, TestName = "ConnectionTimeout less than ClusterClientConstants.LastAttemptConnectionTimeBudget")] + [TestCase(9, TestName = "ConnectionTimeout greater than ClusterClientConstants.LastAttemptConnectionTimeBudget")] + public void Should_correctly_select_connection_timeout_when_it_is_greater_than_full_timeout(int secondsConnectionTimeout) { - connectionTimeout = 10.Seconds(); + connectionTimeout = secondsConnectionTimeout.Seconds(); Send(); - transport.Received(1).SendAsync(absoluteRequest, null, Arg.Any(), Arg.Any()); + var expectedConnectionTimeout = connectionTimeout > ClusterClientConstants.LastAttemptConnectionTimeBudget + ? connectionTimeout + : ClusterClientConstants.LastAttemptConnectionTimeBudget; + transport.Received(1).SendAsync(absoluteRequest, expectedConnectionTimeout, Arg.Any(), Arg.Any()); } [Test] diff --git a/Vostok.ClusterClient.Core.Tests/Strategies/ForkingRequestStrategy_Tests.cs b/Vostok.ClusterClient.Core.Tests/Strategies/ForkingRequestStrategy_Tests.cs index 0be57d12..536d2460 100644 --- a/Vostok.ClusterClient.Core.Tests/Strategies/ForkingRequestStrategy_Tests.cs +++ b/Vostok.ClusterClient.Core.Tests/Strategies/ForkingRequestStrategy_Tests.cs @@ -4,11 +4,11 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; -using Castle.Core.Logging; using FluentAssertions; using FluentAssertions.Extensions; using NSubstitute; using NUnit.Framework; +using Vostok.Clusterclient.Core.Misc; using Vostok.Clusterclient.Core.Model; using Vostok.Clusterclient.Core.Sending; using Vostok.Clusterclient.Core.Strategies; @@ -217,7 +217,28 @@ public void Should_launch_all_requests_with_configured_connection_timeout() strategy = new ForkingRequestStrategy(delaysProvider, delaysPlanner, replicas.Length); - strategy.SendAsync(request, parameters, sender, Budget.WithRemaining(5.Seconds()), replicas, replicas.Length, token); + var remainingBudget = 5.Seconds(); + strategy.SendAsync(request, parameters, sender, Budget.WithRemaining(remainingBudget), replicas, replicas.Length, token); + + for (var i = 0; i < replicas.Length; ++i) + CompleteForkingDelay(); + + for (var i = 0; i < replicas.Length - 1; ++i) + sender.Received(1).SendToReplicaAsync(replicas[i], Arg.Any(), parameters.ConnectionTimeout, Arg.Any(), Arg.Any()); + + sender.Received(1).SendToReplicaAsync(replicas.Last(), Arg.Any(), ClusterClientConstants.LastAttemptConnectionTimeBudget, remainingBudget, Arg.Any()); + } + + [Test] + public void Should_select_connection_timeout_on_last_attempt_as_max_between_it_and_ClusterClientConstantsLastAttemptConnectionTimeBudget() + { + sender.ClearReceivedCalls(); + + strategy = new ForkingRequestStrategy(delaysProvider, delaysPlanner, replicas.Length); + + var parameters = RequestParameters.Empty.WithConnectionTimeout(100.Seconds()); + var remainingBudget = 5.Seconds(); + strategy.SendAsync(request, parameters, sender, Budget.WithRemaining(remainingBudget), replicas, replicas.Length, token); for (var i = 0; i < replicas.Length; ++i) CompleteForkingDelay(); @@ -225,7 +246,7 @@ public void Should_launch_all_requests_with_configured_connection_timeout() for (var i = 0; i < replicas.Length - 1; ++i) sender.Received(1).SendToReplicaAsync(replicas[i], Arg.Any(), parameters.ConnectionTimeout, Arg.Any(), Arg.Any()); - sender.Received(1).SendToReplicaAsync(replicas.Last(), Arg.Any(), parameters.ConnectionTimeout, Arg.Any(), Arg.Any()); + sender.Received(1).SendToReplicaAsync(replicas.Last(), Arg.Any(), parameters.ConnectionTimeout, remainingBudget, Arg.Any()); } [TestCase(0)] diff --git a/Vostok.ClusterClient.Core.Tests/Strategies/ParallelRequestStrategy_Tests.cs b/Vostok.ClusterClient.Core.Tests/Strategies/ParallelRequestStrategy_Tests.cs index 12a1f7f5..ef12202b 100644 --- a/Vostok.ClusterClient.Core.Tests/Strategies/ParallelRequestStrategy_Tests.cs +++ b/Vostok.ClusterClient.Core.Tests/Strategies/ParallelRequestStrategy_Tests.cs @@ -8,6 +8,7 @@ using FluentAssertions.Extensions; using NSubstitute; using NUnit.Framework; +using Vostok.Clusterclient.Core.Misc; using Vostok.Clusterclient.Core.Model; using Vostok.Clusterclient.Core.Sending; using Vostok.Clusterclient.Core.Strategies; @@ -146,7 +147,7 @@ public void Should_fire_initial_requests_to_all_replicas_if_parallelism_level_is } [Test] - public void Should_use_configured_connection_timeout_for_all_requests() + public void Should_use_ClusterClientConstantsLastAttemptConnectionTimeBudget_connection_timeout_for_all_requests() { strategy = new ParallelRequestStrategy(int.MaxValue); @@ -158,7 +159,7 @@ public void Should_use_configured_connection_timeout_for_all_requests() foreach (var replica in replicas) { - sender.Received(1).SendToReplicaAsync(replica, Arg.Any(), parameters.ConnectionTimeout, Arg.Any(), Arg.Any()); + sender.Received(1).SendToReplicaAsync(replica, Arg.Any(), ClusterClientConstants.LastAttemptConnectionTimeBudget, Arg.Any(), Arg.Any()); } } diff --git a/Vostok.ClusterClient.Core.Tests/Strategies/SequentialRequestStrategy_Tests.cs b/Vostok.ClusterClient.Core.Tests/Strategies/SequentialRequestStrategy_Tests.cs index 7db6ba11..22662c3c 100644 --- a/Vostok.ClusterClient.Core.Tests/Strategies/SequentialRequestStrategy_Tests.cs +++ b/Vostok.ClusterClient.Core.Tests/Strategies/SequentialRequestStrategy_Tests.cs @@ -5,6 +5,7 @@ using FluentAssertions.Extensions; using NSubstitute; using NUnit.Framework; +using Vostok.Clusterclient.Core.Misc; using Vostok.Clusterclient.Core.Model; using Vostok.Clusterclient.Core.Sending; using Vostok.Clusterclient.Core.Strategies; @@ -145,10 +146,22 @@ public void Should_limit_request_timeouts_by_remaining_time_budget() } [Test] - public void Should_use_configured_connection_timeout() + public void Should_use_configured_connection_timeout_and_ClusterClientConstantsLastAttemptConnectionTimeBudget() { Send(Budget.WithRemaining(1500.Milliseconds())); + sender.ReceivedCalls().Should().HaveCount(3); + sender.Received(1).SendToReplicaAsync(replica1, request, parameters.ConnectionTimeout, Arg.Any(), token); + sender.Received(1).SendToReplicaAsync(replica2, request, parameters.ConnectionTimeout, Arg.Any(), token); + sender.Received(1).SendToReplicaAsync(replica3, request, ClusterClientConstants.LastAttemptConnectionTimeBudget, Arg.Any(), token); + } + + [Test] + public void Should_use_configured_connection_timeout_if_it_greater_than_ClusterClientConstantsLastAttemptConnectionTimeBudget() + { + var parameters = RequestParameters.Empty.WithConnectionTimeout(ClusterClientConstants.LastAttemptConnectionTimeBudget + 100.Milliseconds()); + strategy.SendAsync(request, parameters, sender, Budget.WithRemaining(1500.Milliseconds()), replicas, replicas.Length, token).GetAwaiter().GetResult(); + sender.ReceivedCalls().Should().HaveCount(3); sender.Received(1).SendToReplicaAsync(replica1, request, parameters.ConnectionTimeout, Arg.Any(), token); sender.Received(1).SendToReplicaAsync(replica2, request, parameters.ConnectionTimeout, Arg.Any(), token); diff --git a/Vostok.ClusterClient.Core.Tests/Strategies/SingleReplicaRequestStrategy_Tests.cs b/Vostok.ClusterClient.Core.Tests/Strategies/SingleReplicaRequestStrategy_Tests.cs index f0bc4c20..4ffc14ab 100644 --- a/Vostok.ClusterClient.Core.Tests/Strategies/SingleReplicaRequestStrategy_Tests.cs +++ b/Vostok.ClusterClient.Core.Tests/Strategies/SingleReplicaRequestStrategy_Tests.cs @@ -4,6 +4,7 @@ using FluentAssertions.Extensions; using NSubstitute; using NUnit.Framework; +using Vostok.Clusterclient.Core.Misc; using Vostok.Clusterclient.Core.Model; using Vostok.Clusterclient.Core.Sending; using Vostok.Clusterclient.Core.Strategies; @@ -49,7 +50,7 @@ public void Should_send_request_to_first_replica_with_correct_parameters() strategy.SendAsync(request, parameters, sender, budget, replicas, replicas.Length, token).Wait(); - sender.Received().SendToReplicaAsync(replicas[0], request, parameters.ConnectionTimeout, budget.Remaining, token); + sender.Received().SendToReplicaAsync(replicas[0], request, ClusterClientConstants.LastAttemptConnectionTimeBudget, budget.Remaining, token); } diff --git a/Vostok.ClusterClient.Core/Misc/ClusterClientConstants.cs b/Vostok.ClusterClient.Core/Misc/ClusterClientConstants.cs new file mode 100644 index 00000000..b46dc33f --- /dev/null +++ b/Vostok.ClusterClient.Core/Misc/ClusterClientConstants.cs @@ -0,0 +1,13 @@ +using System; + +namespace Vostok.Clusterclient.Core.Misc; + +public static class ClusterClientConstants +{ + //(deniaa): We can't use "null" as connection time budget because of a bug in Net6. + // Also we can't use TimeBudget.Remaining as connection time budget on last attempt in strategies + // because of HttpMessageHandler cache in Vostok.Clusterclient.Transport.Sockets.SocketsHandlerProvider. + // Connection timeout is a key in this cache and we don't want to create a new HttpMessageHandler for each request. + // So we want to have only one constant "Infinite" for the whole ClusterClient. + public static TimeSpan LastAttemptConnectionTimeBudget = TimeSpan.FromSeconds(7); +} \ No newline at end of file diff --git a/Vostok.ClusterClient.Core/Misc/TimeSpanExtensions.cs b/Vostok.ClusterClient.Core/Misc/TimeSpanExtensions.cs new file mode 100644 index 00000000..c9e839ad --- /dev/null +++ b/Vostok.ClusterClient.Core/Misc/TimeSpanExtensions.cs @@ -0,0 +1,14 @@ +using System; +using Vostok.Commons.Time; + +namespace Vostok.Clusterclient.Core.Misc; + +public static class TimeSpanExtensions +{ + public static TimeSpan? Max(TimeSpan lastAttemptConnectionTimeBudget, TimeSpan? connectionTimeoutFromParameters) + { + return connectionTimeoutFromParameters == null + ? lastAttemptConnectionTimeBudget + : TimeSpanArithmetics.Max(lastAttemptConnectionTimeBudget, connectionTimeoutFromParameters.Value); + } +} \ No newline at end of file diff --git a/Vostok.ClusterClient.Core/PublicAPI.Unshipped.txt b/Vostok.ClusterClient.Core/PublicAPI.Unshipped.txt index e69de29b..ab618782 100644 --- a/Vostok.ClusterClient.Core/PublicAPI.Unshipped.txt +++ b/Vostok.ClusterClient.Core/PublicAPI.Unshipped.txt @@ -0,0 +1,4 @@ +static Vostok.Clusterclient.Core.Misc.ClusterClientConstants.LastAttemptConnectionTimeBudget -> System.TimeSpan +static Vostok.Clusterclient.Core.Misc.TimeSpanExtensions.Max(System.TimeSpan lastAttemptConnectionTimeBudget, System.TimeSpan? connectionTimeoutFromParameters) -> System.TimeSpan? +Vostok.Clusterclient.Core.Misc.ClusterClientConstants +Vostok.Clusterclient.Core.Misc.TimeSpanExtensions \ No newline at end of file diff --git a/Vostok.ClusterClient.Core/Strategies/ForkingRequestStrategy.cs b/Vostok.ClusterClient.Core/Strategies/ForkingRequestStrategy.cs index 9a0d0e94..738d03a8 100644 --- a/Vostok.ClusterClient.Core/Strategies/ForkingRequestStrategy.cs +++ b/Vostok.ClusterClient.Core/Strategies/ForkingRequestStrategy.cs @@ -4,6 +4,7 @@ using System.Threading; using System.Threading.Tasks; using JetBrains.Annotations; +using Vostok.Clusterclient.Core.Misc; using Vostok.Clusterclient.Core.Model; using Vostok.Clusterclient.Core.Sending; using Vostok.Clusterclient.Core.Strategies.DelayProviders; @@ -83,7 +84,11 @@ public async Task SendAsync(Request request, RequestParameters parameters, IRequ break; } - LaunchRequest(currentTasks, request, budget, sender, replicasEnumerator, parameters.ConnectionTimeout, linkedCancellationToken); + var connectionAttemptTimeout = i == replicasCount - 1 + ? TimeSpanExtensions.Max(ClusterClientConstants.LastAttemptConnectionTimeBudget, parameters.ConnectionTimeout) + : parameters.ConnectionTimeout; + + LaunchRequest(currentTasks, request, budget, sender, replicasEnumerator, connectionAttemptTimeout, linkedCancellationToken); ScheduleForkIfNeeded(currentTasks, request, budget, i, replicasCount, linkedCancellationToken); diff --git a/Vostok.ClusterClient.Core/Strategies/ParallelRequestStrategy.cs b/Vostok.ClusterClient.Core/Strategies/ParallelRequestStrategy.cs index c56046a0..cc7e4a18 100644 --- a/Vostok.ClusterClient.Core/Strategies/ParallelRequestStrategy.cs +++ b/Vostok.ClusterClient.Core/Strategies/ParallelRequestStrategy.cs @@ -3,6 +3,7 @@ using System.Threading; using System.Threading.Tasks; using JetBrains.Annotations; +using Vostok.Clusterclient.Core.Misc; using Vostok.Clusterclient.Core.Model; using Vostok.Clusterclient.Core.Sending; @@ -59,7 +60,8 @@ public async Task SendAsync(Request request, RequestParameters parameters, IRequ if (!replicasEnumerator.MoveNext()) throw new InvalidOperationException("Replicas enumerator ended prematurely. This is definitely a bug in code."); - currentTasks.Add(sender.SendToReplicaAsync(replicasEnumerator.Current, request, parameters.ConnectionTimeout, budget.Remaining, linkedCancellationToken)); + var connectionTimeout = TimeSpanExtensions.Max(ClusterClientConstants.LastAttemptConnectionTimeBudget, parameters.ConnectionTimeout); + currentTasks.Add(sender.SendToReplicaAsync(replicasEnumerator.Current, request, connectionTimeout, budget.Remaining, linkedCancellationToken)); } while (currentTasks.Count > 0) diff --git a/Vostok.ClusterClient.Core/Strategies/SequentialRequestStrategy.cs b/Vostok.ClusterClient.Core/Strategies/SequentialRequestStrategy.cs index 0c262d92..de5e6865 100644 --- a/Vostok.ClusterClient.Core/Strategies/SequentialRequestStrategy.cs +++ b/Vostok.ClusterClient.Core/Strategies/SequentialRequestStrategy.cs @@ -3,11 +3,11 @@ using System.Threading; using System.Threading.Tasks; using JetBrains.Annotations; +using Vostok.Clusterclient.Core.Misc; using Vostok.Clusterclient.Core.Model; using Vostok.Clusterclient.Core.Sending; using Vostok.Clusterclient.Core.Strategies.TimeoutProviders; using Vostok.Commons.Time; -using Vostok.Logging.Abstractions; namespace Vostok.Clusterclient.Core.Strategies { @@ -53,7 +53,11 @@ public async Task SendAsync(Request request, RequestParameters parameters, IRequ var timeout = TimeSpanArithmetics.Min(timeoutsProvider.GetTimeout(request, budget, currentReplicaIndex++, replicasCount), budget.Remaining); - var result = await sender.SendToReplicaAsync(replica, request, parameters.ConnectionTimeout, timeout, cancellationToken).ConfigureAwait(false); + var connectionAttemptTimeout = currentReplicaIndex == replicasCount + ? TimeSpanExtensions.Max(ClusterClientConstants.LastAttemptConnectionTimeBudget, parameters.ConnectionTimeout) + : parameters.ConnectionTimeout; + + var result = await sender.SendToReplicaAsync(replica, request, connectionAttemptTimeout, timeout, cancellationToken).ConfigureAwait(false); if (result.Verdict == ResponseVerdict.Accept) break; diff --git a/Vostok.ClusterClient.Core/Strategies/SingleReplicaRequestStrategy.cs b/Vostok.ClusterClient.Core/Strategies/SingleReplicaRequestStrategy.cs index 8c7f3428..4d373473 100644 --- a/Vostok.ClusterClient.Core/Strategies/SingleReplicaRequestStrategy.cs +++ b/Vostok.ClusterClient.Core/Strategies/SingleReplicaRequestStrategy.cs @@ -4,6 +4,7 @@ using System.Threading; using System.Threading.Tasks; using JetBrains.Annotations; +using Vostok.Clusterclient.Core.Misc; using Vostok.Clusterclient.Core.Model; using Vostok.Clusterclient.Core.Sending; @@ -23,7 +24,7 @@ public class SingleReplicaRequestStrategy : IRequestStrategy { /// public Task SendAsync(Request request, RequestParameters parameters, IRequestSender sender, IRequestTimeBudget budget, IEnumerable replicas, int replicasCount, CancellationToken cancellationToken) => - sender.SendToReplicaAsync(replicas.First(), request, parameters.ConnectionTimeout, budget.Remaining, cancellationToken); + sender.SendToReplicaAsync(replicas.First(), request, ClusterClientConstants.LastAttemptConnectionTimeBudget, budget.Remaining, cancellationToken); /// public override string ToString() => "SingleReplica"; diff --git a/Vostok.ClusterClient.Core/Transport/ConnectionAttemptsTransport.cs b/Vostok.ClusterClient.Core/Transport/ConnectionAttemptsTransport.cs index df6ff15d..705a6528 100644 --- a/Vostok.ClusterClient.Core/Transport/ConnectionAttemptsTransport.cs +++ b/Vostok.ClusterClient.Core/Transport/ConnectionAttemptsTransport.cs @@ -2,6 +2,7 @@ using System.Threading; using System.Threading.Tasks; using JetBrains.Annotations; +using Vostok.Clusterclient.Core.Misc; using Vostok.Clusterclient.Core.Model; using Vostok.Commons.Time; @@ -33,7 +34,7 @@ public async Task SendAsync(Request request, TimeSpan? connectionTimeo for (var attempt = 1; attempt <= connectionAttempts; ++attempt) { var connectionAttemptTimeout = connectionTimeout == null || timeBudget.Remaining < connectionTimeout - ? (TimeSpan?)null + ? TimeSpanExtensions.Max(ClusterClientConstants.LastAttemptConnectionTimeBudget, connectionTimeout) : connectionTimeout.Value; var response = await transport.SendAsync(request, connectionAttemptTimeout, timeBudget.Remaining, cancellationToken).ConfigureAwait(false); From 45f9f901f8134cc41cc2bd30f1bbeef991f342e1 Mon Sep 17 00:00:00 2001 From: deniaa Date: Mon, 5 Aug 2024 23:47:08 +0500 Subject: [PATCH 2/2] CR --- Vostok.ClusterClient.Core/Misc/ClusterClientConstants.cs | 4 ++-- Vostok.ClusterClient.Core/Misc/TimeSpanExtensions.cs | 4 ++-- .../Strategies/ForkingRequestStrategy.cs | 2 +- .../Strategies/ParallelRequestStrategy.cs | 2 +- .../Strategies/SequentialRequestStrategy.cs | 2 +- .../Transport/ConnectionAttemptsTransport.cs | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Vostok.ClusterClient.Core/Misc/ClusterClientConstants.cs b/Vostok.ClusterClient.Core/Misc/ClusterClientConstants.cs index b46dc33f..271f2fe7 100644 --- a/Vostok.ClusterClient.Core/Misc/ClusterClientConstants.cs +++ b/Vostok.ClusterClient.Core/Misc/ClusterClientConstants.cs @@ -2,10 +2,10 @@ namespace Vostok.Clusterclient.Core.Misc; -public static class ClusterClientConstants +internal static class ClusterClientConstants { //(deniaa): We can't use "null" as connection time budget because of a bug in Net6. - // Also we can't use TimeBudget.Remaining as connection time budget on last attempt in strategies + // Also we can't use TimeBudget.Remaining as connection time budget for the last attempt in strategies // because of HttpMessageHandler cache in Vostok.Clusterclient.Transport.Sockets.SocketsHandlerProvider. // Connection timeout is a key in this cache and we don't want to create a new HttpMessageHandler for each request. // So we want to have only one constant "Infinite" for the whole ClusterClient. diff --git a/Vostok.ClusterClient.Core/Misc/TimeSpanExtensions.cs b/Vostok.ClusterClient.Core/Misc/TimeSpanExtensions.cs index c9e839ad..23dc3172 100644 --- a/Vostok.ClusterClient.Core/Misc/TimeSpanExtensions.cs +++ b/Vostok.ClusterClient.Core/Misc/TimeSpanExtensions.cs @@ -3,9 +3,9 @@ namespace Vostok.Clusterclient.Core.Misc; -public static class TimeSpanExtensions +internal static class TimeSpanExtensions { - public static TimeSpan? Max(TimeSpan lastAttemptConnectionTimeBudget, TimeSpan? connectionTimeoutFromParameters) + public static TimeSpan? SelectConnectionTimeoutForLastAttempt(TimeSpan lastAttemptConnectionTimeBudget, TimeSpan? connectionTimeoutFromParameters) { return connectionTimeoutFromParameters == null ? lastAttemptConnectionTimeBudget diff --git a/Vostok.ClusterClient.Core/Strategies/ForkingRequestStrategy.cs b/Vostok.ClusterClient.Core/Strategies/ForkingRequestStrategy.cs index 738d03a8..6fe0888a 100644 --- a/Vostok.ClusterClient.Core/Strategies/ForkingRequestStrategy.cs +++ b/Vostok.ClusterClient.Core/Strategies/ForkingRequestStrategy.cs @@ -85,7 +85,7 @@ public async Task SendAsync(Request request, RequestParameters parameters, IRequ } var connectionAttemptTimeout = i == replicasCount - 1 - ? TimeSpanExtensions.Max(ClusterClientConstants.LastAttemptConnectionTimeBudget, parameters.ConnectionTimeout) + ? TimeSpanExtensions.SelectConnectionTimeoutForLastAttempt(ClusterClientConstants.LastAttemptConnectionTimeBudget, parameters.ConnectionTimeout) : parameters.ConnectionTimeout; LaunchRequest(currentTasks, request, budget, sender, replicasEnumerator, connectionAttemptTimeout, linkedCancellationToken); diff --git a/Vostok.ClusterClient.Core/Strategies/ParallelRequestStrategy.cs b/Vostok.ClusterClient.Core/Strategies/ParallelRequestStrategy.cs index cc7e4a18..4bcb6578 100644 --- a/Vostok.ClusterClient.Core/Strategies/ParallelRequestStrategy.cs +++ b/Vostok.ClusterClient.Core/Strategies/ParallelRequestStrategy.cs @@ -60,7 +60,7 @@ public async Task SendAsync(Request request, RequestParameters parameters, IRequ if (!replicasEnumerator.MoveNext()) throw new InvalidOperationException("Replicas enumerator ended prematurely. This is definitely a bug in code."); - var connectionTimeout = TimeSpanExtensions.Max(ClusterClientConstants.LastAttemptConnectionTimeBudget, parameters.ConnectionTimeout); + var connectionTimeout = TimeSpanExtensions.SelectConnectionTimeoutForLastAttempt(ClusterClientConstants.LastAttemptConnectionTimeBudget, parameters.ConnectionTimeout); currentTasks.Add(sender.SendToReplicaAsync(replicasEnumerator.Current, request, connectionTimeout, budget.Remaining, linkedCancellationToken)); } diff --git a/Vostok.ClusterClient.Core/Strategies/SequentialRequestStrategy.cs b/Vostok.ClusterClient.Core/Strategies/SequentialRequestStrategy.cs index de5e6865..e834b1b7 100644 --- a/Vostok.ClusterClient.Core/Strategies/SequentialRequestStrategy.cs +++ b/Vostok.ClusterClient.Core/Strategies/SequentialRequestStrategy.cs @@ -54,7 +54,7 @@ public async Task SendAsync(Request request, RequestParameters parameters, IRequ var timeout = TimeSpanArithmetics.Min(timeoutsProvider.GetTimeout(request, budget, currentReplicaIndex++, replicasCount), budget.Remaining); var connectionAttemptTimeout = currentReplicaIndex == replicasCount - ? TimeSpanExtensions.Max(ClusterClientConstants.LastAttemptConnectionTimeBudget, parameters.ConnectionTimeout) + ? TimeSpanExtensions.SelectConnectionTimeoutForLastAttempt(ClusterClientConstants.LastAttemptConnectionTimeBudget, parameters.ConnectionTimeout) : parameters.ConnectionTimeout; var result = await sender.SendToReplicaAsync(replica, request, connectionAttemptTimeout, timeout, cancellationToken).ConfigureAwait(false); diff --git a/Vostok.ClusterClient.Core/Transport/ConnectionAttemptsTransport.cs b/Vostok.ClusterClient.Core/Transport/ConnectionAttemptsTransport.cs index 705a6528..15130ffe 100644 --- a/Vostok.ClusterClient.Core/Transport/ConnectionAttemptsTransport.cs +++ b/Vostok.ClusterClient.Core/Transport/ConnectionAttemptsTransport.cs @@ -34,7 +34,7 @@ public async Task SendAsync(Request request, TimeSpan? connectionTimeo for (var attempt = 1; attempt <= connectionAttempts; ++attempt) { var connectionAttemptTimeout = connectionTimeout == null || timeBudget.Remaining < connectionTimeout - ? TimeSpanExtensions.Max(ClusterClientConstants.LastAttemptConnectionTimeBudget, connectionTimeout) + ? TimeSpanExtensions.SelectConnectionTimeoutForLastAttempt(ClusterClientConstants.LastAttemptConnectionTimeBudget, connectionTimeout) : connectionTimeout.Value; var response = await transport.SendAsync(request, connectionAttemptTimeout, timeBudget.Remaining, cancellationToken).ConfigureAwait(false);