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);
            }
        }
예제 #2
0
        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;
                }
            }
        }
예제 #3
0
    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>();
    }
예제 #4
0
    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);
    }
예제 #5
0
    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);
예제 #6
0
        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)); });
        }
예제 #7
0
    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);
    }
예제 #8
0
        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)); });
        }
예제 #9
0
    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);
    }
예제 #10
0
    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);
    }
예제 #11
0
    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);
    }