Skip to content

Commit

Permalink
Resiliency config added
Browse files Browse the repository at this point in the history
  • Loading branch information
aritchie committed Sep 22, 2024
1 parent 3eb6f65 commit feec966
Show file tree
Hide file tree
Showing 3 changed files with 78 additions and 30 deletions.
12 changes: 3 additions & 9 deletions samples/Sample/MauiProgram.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using Microsoft.Extensions.Configuration;
using Polly;

Expand Down Expand Up @@ -48,15 +49,8 @@ public static MauiApp CreateMauiApp()
.AddPrismSupport()
.AddDataAnnotations()
// TODO: don't add both
// .AddFluentValidation()
.AddResiliencyMiddleware(
("Test", builder =>
{
// builder.AddRetry(new RetryStrategyOptions());
builder.AddTimeout(TimeSpan.FromSeconds(2.0));
})
)
// .AddFluentValidation() // don't add both
.AddResiliencyMiddleware(builder.Configuration)
.AddMemoryCaching(y =>
{
y.ExpirationScanFrequency = TimeSpan.FromSeconds(5);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,25 +1,12 @@
using System.Reflection;
using Microsoft.Extensions.Configuration;
using Polly;
using Polly.Registry;
using Shiny.Mediator.Infrastructure;

namespace Shiny.Mediator.Resilience.Handlers;

/*
services.AddResiliencePipeline(key, static builder =>
{
// See: https://www.pollydocs.org/strategies/retry.html
builder.AddRetry(new RetryStrategyOptions
{
ShouldHandle = new PredicateBuilder().Handle<TimeoutRejectedException>()
});
// See: https://www.pollydocs.org/strategies/timeout.html
builder.AddTimeout(TimeSpan.FromSeconds(1.5));
});
*/
//https://learn.microsoft.com/en-us/dotnet/core/resilience/?tabs=dotnet-cli
//https://devblogs.microsoft.com/dotnet/building-resilient-cloud-services-with-dotnet-8/

public class ResilientRequestHandlerMiddleware<TRequest, TResult>(
IConfiguration configuration,
ResiliencePipelineProvider<string> pipelineProvider
Expand All @@ -32,16 +19,23 @@ public async Task<TResult> Process(
CancellationToken cancellationToken
)
{
// TODO: configuration should also setup resilience pipelines at startup?
ResiliencePipeline? pipeline = null;
var section = configuration.GetHandlerSection("Resilience", request!, requestHandler);

var attribute = requestHandler.GetHandlerHandleMethodAttribute<TRequest, ResilientAttribute>();
attribute ??= request.GetType().GetCustomAttribute<ResilientAttribute>();
if (attribute == null)
if (section != null)
{
pipeline = pipelineProvider.GetPipeline(section.Key.ToLower());
}
else
{
var attribute = requestHandler.GetHandlerHandleMethodAttribute<TRequest, ResilientAttribute>();
attribute ??= request.GetType().GetCustomAttribute<ResilientAttribute>();
if (attribute == null)
pipeline = pipelineProvider.GetPipeline(attribute.ConfigurationKey.ToLower());
}
if (pipeline == null)
return await next().ConfigureAwait(false);

var pipeline = pipelineProvider.GetPipeline(attribute.ConfigurationKey.ToLower());

// it can't cancel properly here... may need to make next take a CancellationToken
var result = await pipeline
.ExecuteAsync(async _ => await next(), cancellationToken)
Expand Down
60 changes: 60 additions & 0 deletions src/Shiny.Mediator.Resilience/ResilientExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
using Microsoft.Extensions.Configuration;
using Polly;
using Polly.CircuitBreaker;
using Polly.Retry;
using Polly.Timeout;
using Shiny.Mediator.Resilience.Handlers;

namespace Shiny.Mediator;
Expand All @@ -14,4 +18,60 @@ public static ShinyConfigurator AddResiliencyMiddleware(this ShinyConfigurator c
configurator.AddOpenRequestMiddleware(typeof(ResilientRequestHandlerMiddleware<,>));
return configurator;
}


public static ShinyConfigurator AddResiliencyMiddleware(this ShinyConfigurator configurator, IConfiguration configuration)
{

/*
"*": {
"RetryCount": 3,
"RetryDelay": 2000,
"CircuitBreakerCount": 5,
"CircuitBreakerDelay": 5000
}
*/
var items = configuration.GetSection("Resilience").Get<Dictionary<string, ResilienceConfig>>();
if (items != null)
{
foreach (var item in items)
{
configurator.Services.AddResiliencePipeline(item.Key.ToLower(), builder =>
{
if (item.Value.MaxRetries != null)
{
var strategy = new RetryStrategyOptions
{
MaxRetryAttempts = item.Value.MaxRetries.Value,
UseJitter = item.Value.UseJitter,
ShouldHandle = new PredicateBuilder().Handle<TimeoutRejectedException>()
};
if (item.Value.RetryDelay != null)
strategy.Delay = TimeSpan.FromSeconds(item.Value.RetryDelay.Value);
if (item.Value.BackoffType != null)
strategy.BackoffType = item.Value.BackoffType.Value;
builder.AddRetry(strategy);
}
if (item.Value.TimeoutMilliseconds != null)
builder.AddTimeout(TimeSpan.FromMilliseconds(item.Value.TimeoutMilliseconds.Value));
// builder.AddCircuitBreaker(new CircuitBreakerStrategyOptions())
});
}
}
return configurator;
}
}


public class ResilienceConfig
{
public double? TimeoutMilliseconds { get; init; }
public int? MaxRetries { get; init; }
public int? RetryDelay { get; init; }
public DelayBackoffType? BackoffType { get; init; }
public bool UseJitter { get; init; }
}

0 comments on commit feec966

Please sign in to comment.