Skip to content

Commit

Permalink
Implement HedgingResilienceStrategy
Browse files Browse the repository at this point in the history
  • Loading branch information
martintmk committed Apr 28, 2023
1 parent 38ae8fd commit 60f4113
Show file tree
Hide file tree
Showing 39 changed files with 2,728 additions and 43 deletions.
1 change: 1 addition & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,4 @@ updates:
- dependency-name: "Microsoft.Extensions.Logging"
- dependency-name: "System.Diagnostics.DiagnosticSource"
- dependency-name: "System.Threading.RateLimiting"
- dependency-name: "Microsoft.Bcl.AsyncInterfaces"
3 changes: 2 additions & 1 deletion src/Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
<PackageVersion Include="BenchmarkDotNet" Version="0.13.5" />
<PackageVersion Include="FluentAssertions" Version="6.10.0" />
<PackageVersion Include="GitHubActionsTestLogger" Version="2.0.1" />
<PackageVersion Include="Microsoft.Bcl.AsyncInterfaces" Version="1.0.0" />
<PackageVersion Include="Microsoft.CodeAnalysis.NetAnalyzers" Version="7.0.1" />
<PackageVersion Include="Microsoft.Extensions.Caching.Memory" Version="7.0.0" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="7.0.0" />
Expand Down Expand Up @@ -34,4 +35,4 @@
<PackageVersion Include="Microsoft.Extensions.Options" Version="2.2.0" />
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="2.2.0" />
</ItemGroup>
</Project>
</Project>
42 changes: 42 additions & 0 deletions src/Polly.Core.Benchmarks/Benchmarks/HedgingBenchmark.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
using System.Threading.Tasks;
using BenchmarkDotNet.Attributes;
using Polly.Core.Benchmarks;

namespace Polly.Benchmarks;

public class HedgingBenchmark
{
private ResilienceStrategy? _strategy;

[GlobalSetup]
public void Setup()
{
_strategy = Helper.CreateHedging();
}

[Benchmark(Baseline = true)]
public async ValueTask Hedging_Primary()
=> await _strategy!.ExecuteValueTaskAsync(static _ => new ValueTask<string>("primary")).ConfigureAwait(false);

[Benchmark]
public async ValueTask Hedging_Secondary()
=> await _strategy!.ExecuteValueTaskAsync(static _ => new ValueTask<string>(Helper.Failure)).ConfigureAwait(false);

[Benchmark]
public async ValueTask Hedging_Primary_AsyncWork()
=> await _strategy!.ExecuteValueTaskAsync(
static async _ =>
{
await Task.Yield();
return "primary";
}).ConfigureAwait(false);

[Benchmark]
public async ValueTask Hedging_Secondary_AsyncWork()
=> await _strategy!.ExecuteValueTaskAsync(
static async _ =>
{
await Task.Yield();
return Helper.Failure;
}).ConfigureAwait(false);
}
23 changes: 23 additions & 0 deletions src/Polly.Core.Benchmarks/Internals/Helper.Hedging.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
using Polly.Hedging;

namespace Polly.Core.Benchmarks;

internal static partial class Helper
{
public const string Failure = "failure";

public static ResilienceStrategy CreateHedging()
{
return CreateStrategy(builder =>
{
builder.AddHedging(new HedgingStrategyOptions
{
Handler = new HedgingHandler().SetHedging<string>(handler =>
{
handler.ShouldHandle.HandleResult(Failure);
handler.HedgingActionGenerator = args => () => Task.FromResult("hedged response");
})
});
});
}
}
9 changes: 9 additions & 0 deletions src/Polly.Core.Benchmarks/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,3 +60,12 @@ LaunchCount=2 WarmupCount=10
|--------------------------- |---------:|----------:|----------:|------:|-------:|----------:|------------:|
| ExecuteStrategyPipeline_V7 | 1.523 us | 0.0092 us | 0.0137 us | 1.00 | 0.3433 | 2872 B | 1.00 |
| ExecuteStrategyPipeline_V8 | 1.276 us | 0.0128 us | 0.0191 us | 0.84 | 0.0114 | 96 B | 0.03 |

## HEDGING

| Method | Mean | Error | StdDev | Ratio | RatioSD | Gen0 | Gen1 | Allocated | Alloc Ratio |
|---------------------------- |-----------:|----------:|----------:|------:|--------:|-------:|-------:|----------:|------------:|
| Hedging_Primary | 891.3 ns | 39.39 ns | 58.96 ns | 1.00 | 0.00 | 0.0048 | - | 40 B | 1.00 |
| Hedging_Secondary | 1,500.0 ns | 7.88 ns | 11.80 ns | 1.69 | 0.11 | 0.0229 | - | 200 B | 5.00 |
| Hedging_Primary_AsyncWork | 4,250.9 ns | 140.89 ns | 206.52 ns | 4.78 | 0.34 | 0.1831 | 0.0305 | 1518 B | 37.95 |
| Hedging_Secondary_AsyncWork | 6,544.9 ns | 99.90 ns | 143.27 ns | 7.34 | 0.39 | 0.2213 | 0.0839 | 1872 B | 46.80 |
36 changes: 36 additions & 0 deletions src/Polly.Core.Tests/Hedging/Controller/HedgingControllerTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
using Polly.Hedging;
using Polly.Hedging.Utils;

namespace Polly.Core.Tests.Hedging.Controller;

public class HedgingControllerTests
{
[Fact]
public async Task Pooling_Ok()
{
var handler = new HedgingHandler().SetHedging<int>(handler => handler.HedgingActionGenerator = args => null).CreateHandler();
var controller = new HedgingController(new HedgingTimeProvider(), handler!, 3);

var context1 = controller.GetContext(ResilienceContext.Get());
await PrepareAsync(context1);

var context2 = controller.GetContext(ResilienceContext.Get());
await PrepareAsync(context2);

controller.RentedContexts.Should().Be(2);
controller.RentedExecutions.Should().Be(2);

context1.Complete();
context2.Complete();

controller.RentedContexts.Should().Be(0);
controller.RentedExecutions.Should().Be(0);
}

private static async Task PrepareAsync(HedgingExecutionContext context)
{
await context.LoadExecutionAsync((_, _) => new ValueTask<int>(10), "state");
await context.TryWaitForCompletedExecutionAsync(HedgingStrategyOptions.InfiniteHedgingDelay);
context.Tasks[0].AcceptOutcome();
}
}
Loading

0 comments on commit 60f4113

Please sign in to comment.