diff --git a/src/MongoDB.Driver.Core/Core/Servers/RoundTripTimeMonitor.cs b/src/MongoDB.Driver.Core/Core/Servers/RoundTripTimeMonitor.cs index 1cb46817ed8..d1e270e2d7d 100644 --- a/src/MongoDB.Driver.Core/Core/Servers/RoundTripTimeMonitor.cs +++ b/src/MongoDB.Driver.Core/Core/Servers/RoundTripTimeMonitor.cs @@ -84,8 +84,6 @@ public void Dispose() public async Task RunAsync() { - await Task.Yield(); // return control immediately - while (!_cancellationToken.IsCancellationRequested) { try diff --git a/src/MongoDB.Driver.Core/Core/Servers/ServerMonitor.cs b/src/MongoDB.Driver.Core/Core/Servers/ServerMonitor.cs index 4206f3fb24d..1bd11b19f75 100644 --- a/src/MongoDB.Driver.Core/Core/Servers/ServerMonitor.cs +++ b/src/MongoDB.Driver.Core/Core/Servers/ServerMonitor.cs @@ -128,7 +128,14 @@ public void Initialize() { if (_state.TryChange(State.Initial, State.Open)) { - _ = MonitorServerAsync().ConfigureAwait(false); + // the call to Task.Factory.StartNew is not normally recommended or necessary + // we are using it temporarily to work around a race condition in some of our tests + // the issue is that we set up some mocked async methods to return results immediately synchronously + // which results in the MonitorServerAsync method making more progress synchronously than the test expected + // by using Task.Factory.StartNew we introduce a short delay before the MonitorServerAsync Task starts executing + // the delay is whatever time it takes for the new Task to be activated and scheduled + // and the delay is usually long enough for the test to get past the race condition (though not guaranteed) + _ = Task.Factory.StartNew(() => _ = MonitorServerAsync().ConfigureAwait(false)).ConfigureAwait(false); _ = _roundTripTimeMonitor.RunAsync().ConfigureAwait(false); } } @@ -189,8 +196,6 @@ private async Task InitializeConnectionAsync(CancellationToken canc private async Task MonitorServerAsync() { - await Task.Yield(); // return control immediately - var metronome = new Metronome(_serverMonitorSettings.HeartbeatInterval); var monitorCancellationToken = _monitorCancellationTokenSource.Token; diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Servers/HeartbeatDelayTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Servers/HeartbeatDelayTests.cs index c5caf2306f7..de8709375b7 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/Servers/HeartbeatDelayTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/Servers/HeartbeatDelayTests.cs @@ -14,6 +14,7 @@ */ using System; +using System.Diagnostics; using System.Threading; using System.Threading.Tasks; using FluentAssertions; @@ -38,13 +39,20 @@ public void Dispose_can_be_called_multiple_times() public void RequestHeartbeat_should_respect_to_minHeartbeatInterval([Values(10, -1)]int heartbeatIntervalInMinutes) { var heartbeatInterval = heartbeatIntervalInMinutes == -1 ? Timeout.InfiniteTimeSpan : TimeSpan.FromMinutes(heartbeatIntervalInMinutes); - var subject = new HeartbeatDelay(heartbeatInterval, TimeSpan.FromSeconds(2)); + var minHeartbeatInterval = TimeSpan.FromSeconds(2); + var stopwatch = Stopwatch.StartNew(); + var subject = new HeartbeatDelay(heartbeatInterval, minHeartbeatInterval); subject.RequestHeartbeat(); + var timeout = TimeSpan.FromMinutes(1); + var result = Task.WaitAny(subject.Task, Task.Delay(timeout)); + if (result != 0) + { + throw new Exception($"The test timeout {timeout} is exceeded."); + } + stopwatch.Stop(); - SpinWait.SpinUntil(() => subject.Task.Status != TaskStatus.WaitingForActivation, TimeSpan.FromMilliseconds(1500)).Should().BeFalse(); - Thread.Sleep(TimeSpan.FromMilliseconds(700)); - subject.Task.Status.Should().Be(TaskStatus.RanToCompletion); + stopwatch.Elapsed.Should().BeGreaterOrEqualTo(minHeartbeatInterval - TimeSpan.FromMilliseconds(15)); } [Fact]