Skip to content

Instantly share code, notes, and snippets.

@aidapsibr
Last active December 22, 2020 21:05
Show Gist options
  • Save aidapsibr/978348c3e4bd65e9f4dad7c7fa9044cb to your computer and use it in GitHub Desktop.
Save aidapsibr/978348c3e4bd65e9f4dad7c7fa9044cb to your computer and use it in GitHub Desktop.
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