public async Task ReplaceDocumentAsync(ReplaceOneModel <BsonDocument> model, CancellationToken token) { var sw = new Stopwatch(); sw.Start(); try { var result = await WithRetry.ExecuteAsync(3, TimeSpan.FromSeconds(5), () => _collection.ReplaceOneAsync(model.Filter, model.Replacement, new ReplaceOptions { BypassDocumentValidation = true }, token), e => e is MongoWriteException mwe && mwe.WriteError.Code == MongoErrorCodes.LockTimeout, token, e => _logger.Warning(e, "Failed replace with retryable exception")); sw.Stop(); _logger.Debug( result.ModifiedCount == 1 ? "Successfully retried replacement of document (ID: {Id}) in {Elapsed:N1} ms" : "Failed replacement of document (ID: {Id}) in {Elapsed:N1} ms. Document is missing", model.Replacement["_id"], sw.ElapsedMilliseconds); } catch (OperationCanceledException) { throw; } catch (Exception e) { throw new ReplaceOneException($"Failed to replace document: {e.Message}", e); } }
public async void Connect() { if (!IsConnected) { IsConnected = true; try { await WithRetry.InvokeAsync(_connectionRetryPolicy, () => { if (!IsNetworkAvailable) { Debug.WriteLine("NetworkUnavailableException"); throw new NetworkUnavailableException(); } _client = new UdpClient(); _client.Connect(_endPoint); }); Connected?.Invoke(this, EventArgs.Empty); } catch (Exception ex) { IsConnected = false; Debug.WriteLine(ex); throw; } } }
public async Task ExecuteAsync_ShouldThrowArgumentException_AttemptsIsLessOrEqualToZero(int attempts) { // Arrange var func = () => WithRetry.ExecuteAsync(attempts, TimeSpan.Zero, () => Task.FromResult(0), _ => true, default); // Act & Assert await func.Awaiting(f => f.Invoke()).Should().ThrowExactlyAsync <ArgumentException>(); }
public void ExecuteAsync_ShouldThrowExceptionFromFunction_WrappedFunctionThrowsException(int attempts) { // Arrange var exception = new Exception($"Attempt failed"); var func = () => WithRetry.ExecuteAsync(attempts, TimeSpan.Zero, () => Task.FromException <int>(exception), _ => true, default); // Act & Assert func.Awaiting(f => f.Invoke()).Should().Throw <Exception>().Which.Should().Be(exception); }
public async Task ExecuteAsync_ShouldInvokeCallbackOnRetry_WrappedFunctionFails() { // Arrange var onRetryMock = new Mock <Action <Exception> >(); var wrappedFunc = new Mock <Func <Task <int> > >(); wrappedFunc.SetupSequence(wf => wf()) .ThrowsAsync(new Exception()) .ReturnsAsync(42); // Act await WithRetry.ExecuteAsync(2, TimeSpan.Zero, wrappedFunc.Object, _ => true, default, onRetryMock.Object);
public async Task WithRetry_InvokeAsync() { var ac = new WithRetryTestClass(); WithRetry.SetDefaultOptions(new RetryOptions(3, TimeSpan.FromSeconds(15))); var fr2 = await WithRetry.InvokeAsync(async () => await ac.FunctionAsync(1)); await Assert.ThrowsExceptionAsync<ExceededMaxAttemptsException>(async () => { var fr = await WithRetry.InvokeAsync(() => ac.ExceptionAsync(1)); }); WithRetry.SetDefaultOptions(new RetryOptions(100000, TimeSpan.FromMilliseconds(10))); await Assert.ThrowsExceptionAsync<ExceededMaxWaitTimeException>(async () => { var fr = await WithRetry.InvokeAsync(() => ac.ExceptionAsync(1)); }); }
public void ExecuteAsync_ShouldRetryOnlyForSpecifiedException_ShouldRetryPredicateChecksException() { // Arrange var retryableException = new Exception("Retryable"); var notRetryableException = new Exception("Not Retryable"); var wrappedFunc = new Mock <Func <Task <int> > >(); wrappedFunc.SetupSequence(wf => wf()) .ThrowsAsync(retryableException) .ThrowsAsync(notRetryableException) .ReturnsAsync(42); var func = () => WithRetry.ExecuteAsync(4, TimeSpan.Zero, wrappedFunc.Object, e => e == retryableException, default); // Act & Assert func.Awaiting(f => f.Invoke()).Should().Throw <Exception>().Which.Should().Be(notRetryableException); }
public async Task WithRetry_Invoke() { var ac = new WithRetryTestClass(); WithRetry.SetDefaultPolicy(new RetryPolicy(3, TimeSpan.FromMinutes(15))); await WithRetry.InvokeAsync(() => ac.Action(1)); var fr1 = await WithRetry.InvokeAsync(() => ac.Function(1)); await Assert.ThrowsExceptionAsync <ExceededMaxAttemptsException>(async() => { var fr = await WithRetry.InvokeAsync(() => ac.Exception(1)); }); WithRetry.SetDefaultPolicy(new RetryPolicy(int.MaxValue, TimeSpan.FromMilliseconds(1))); await Assert.ThrowsExceptionAsync <ExceededMaxWaitTimeException>(async() => { var fr = await WithRetry.InvokeAsync(() => ac.Exception(1)); }); }
public async Task ExecuteAsync_ShouldReturnValue_WrappedFunctionSucceededOnLastAttempt(int attempts) { // Arrange var wrappedFunc = new Mock <Func <Task <int> > >(); var seq = wrappedFunc.SetupSequence(wf => wf()); for (var i = 1; i < attempts; i++) { seq.Throws <Exception>(); } seq.ReturnsAsync(42); // Act var actual = await WithRetry.ExecuteAsync(attempts, TimeSpan.Zero, wrappedFunc.Object, _ => true, default); // Assert actual.Should().Be(42); }
public async Task ExecuteAsync_ShouldRunFunctionExactNumberOfTimes_WrappedFunctionThrowsException(int attempts) { // Arrange var count = 0; // Act try { await WithRetry.ExecuteAsync(attempts, TimeSpan.Zero, () => Task.FromException <int>(new Exception($"Attempt {count++} failed")), _ => true, default); } catch (Exception) { // Discard thrown exception } // Assert count.Should().Be(attempts); }
public async Task ExecuteAsync_ShouldWaitBeforeEachAttempt_WrappedFunctionSucceededOnLastAttempt() { // Arrange var cooldown = TimeSpan.FromSeconds(2); var waitBetweenAttempts = new Stopwatch(); var wrappedFunc = () => { if (waitBetweenAttempts.IsRunning) { waitBetweenAttempts.Stop(); return(Task.FromResult(42)); } waitBetweenAttempts.Start(); return(Task.FromException <int>(new Exception("Attempt failed"))); }; // Act await WithRetry.ExecuteAsync(2, cooldown, wrappedFunc, _ => true, default); // Assert waitBetweenAttempts.Elapsed.Should().BeCloseTo(cooldown); }