Skip to content

Commit

Permalink
Merge pull request #912 from App-vNext/v723-or-v730
Browse files Browse the repository at this point in the history
v7.3.0: Rate-limit policy and codebase maintenance
  • Loading branch information
martincostello authored Jan 17, 2022
2 parents 509b34f + 15a51d1 commit c27faf8
Show file tree
Hide file tree
Showing 159 changed files with 4,879 additions and 2,648 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ UpgradeLog*.XML

artifacts
build
BenchmarkDotNet.Artifacts
tools

*.lock.json
Expand Down
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
## 7.3.0

- Add RateLimit policy - Thanks to [@reisenberger](https://github.com/reisenberger)
- Various codebase health updates - Thanks to [@SimonCropp](https://github.com/SimonCropp)
- Add benchmarks project - Thanks to [@martincostello](https://github.com/martincostello)
- Fix broken README image - Thanks to [@GitHubPang](https://github.com/GitHubPang)

## 7.2.2

- Recursively search all `AggregateException` inner exceptions for predicate matches when using `HandleInner()` ([#818](https://github.com/App-vNext/Polly/issues/818)) - Thanks to [@sideproject](https://github.com/sideproject)
Expand Down
2 changes: 1 addition & 1 deletion GitVersionConfig.yaml
Original file line number Diff line number Diff line change
@@ -1 +1 @@
next-version: 7.2.2
next-version: 7.3.0
296 changes: 179 additions & 117 deletions README.md

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion appveyor.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
os: Visual Studio 2019
os: Visual Studio 2022

# Build script
build_script:
Expand Down
4 changes: 2 additions & 2 deletions build.cake
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ var configuration = Argument<string>("configuration", "Release");
// EXTERNAL NUGET LIBRARIES
//////////////////////////////////////////////////////////////////////

#addin "Cake.FileHelpers"
#addin nuget:?package=Cake.Yaml
#addin nuget:?package=Cake.FileHelpers&version=3.3.0
#addin nuget:?package=Cake.Yaml&version=3.1.1
#addin nuget:?package=YamlDotNet&version=5.2.1

///////////////////////////////////////////////////////////////////////////////
Expand Down
2 changes: 1 addition & 1 deletion build.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ if(-Not $SkipToolPackageRestore.IsPresent)
# Install just Cake if missing config
else
{
$NuGetOutput = Invoke-Expression "&`"$NUGET_EXE`" install Cake -Version 0.25.0 -ExcludeVersion" # Pin Cake version to 0.25.0; see https://github.com/App-vNext/Polly/issues/416
$NuGetOutput = Invoke-Expression "&`"$NUGET_EXE`" install Cake -Version 0.38.5 -ExcludeVersion" # Pin Cake version to 0.38.5; see https://github.com/App-vNext/Polly/issues/416
Write-Verbose ($NuGetOutput | Out-String)
}
Pop-Location
Expand Down
1 change: 1 addition & 0 deletions src/Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@
<PropertyGroup>
<AssemblyOriginatorKeyFile>..\Polly.snk</AssemblyOriginatorKeyFile>
<SignAssembly>true</SignAssembly>
<LangVersion>latest</LangVersion>
</PropertyGroup>
</Project>
49 changes: 49 additions & 0 deletions src/Polly.Benchmarks/Bulkhead.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
using System.Threading;
using System.Threading.Tasks;
using BenchmarkDotNet.Attributes;

namespace Polly.Benchmarks
{
[Config(typeof(PollyConfig))]
public class Bulkhead
{
private static readonly Policy SyncPolicy = Policy.Bulkhead(2);
private static readonly AsyncPolicy AsyncPolicy = Policy.BulkheadAsync(2);

[Benchmark]
public void Bulkhead_Synchronous()
{
SyncPolicy.Execute(() => Workloads.Action());
}

[Benchmark]
public async Task Bulkhead_Asynchronous()
{
await AsyncPolicy.ExecuteAsync(() => Workloads.ActionAsync());
}

[Benchmark]
public async Task Bulkhead_Asynchronous_With_CancellationToken()
{
await AsyncPolicy.ExecuteAsync((token) => Workloads.ActionAsync(token), CancellationToken.None);
}

[Benchmark]
public int Bulkhead_Synchronous_With_Result()
{
return SyncPolicy.Execute(() => Workloads.Func<int>());
}

[Benchmark]
public async Task<int> Bulkhead_Asynchronous_With_Result()
{
return await AsyncPolicy.ExecuteAsync(() => Workloads.FuncAsync<int>());
}

[Benchmark]
public async Task<int> Bulkhead_Asynchronous_With_Result_With_CancellationToken()
{
return await AsyncPolicy.ExecuteAsync((token) => Workloads.FuncAsync<int>(token), CancellationToken.None);
}
}
}
111 changes: 111 additions & 0 deletions src/Polly.Benchmarks/Cache.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using BenchmarkDotNet.Attributes;
using Microsoft.Extensions.Caching.Memory;
using Polly.Caching;

namespace Polly.Benchmarks
{
[Config(typeof(PollyConfig))]
public class Cache
{
private static readonly MemoryCache MemoryCache = new MemoryCache(new MemoryCacheOptions());
private static readonly MemoryCacheProvider CacheProvider = new MemoryCacheProvider(MemoryCache);

private static readonly Policy SyncPolicyMiss = Policy.Cache(CacheProvider, TimeSpan.Zero);
private static readonly AsyncPolicy AsyncPolicyMiss = Policy.CacheAsync(CacheProvider, TimeSpan.Zero);

private static readonly Policy SyncPolicyHit = Policy.Cache(CacheProvider, TimeSpan.MaxValue);
private static readonly AsyncPolicy AsyncPolicyHit = Policy.CacheAsync(CacheProvider, TimeSpan.MaxValue);

private static readonly Context HitContext = new Context(nameof(HitContext));
private static readonly Context MissContext = new Context(nameof(MissContext));

[GlobalSetup]
public async Task GlobalSetup()
{
SyncPolicyHit.Execute((context) => GetObject(), HitContext);
await AsyncPolicyHit.ExecuteAsync((context, token) => GetObjectAsync(token), HitContext, CancellationToken.None);
}

[Benchmark]
public object Cache_Synchronous_Hit()
{
return SyncPolicyHit.Execute((context) => GetObject(), HitContext);
}

[Benchmark]
public async Task<object> Cache_Asynchronous_Hit()
{
return await AsyncPolicyHit.ExecuteAsync((context, token) => GetObjectAsync(token), HitContext, CancellationToken.None);
}

[Benchmark]
public object Cache_Synchronous_Miss()
{
return SyncPolicyMiss.Execute((context) => GetObject(), MissContext);
}

[Benchmark]
public async Task<object> Cache_Asynchronous_Miss()
{
return await AsyncPolicyMiss.ExecuteAsync((context, token) => GetObjectAsync(token), MissContext, CancellationToken.None);
}

private static object GetObject() => new object();

private static Task<object> GetObjectAsync(CancellationToken cancellationToken) => Task.FromResult(new object());

private sealed class MemoryCacheProvider : ISyncCacheProvider, IAsyncCacheProvider
{
private readonly IMemoryCache _cache;

public MemoryCacheProvider(IMemoryCache memoryCache)
{
_cache = memoryCache;
}

public (bool, object) TryGet(string key)
{
bool cacheHit = _cache.TryGetValue(key, out var value);
return (cacheHit, value);
}

public void Put(string key, object value, Ttl ttl)
{
TimeSpan remaining = DateTimeOffset.MaxValue - DateTimeOffset.UtcNow;
var options = new MemoryCacheEntryOptions();

if (ttl.SlidingExpiration)
{
options.SlidingExpiration = ttl.Timespan < remaining ? ttl.Timespan : remaining;
}
else
{
if (ttl.Timespan == TimeSpan.MaxValue)
{
options.AbsoluteExpiration = DateTimeOffset.MaxValue;
}
else
{
options.AbsoluteExpirationRelativeToNow = ttl.Timespan < remaining ? ttl.Timespan : remaining;
}
}

_cache.Set(key, value, options);
}

public Task<(bool, object)> TryGetAsync(string key, CancellationToken cancellationToken, bool continueOnCapturedContext)
{
return Task.FromResult(TryGet(key));
}

public Task PutAsync(string key, object value, Ttl ttl, CancellationToken cancellationToken, bool continueOnCapturedContext)
{
Put(key, value, ttl);
return Task.CompletedTask;
}
}
}
}
38 changes: 38 additions & 0 deletions src/Polly.Benchmarks/CircuitBreaker.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using BenchmarkDotNet.Attributes;

namespace Polly.Benchmarks
{
[Config(typeof(PollyConfig))]
public class CircuitBreaker
{
private static readonly Policy SyncPolicy = Policy.Handle<InvalidOperationException>().CircuitBreaker(2, TimeSpan.FromMinutes(1));
private static readonly AsyncPolicy AsyncPolicy = Policy.Handle<InvalidOperationException>().CircuitBreakerAsync(2, TimeSpan.FromMinutes(1));

[Benchmark]
public void CircuitBreaker_Synchronous_Succeeds()
{
SyncPolicy.Execute(() => Workloads.Action());
}

[Benchmark]
public async Task CircuitBreaker_Asynchronous_Succeeds()
{
await AsyncPolicy.ExecuteAsync((token) => Workloads.ActionAsync(token), CancellationToken.None);
}

[Benchmark]
public int CircuitBreaker_Synchronous_With_Result_Succeeds()
{
return SyncPolicy.Execute(() => Workloads.Func<int>());
}

[Benchmark]
public async Task<int> CircuitBreaker_Asynchronous_With_Result_Succeeds()
{
return await AsyncPolicy.ExecuteAsync((token) => Workloads.FuncAsync<int>(token), CancellationToken.None);
}
}
}
37 changes: 37 additions & 0 deletions src/Polly.Benchmarks/Fallback.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
using System;
using System.Threading.Tasks;
using BenchmarkDotNet.Attributes;

namespace Polly.Benchmarks
{
[Config(typeof(PollyConfig))]
public class Fallback
{
private static readonly Policy<int> SyncPolicy = Policy<int>.Handle<InvalidOperationException>().Fallback(0);
private static readonly AsyncPolicy<int> AsyncPolicy = Policy<int>.Handle<InvalidOperationException>().FallbackAsync(0);

[Benchmark]
public int Fallback_Synchronous_Succeeds()
{
return SyncPolicy.Execute(() => Workloads.Func<int>());
}

[Benchmark]
public async Task<int> Fallback_Asynchronous_Succeeds()
{
return await AsyncPolicy.ExecuteAsync(() => Workloads.FuncAsync<int>());
}

[Benchmark]
public int Fallback_Synchronous_Throws()
{
return SyncPolicy.Execute(() => Workloads.FuncThrows<int, InvalidOperationException>());
}

[Benchmark]
public async Task<int> Fallback_Asynchronous_Throws()
{
return await AsyncPolicy.ExecuteAsync(() => Workloads.FuncThrowsAsync<int, InvalidOperationException>());
}
}
}
37 changes: 37 additions & 0 deletions src/Polly.Benchmarks/NoOp.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
using System.Threading;
using System.Threading.Tasks;
using BenchmarkDotNet.Attributes;

namespace Polly.Benchmarks
{
[Config(typeof(PollyConfig))]
public class NoOp
{
private static readonly Policy SyncPolicy = Policy.NoOp();
private static readonly AsyncPolicy AsyncPolicy = Policy.NoOpAsync();

[Benchmark]
public void NoOp_Synchronous()
{
SyncPolicy.Execute(() => Workloads.Action());
}

[Benchmark]
public async Task NoOp_Asynchronous()
{
await AsyncPolicy.ExecuteAsync((token) => Workloads.ActionAsync(token), CancellationToken.None);
}

[Benchmark]
public int NoOp_Synchronous_With_Result()
{
return SyncPolicy.Execute(() => Workloads.Func<int>());
}

[Benchmark]
public async Task<int> NoOp_Asynchronous_With_Result()
{
return await AsyncPolicy.ExecuteAsync((token) => Workloads.FuncAsync<int>(token), CancellationToken.None);
}
}
}
47 changes: 47 additions & 0 deletions src/Polly.Benchmarks/PolicyWrap.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using BenchmarkDotNet.Attributes;

namespace Polly.Benchmarks
{
[Config(typeof(PollyConfig))]
public class PolicyWrap
{
private static readonly Policy SyncPolicy = Policy.Wrap(
Policy.Handle<InvalidOperationException>().Retry(),
Policy.Handle<InvalidOperationException>().CircuitBreaker(2, TimeSpan.FromMinutes(1)),
Policy.Timeout(TimeSpan.FromMilliseconds(10)),
Policy.Bulkhead(2));

private static readonly AsyncPolicy AsyncPolicy = Policy.WrapAsync(
Policy.Handle<InvalidOperationException>().RetryAsync(),
Policy.Handle<InvalidOperationException>().CircuitBreakerAsync(2, TimeSpan.FromMinutes(1)),
Policy.TimeoutAsync(TimeSpan.FromMilliseconds(10)),
Policy.BulkheadAsync(2));

[Benchmark]
public void PolicyWrap_Synchronous()
{
SyncPolicy.Execute(() => Workloads.Action());
}

[Benchmark]
public async Task PolicyWrap_Asynchronous()
{
await AsyncPolicy.ExecuteAsync((token) => Workloads.ActionAsync(token), CancellationToken.None);
}

[Benchmark]
public int PolicyWrap_Synchronous_With_Result()
{
return SyncPolicy.Execute(() => Workloads.Func<int>());
}

[Benchmark]
public async Task<int> PolicyWrap_Asynchronous_With_Result()
{
return await AsyncPolicy.ExecuteAsync((token) => Workloads.FuncAsync<int>(token), CancellationToken.None);
}
}
}
15 changes: 15 additions & 0 deletions src/Polly.Benchmarks/Polly.Benchmarks.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<IsPackable>false</IsPackable>
<LangVersion>latest</LangVersion>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="BenchmarkDotNet" Version="0.13.1" />
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="6.0.0" />
</ItemGroup>
<ItemGroup Condition=" '$(BenchmarkFromNuGet)' != 'True' ">
<ProjectReference Include="..\Polly\Polly.csproj" />
</ItemGroup>
</Project>
Loading

0 comments on commit c27faf8

Please sign in to comment.