Skip to content

Instantly share code, notes, and snippets.

@jacobduijzer
Last active April 18, 2019 16:57
Show Gist options
  • Select an option

  • Save jacobduijzer/9d2fc59a0b30a96c451f9609292d27dd to your computer and use it in GitHub Desktop.

Select an option

Save jacobduijzer/9d2fc59a0b30a96c451f9609292d27dd to your computer and use it in GitHub Desktop.
Polly samples
public class AuthenticationService : IAuthenticationService
{
private static readonly int NUMBER_OF_RETRIES = 3;
private readonly IAuthenticationApi _authenticationApi;
private readonly string _clientId;
private readonly string _clientSecret;
public AuthenticationService(
IAuthenticationApi authenticationApi,
string clientId,
string clientSecret
)
{
Guard.Against.Null(authenticationApi, nameof(authenticationApi));
Guard.Against.NullOrEmpty(clientId, nameof(clientId));
Guard.Against.NullOrEmpty(clientSecret, nameof(clientSecret));
_authenticationApi = authenticationApi;
_clientId = clientId;
_clientSecret = clientSecret;
}
public async Task<AuthenticationResult> GetAccessToken() =>
await Policy
.Handle<ApiException>(ex => ex.StatusCode == HttpStatusCode.RequestTimeout)
.RetryAsync(NUMBER_OF_RETRIES, async (exception, retryCount) => await Task.Delay(500))
.ExecuteAsync(async () => await _authenticationApi.GetAccessToken(_clientId, _clientSecret).ConfigureAwait(false))
.ConfigureAwait(false);
}
[Fact]
public async Task RetryWhenReceivingTimeout()
{
// ARRANGE
var mockApi = new Mock<IAuthenticationApi>(MockBehavior.Strict);
mockApi.SetupSequence(x => x.GetAccessToken(CORRECT_CLIENT_ID, CORRECT_CLIENT_SECRET))
.Throws(TestHelper.CreateRefitException(HttpMethod.Post, HttpStatusCode.RequestTimeout))
.Throws(TestHelper.CreateRefitException(HttpMethod.Post, HttpStatusCode.RequestTimeout))
.ReturnsAsync(new AuthenticationResult { access_token = TEST_ACCESS_TOKEN });
var service = new AuthenticationService(mockApi.Object, CORRECT_CLIENT_ID, CORRECT_CLIENT_SECRET);
// ACT
var authResult = await service.GetAccessToken().ConfigureAwait(false);
// ASSERT
authResult.Should().NotBeNull();
authResult.access_token.Should().NotBeNullOrEmpty().And.Be(TEST_ACCESS_TOKEN);
// The API Should have called GetAccessToken exactly 3 times: 2 times it received an exception,
// the third time a correct result
mockApi.Verify(x => x.GetAccessToken(It.IsAny<string>(), It.IsAny<string>()), Times.Exactly(3));
}
public OrderService(IOrderApi orderApi, IQueueService queueService)
{
_orderApi = orderApi;
_queueService = queueService;
_circuitBreaker = Policy
.Handle<Exception>()
.CircuitBreakerAsync(EXCEPTIONS_ALLOWED_BEFORE_BREAKING_CIRCUIT,TimeSpan.FromMilliseconds(5000));
}
public async Task SaveOrder(Order order)
{
var retryPolicy = Policy
.Handle<ApiException>(ex => ex.StatusCode == HttpStatusCode.RequestTimeout)
.RetryAsync(NUMBER_OF_RETRIES, async (exception, retryCount) => await Task.Delay(500)
.ConfigureAwait(false));
var fallbackPolicy = Policy
.Handle<Exception>()
.FallbackAsync(async (cancellationToken) => await SaveOrderInQueue(order)
.ConfigureAwait(false));
await fallbackPolicy
.WrapAsync(retryPolicy)
.WrapAsync(_circuitBreaker)
.ExecuteAsync(async () => await _orderApi.SaveOrder(order).ConfigureAwait(false))
.ConfigureAwait(false);
}
[Fact]
public async Task BreakAfter10Tries()
{
var mockOrderApi = new Mock<IOrderApi>(MockBehavior.Strict);
mockOrderApi.Setup(x => x.SaveOrder(It.IsAny<Order>()))
.Throws(TestHelper.CreateRefitException(HttpMethod.Post, HttpStatusCode.RequestTimeout));
var orderService = new OrderService(mockOrderApi.Object, _mockQueueService.Object, _testLogger);
for (int i = 0; i <= 10; i++)
await orderService.SaveOrder(TestHelper.CreateFakeOrder(3)).ConfigureAwait(false);
mockOrderApi.Verify(x => x.SaveOrder(It.IsAny<Order>()), Times.AtLeast(10));
_mockQueueService.Verify(x => x.SaveOrder(It.IsAny<Order>()), Times.AtLeast(10));
}
public async Task<List<Product>> GetProducts()
{
if (_authenticationResult == null)
_authenticationResult = await _authenticationService.GetAccessToken().ConfigureAwait(false);
var unauthorizedPolicy = Policy
.Handle<ApiException>(ex => ex.StatusCode == HttpStatusCode.Unauthorized)
.RetryAsync(async (exception, retryCount) =>
{
_authenticationResult = await _authenticationService.GetAccessToken().ConfigureAwait(false);
});
var timeoutPolicy = Policy
.Handle<ApiException>(ex => ex.StatusCode == HttpStatusCode.RequestTimeout)
.RetryAsync(NUMBER_OF_RETRIES, async (exception, retryCount) =>
await Task.Delay(300).ConfigureAwait(false));
return await unauthorizedPolicy
.WrapAsync(timeoutPolicy)
.ExecuteAsync(async () => await _productsApi.GetProductsAsync(_authenticationResult.access_token))
.ConfigureAwait(false);
}
public async Task SaveOrder(Order order)
{
var retryPolicy = Policy
.Handle<ApiException>(ex => ex.StatusCode == HttpStatusCode.RequestTimeout)
.RetryAsync(NUMBER_OF_RETRIES, async (exception, retryCount) => await Task.Delay(500).ConfigureAwait(false));
var fallbackPolicy = Policy
.Handle<Exception>()
.FallbackAsync(async (cancellationToken) => await SaveOrderInQueue(order).ConfigureAwait(false));
await fallbackPolicy
.WrapAsync(retryPolicy)
.ExecuteAsync(async () => await _orderApi.SaveOrder(order).ConfigureAwait(false))
.ConfigureAwait(false);
}
private async Task SaveOrderInQueue(Order order) =>
await _queueService.SaveOrder(order)
.ConfigureAwait(false);
[Fact]
public async Task HandleFallbackOnMultipleTimeouts()
{
var mockOrderApi = new Mock<IOrderApi>(MockBehavior.Strict);
mockOrderApi
.SetupSequence(x => x.SaveOrder(It.IsAny<Order>()))
.Throws(TestHelper.CreateRefitException(HttpMethod.Post, HttpStatusCode.RequestTimeout))
.Throws(TestHelper.CreateRefitException(HttpMethod.Post, HttpStatusCode.RequestTimeout))
.Throws(TestHelper.CreateRefitException(HttpMethod.Post, HttpStatusCode.RequestTimeout));
var orderService = new OrderService(mockOrderApi.Object, _mockQueueService.Object);
await orderService.SaveOrder(TestHelper.CreateFakeOrder(3)).ConfigureAwait(false);
mockOrderApi.Verify(x => x.SaveOrder(It.IsAny<Order>()), Times.Exactly(4));
_mockQueueService.Verify(x => x.SaveOrder(It.IsAny<Order>()), Times.Once);
}
return await Policy
// When recieving a ApiException with status code 408
.Handle<ApiException>(ex => ex.StatusCode == HttpStatusCode.RequestTimeout)
// Retry NUMBER_OF_TIMES but execute some code before retrying
.RetryAsync(NUMBER_OF_RETRIES, async (exception, retryCount) =>
{
// wait a while
await Task.Delay(300).ConfigureAwait(false);
})
// execute the command
.ExecuteAsync(async () => await _productsApi.GetProductsAsync(_authenticationResult.access_token))
.ConfigureAwait(false);
public class ProductService : IProductService
{
private readonly IProductsApi _productsApi;
public ProductService(IProductsApi productsApi) => _productsApi = productsApi;
public async Task<Product> GetProducts() =>
await _productsApi
.GetProducts()
.ConfigureAwait(false);
}
public async Task<List<Product>> GetProducts()
{
int tryCount = 0;
while(tryCount < 3)
{
try
{
tryCount++;
return await GetProductsAsync().ConfigureAwait(false);
}
catch (System.Exception)
{
await Task.Delay(500);
}
}
throw new Exception("Something went wrong");
}
private async Task<List<Product>> GetProductsAsync() =>
await _productsApi
.GetProductsAsync()
.ConfigureAwait(false);
public async Task<List<Product>> GetProducts()
{
if(_authenticationResult == null)
_authenticationResult = await _authenticationService.GetAccessToken().ConfigureAwait(false);
int tryCount = 0;
while(tryCount < 3)
{
try
{
tryCount++;
return await GetProductsAsync(_authenticationResult.access_token).ConfigureAwait(false);
}
catch (ApiException ex)
{
switch(ex.StatusCode)
{
case System.Net.HttpStatusCode.RequestTimeout:
await Task.Delay(500);
break;
case System.Net.HttpStatusCode.Unauthorized:
_authenticationResult = await _authenticationService.GetAccessToken().ConfigureAwait(false);
break;
}
}
catch(Exception ex)
{
}
}
throw new Exception("Something went wrong");
}
private async Task<List<Product>> GetProductsAsync(string accessToken) =>
await _productsApi
.GetProductsAsync(accessToken)
.ConfigureAwait(false);
public class ProductService : IProductService
{
private readonly IProductsApi _productsApi;
private readonly IAuthenticationService _authenticationService;
private AuthenticationResult _authenticationResult;
public ProductService(IProductsApi productsApi, IAuthenticationService authenticationService)
{
_productsApi = productsApi;
_authenticationService = authenticationService;
}
private static readonly int NUMBER_OF_RETRIES = 3;
public async Task<List<Product>> GetProducts()
{
if (_authenticationResult == null)
_authenticationResult = await _authenticationService.GetAccessToken().ConfigureAwait(false);
return await Policy
.Handle<Exception>()
.RetryAsync(NUMBER_OF_RETRIES)
.ExecuteAsync(async () => await _productsApi.GetProductsAsync(_authenticationResult.access_token))
.ConfigureAwait(false);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment