diff --git a/src/Raven.Client/Http/RavenCommand.cs b/src/Raven.Client/Http/RavenCommand.cs index 26e9c0b8f35..b11c84a981f 100644 --- a/src/Raven.Client/Http/RavenCommand.cs +++ b/src/Raven.Client/Http/RavenCommand.cs @@ -57,7 +57,7 @@ public abstract class RavenCommand public RavenCommandResponseType ResponseType { get; protected set; } - public TimeSpan? Timeout { get; protected set; } + public TimeSpan? Timeout { get; protected internal set; } public bool CanCache { get; protected set; } public bool CanCacheAggressively { get; protected set; } public string SelectedNodeTag { get; protected set; } diff --git a/src/Raven.Client/Http/RequestExecutor.cs b/src/Raven.Client/Http/RequestExecutor.cs index cfdef884966..35cc1800f97 100644 --- a/src/Raven.Client/Http/RequestExecutor.cs +++ b/src/Raven.Client/Http/RequestExecutor.cs @@ -487,6 +487,11 @@ public virtual async Task UpdateTopologyAsync(UpdateTopologyParameters par using (ContextPool.AllocateOperationContext(out JsonOperationContext context)) { var command = new GetDatabaseTopologyCommand(parameters.DebugTag, Conventions.SendApplicationIdentifier ? parameters.ApplicationIdentifier : null); + ForTestingPurposes?.SetCommandTimeout?.Invoke(command); + + if (DefaultTimeout.HasValue && DefaultTimeout.Value > command.Timeout) + command.Timeout = DefaultTimeout.Value; + await ExecuteAsync(parameters.Node, null, context, command, shouldRetry: false, sessionInfo: null, token: CancellationToken.None).ConfigureAwait(false); var topology = command.Result; @@ -995,6 +1000,7 @@ private async Task SendRequestToServer( cts.CancelAfter(timeout.Value); try { + ForTestingPurposes?.DelayRequest?.Invoke(); return await SendAsync(chosenNode, command, sessionInfo, request, cts.Token).ConfigureAwait(false); } catch (OperationCanceledException e) @@ -2404,6 +2410,10 @@ internal TestingStuff(RequestExecutor requestExecutor) public Action ExecuteOnAllToFigureOutTheFastestOnBeforeWait; internal Action OnBeforeScheduleSpeedTest; + + internal Action DelayRequest; + + internal Action SetCommandTimeout; } } } diff --git a/test/SlowTests/Issues/RavenDB_19900.cs b/test/SlowTests/Issues/RavenDB_19900.cs new file mode 100644 index 00000000000..81de1cee3d2 --- /dev/null +++ b/test/SlowTests/Issues/RavenDB_19900.cs @@ -0,0 +1,61 @@ +using System.Threading.Tasks; +using Xunit; +using Xunit.Abstractions; +using System; +using System.Threading; +using FastTests; +using Raven.Client.Http; + + +namespace SlowTests.Issues +{ + public class RavenDB_19900 : RavenTestBase + { + public RavenDB_19900(ITestOutputHelper output) : base(output) + { + } + + [Fact] + public async Task UpdateTopologyTimeoutBehaviourShouldBeAccordingToConfiguration() + { + var generalTimeout = TimeSpan.FromSeconds(3); + var customCommandTimeout = TimeSpan.FromSeconds(1); + var delay = 2000; + + using (var store = GetDocumentStore()) + { + var executor = store.GetRequestExecutor(); + + try + { + // delaying response should result in UpdateTopology timeout + executor.ForTestingPurposesOnly().DelayRequest = () => { Thread.Sleep(delay); }; + executor.ForTestingPurposesOnly().SetCommandTimeout = (command) => + { + command.Timeout = customCommandTimeout; + }; + var e = await Assert.ThrowsAsync(async () => await UpdateTopology()); + Assert.Contains("failed with timeout after 00:00:01", e.ToString()); + + // increasing the general timeout should let UpdateTopology() to be executed + using (store.SetRequestTimeout(generalTimeout)) + { + await UpdateTopology(); + } + + async Task UpdateTopology() + { + var node = executor.GetPreferredNode().Result.Node; + var topologyUpdateCommand = new RequestExecutor.UpdateTopologyParameters(node); + await executor.UpdateTopologyAsync(topologyUpdateCommand); + } + } + finally + { + executor.ForTestingPurposes = null; + } + } + } + } +} +