Last active
December 22, 2020 21:05
-
-
Save aidapsibr/978348c3e4bd65e9f4dad7c7fa9044cb to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using Xunit; | |
using System.Threading; | |
using System.Threading.Tasks; | |
namespace Resiliency.Tests.Functions | |
{ | |
using static ResilientOperation; | |
public class ResilientFunctionsReturnTests | |
{ | |
public ResilientFunctionsReturnTests() | |
{ | |
ResilientOperation.WaiterFactory = (cancellationToken) => new FakeWaiter(cancellationToken); | |
} | |
[Fact] | |
public async Task ResilientFunctionReturnsAfterRetries() | |
{ | |
SomeSdk sdk = new(); | |
var resilientOperation = sdk.GetValueAsync(CancellationToken.None); | |
Assert.Equal(200 /*OK*/, await resilientOperation); | |
} | |
} | |
public class SomeSdk | |
{ | |
public CircuitBreaker SdkCircuitBreaker {get; } = new(new ExplicitTripCircuitBreakerStrategy()); | |
public async Task<int> GetValueAsync(CancellationToken cancellationToken) | |
{ | |
// you wouldn't really track failures in your method, just useful to simulate transient behavior | |
int failureCount = 0; | |
return await TryAsync(GetValueInternalAsync, BuildSdkResiliencyHandler, cancellationToken).ConfigureAwait(false); | |
Task<int> GetValueInternalAsync(CancellationToken cancellationToken) | |
{ | |
cancellationToken.ThrowIfCancellationRequested(); | |
// Fail 3 times before succeeding | |
if (failureCount < 3) | |
{ | |
failureCount++; | |
throw new ThrottledException(); | |
} | |
if (failureCount < 4) | |
{ | |
failureCount++; | |
return Task.FromResult(503) /*service unavailable*/; | |
} | |
return Task.FromResult(200) /*OK*/; | |
} | |
} | |
public void BuildSdkResiliencyHandler(ResilientFuncBuilder<Func<CancellationToken, Task<int>>, int> handlers) | |
{ | |
var throttledStrategy = Backoff.ExponentiallyFrom(TimeSpan.FromMilliseconds(100)).WithDecorrelatedJitter(); | |
var systemRecoveryStrategy = Backoff.Constant(TimeSpan.FromSeconds(30)).WithDecorrelatedJitter(); | |
handlers | |
.AddCircuitBreaker(SdkCircuitBreaker) | |
.Catch<ThrottledException>(async (op, ex) => | |
{ | |
if (op.CurrentAttempt < 4) | |
{ | |
await op.RetryAfterAsync(throttledStrategy.Next()); | |
} | |
}) | |
.CatchResult(when: result => result == 503 /*service unavailable*/, (op, result) => | |
{ | |
SdkCircuitBreaker.Trip(new ServiceUnavailableException(), systemRecoveryStrategy.Next()); | |
return Task.CompletedTask; | |
}) | |
.Catch<CircuitBrokenException>(async (op, ex) => | |
{ | |
await op.RetryAfterAsync(ex.MinimumRetryPeriod); | |
}); | |
} | |
} | |
public class ThrottledException : Exception | |
{ | |
public ThrottledException() | |
{ | |
} | |
} | |
public class ServiceUnavailableException : Exception | |
{ | |
public ServiceUnavailableException() | |
{ | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment