Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement HedgingResilienceStrategy #1161

Merged
merged 5 commits into from
Apr 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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