public Task ExplicitTripWorks() { var resilientOperation = ResilientOperation .From(() => { throw new InvalidOperationException(); #pragma warning disable CS0162 // Unreachable code detected return(Task.FromResult(42)); #pragma warning restore CS0162 // Unreachable code detected }) .WhenExceptionIs <InvalidOperationException>(async(op, ex) => { if (op.CurrentAttempt <= 3) { await op.RetryAfterAsync(TimeSpan.FromMilliseconds(100)); } else { op.DefaultCircuitBreaker.Trip(ex, TimeSpan.FromDays(1)); } }) .GetOperation(); return(Assert.ThrowsAsync <CircuitBrokenException>(async() => await resilientOperation(CancellationToken.None))); }
public Task CircuitBreaksAfterConsecutiveFailures() { var resilientOperation = ResilientOperation .From(() => { throw new Exception(); #pragma warning disable CS0162 // Unreachable code detected return(Task.FromResult(42)); #pragma warning restore CS0162 // Unreachable code detected }) .WithCircuitBreaker( operationKey: nameof(CircuitBreaksAfterConsecutiveFailures), onMissingFactory: () => new CircuitBreaker(new ConsecutiveFailureCircuitBreakerStrategy(3))) .WhenExceptionIs <Exception>(async(op, ex) => { if (op.CurrentAttempt <= 3) { await op.RetryAfterAsync(TimeSpan.FromMilliseconds(100)); } }) .GetOperation(); return(Assert.ThrowsAsync <CircuitBrokenException>(async() => await resilientOperation(CancellationToken.None))); }
public async Task TimeoutExceptionRetried() { var retryWasHit = false; var resilientOperation = ResilientOperation.From(async() => { await Task.Delay(100); return(42); }) .TimeoutAfter(TimeSpan.FromMilliseconds(20)) .WhenExceptionIs <TimeoutException>((op, ex) => { if (op.Total.AttemptsExhausted == 0) { retryWasHit = true; op.Retry(); } return(Task.CompletedTask); }) .GetOperation(); await Assert.ThrowsAsync <TimeoutException>(() => resilientOperation(CancellationToken.None)); Assert.True(retryWasHit); }
public async Task ResilientFunctionReturnsAfterRetries() { int failureCount = 0; var resilientOperation = ResilientOperation.From(() => { // Fail 3 times before succeeding if (failureCount < 3) { failureCount++; throw new Exception(); } return(42); }) .WhenExceptionIs <Exception>(async(op, ex) => { if (op.Total.AttemptsExhausted < 3) { await op.WaitThenRetryAsync(TimeSpan.FromMilliseconds(100)); } }) .GetOperation(); Assert.Equal(42, await resilientOperation(CancellationToken.None)); }
public Task TimeoutExceptionIsThrown() { var resilientOperation = ResilientOperation.From(() => Task.Delay(100)) .TimeoutAfter(TimeSpan.FromMilliseconds(20)) .GetOperation(); return(Assert.ThrowsAsync <TimeoutException>(() => resilientOperation(CancellationToken.None))); }
public async Task ExceptionHandlersIncrementBackoff() { var backoffStrategy = Backoff .LinearlyFrom(TimeSpan.FromMilliseconds(250)); int failureCount = 0; var resilientOperation = ResilientOperation.From(() => { // Fail 3 times before succeeding if (failureCount < 3) { failureCount++; throw new Exception(); } if (failureCount < 4) { failureCount++; return(Task.FromResult(5)); } return(Task.FromResult(42)); }) .WhenExceptionIs <Exception>(async(op, ex) => { if (op.CurrentAttempt <= 3) { await op.RetryAfterAsync(backoffStrategy.Next()); } }) .WhenResult(value => value != 42, async(op, value) => { if (op.CurrentAttempt <= 4) { await op.RetryAfterAsync(backoffStrategy.Next()); } }) .WhenResult(value => value == 42, (op, ex) => { op.Return(0); return(Task.CompletedTask); }) .GetOperation(); Assert.Equal(0, await resilientOperation(CancellationToken.None)); Assert.Equal(4, failureCount); Assert.Equal(TimeSpan.FromMilliseconds(250 * 5), backoffStrategy.Next()); }
public Task ThrowsOnceRetryHandlersAreExhausted() { var resilientOperation = ResilientOperation.From(() => { throw new Exception(); #pragma warning disable CS0162 // Unreachable code detected return(Task.FromResult(42)); #pragma warning restore CS0162 // Unreachable code detected }) .WhenExceptionIs <Exception>(async(op, ex) => { if (op.CurrentAttempt <= 3) { await op.RetryAfterAsync(TimeSpan.FromMilliseconds(100)); } }) .GetOperation(); return(Assert.ThrowsAsync <Exception>(async() => await resilientOperation(CancellationToken.None))); }