-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Bridge between Polly V7 and Polly V8 (#1255)
- Loading branch information
Showing
9 changed files
with
439 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
188 changes: 188 additions & 0 deletions
188
src/Polly.Specs/ResilienceStrategyConversionExtensionsTests.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,188 @@ | ||
using Polly.Strategy; | ||
using Polly.TestUtils; | ||
|
||
namespace Polly.Specs; | ||
|
||
public class ResilienceStrategyConversionExtensionsTests | ||
{ | ||
private static readonly ResiliencePropertyKey<string> Incoming = new ResiliencePropertyKey<string>("incoming-key"); | ||
|
||
private static readonly ResiliencePropertyKey<string> Executing = new ResiliencePropertyKey<string>("executing-key"); | ||
|
||
private static readonly ResiliencePropertyKey<string> Outgoing = new ResiliencePropertyKey<string>("outgoing-key"); | ||
|
||
private readonly TestResilienceStrategy _strategy; | ||
private readonly ResilienceStrategy<string> _genericStrategy; | ||
private bool _isSynchronous; | ||
private bool _isVoid; | ||
private Type? _resultType; | ||
|
||
public ResilienceStrategyConversionExtensionsTests() | ||
{ | ||
_strategy = new TestResilienceStrategy(); | ||
_strategy.Before = (context, ct) => | ||
{ | ||
context.IsVoid.Should().Be(_isVoid); | ||
context.IsSynchronous.Should().Be(_isSynchronous); | ||
context.Properties.Set(Outgoing, "outgoing-value"); | ||
context.Properties.GetValue(Incoming, string.Empty).Should().Be("incoming-value"); | ||
|
||
if (_resultType != null) | ||
{ | ||
context.ResultType.Should().Be(_resultType); | ||
} | ||
}; | ||
|
||
_genericStrategy = new ResilienceStrategyBuilder<string>() | ||
.AddStrategy(_strategy) | ||
.Build(); | ||
} | ||
|
||
[Fact] | ||
public void AsSyncPolicy_Ok() | ||
{ | ||
_isVoid = true; | ||
_isSynchronous = true; | ||
var context = new Context | ||
{ | ||
[Incoming.Key] = "incoming-value" | ||
}; | ||
|
||
_strategy.AsSyncPolicy().Execute(c => | ||
{ | ||
context[Executing.Key] = "executing-value"; | ||
}, | ||
context); | ||
|
||
AssertContext(context); | ||
} | ||
|
||
[Fact] | ||
public void AsSyncPolicy_Generic_Ok() | ||
{ | ||
_isVoid = false; | ||
_isSynchronous = true; | ||
_resultType = typeof(string); | ||
var context = new Context | ||
{ | ||
[Incoming.Key] = "incoming-value" | ||
}; | ||
|
||
var result = _genericStrategy.AsSyncPolicy().Execute(c => { context[Executing.Key] = "executing-value"; return "dummy"; }, context); | ||
AssertContext(context); | ||
result.Should().Be("dummy"); | ||
} | ||
|
||
[Fact] | ||
public void AsSyncPolicy_Result_Ok() | ||
{ | ||
_isVoid = false; | ||
_isSynchronous = true; | ||
_resultType = typeof(string); | ||
var context = new Context | ||
{ | ||
[Incoming.Key] = "incoming-value" | ||
}; | ||
|
||
var result = _strategy.AsSyncPolicy().Execute(c => { context[Executing.Key] = "executing-value"; return "dummy"; }, context); | ||
|
||
AssertContext(context); | ||
result.Should().Be("dummy"); | ||
} | ||
|
||
[Fact] | ||
public async Task AsAsyncPolicy_Ok() | ||
{ | ||
_isVoid = true; | ||
_isSynchronous = false; | ||
var context = new Context | ||
{ | ||
[Incoming.Key] = "incoming-value" | ||
}; | ||
|
||
await _strategy.AsAsyncPolicy().ExecuteAsync(c => | ||
{ | ||
context[Executing.Key] = "executing-value"; | ||
return Task.CompletedTask; | ||
}, | ||
context); | ||
|
||
AssertContext(context); | ||
} | ||
|
||
[Fact] | ||
public async Task AsAsyncPolicy_Generic_Ok() | ||
{ | ||
_isVoid = false; | ||
_isSynchronous = false; | ||
_resultType = typeof(string); | ||
var context = new Context | ||
{ | ||
[Incoming.Key] = "incoming-value" | ||
}; | ||
|
||
var result = await _genericStrategy.AsAsyncPolicy().ExecuteAsync(c => | ||
{ | ||
context[Executing.Key] = "executing-value"; | ||
return Task.FromResult("dummy"); | ||
}, | ||
context); | ||
AssertContext(context); | ||
result.Should().Be("dummy"); | ||
} | ||
|
||
[Fact] | ||
public async Task AsAsyncPolicy_Result_Ok() | ||
{ | ||
_isVoid = false; | ||
_isSynchronous = false; | ||
_resultType = typeof(string); | ||
var context = new Context | ||
{ | ||
[Incoming.Key] = "incoming-value" | ||
}; | ||
|
||
var result = await _strategy.AsAsyncPolicy().ExecuteAsync(c => | ||
{ | ||
context[Executing.Key] = "executing-value"; | ||
return Task.FromResult("dummy"); | ||
}, | ||
context); | ||
|
||
AssertContext(context); | ||
result.Should().Be("dummy"); | ||
} | ||
|
||
[Fact] | ||
public void RetryStrategy_AsSyncPolicy_Ok() | ||
{ | ||
var policy = new ResilienceStrategyBuilder<string>() | ||
.AddRetry(new RetryStrategyOptions<string> | ||
{ | ||
ShouldRetry = _ => PredicateResult.True, | ||
BackoffType = RetryBackoffType.Constant, | ||
RetryCount = 5, | ||
BaseDelay = TimeSpan.FromMilliseconds(1) | ||
}) | ||
.Build() | ||
.AsSyncPolicy(); | ||
|
||
var tries = 0; | ||
policy.Execute( | ||
() => | ||
{ | ||
tries++; | ||
return "dummy"; | ||
}) | ||
.Should() | ||
.Be("dummy"); | ||
|
||
tries.Should().Be(6); | ||
} | ||
|
||
private static void AssertContext(Context context) | ||
{ | ||
context[Outgoing.Key].Should().Be("outgoing-value"); | ||
context[Executing.Key].Should().Be("executing-value"); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
using Polly.Utilities.Wrappers; | ||
using Polly.Utils; | ||
|
||
namespace Polly; | ||
|
||
/// <summary> | ||
/// Extensions for conversion of resilience strategies to policies. | ||
/// </summary> | ||
public static class ResilienceStrategyConversionExtensions | ||
{ | ||
/// <summary> | ||
/// Converts a <see cref="ResilienceStrategy"/> to an <see cref="IAsyncPolicy"/>. | ||
/// </summary> | ||
/// <param name="strategy">The strategy instance.</param> | ||
/// <returns>An instance of <see cref="IAsyncPolicy"/>.</returns> | ||
/// <exception cref="ArgumentNullException">Thrown when <paramref name="strategy"/> is <see langword="null"/>.</exception> | ||
public static IAsyncPolicy AsAsyncPolicy(this ResilienceStrategy strategy) | ||
=> new ResilienceStrategyAsyncPolicy(strategy ?? throw new ArgumentNullException(nameof(strategy))); | ||
|
||
/// <summary> | ||
/// Converts a <see cref="ResilienceStrategy{TResult}"/> to an <see cref="IAsyncPolicy{TResult}"/>. | ||
/// </summary> | ||
/// <param name="strategy">The strategy instance.</param> | ||
/// <returns>An instance of <see cref="IAsyncPolicy{TResult}"/>.</returns> | ||
/// <exception cref="ArgumentNullException">Thrown when <paramref name="strategy"/> is <see langword="null"/>.</exception> | ||
public static IAsyncPolicy<TResult> AsAsyncPolicy<TResult>(this ResilienceStrategy<TResult> strategy) | ||
=> new ResilienceStrategyAsyncPolicy<TResult>(strategy ?? throw new ArgumentNullException(nameof(strategy))); | ||
|
||
/// <summary> | ||
/// Converts a <see cref="ResilienceStrategy"/> to an <see cref="ISyncPolicy"/>. | ||
/// </summary> | ||
/// <param name="strategy">The strategy instance.</param> | ||
/// <returns>An instance of <see cref="ISyncPolicy"/>.</returns> | ||
/// <exception cref="ArgumentNullException">Thrown when <paramref name="strategy"/> is <see langword="null"/>.</exception> | ||
public static ISyncPolicy AsSyncPolicy(this ResilienceStrategy strategy) | ||
=> new ResilienceStrategySyncPolicy(strategy ?? throw new ArgumentNullException(nameof(strategy))); | ||
|
||
/// <summary> | ||
/// Converts a <see cref="ResilienceStrategy{TResult}"/> to an <see cref="ISyncPolicy{TResult}"/>. | ||
/// </summary> | ||
/// <param name="strategy">The strategy instance.</param> | ||
/// <returns>An instance of <see cref="ISyncPolicy{TResult}"/>.</returns> | ||
/// <exception cref="ArgumentNullException">Thrown when <paramref name="strategy"/> is <see langword="null"/>.</exception> | ||
public static ISyncPolicy<TResult> AsSyncPolicy<TResult>(this ResilienceStrategy<TResult> strategy) | ||
=> new ResilienceStrategySyncPolicy<TResult>(strategy ?? throw new ArgumentNullException(nameof(strategy))); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
namespace Polly.Utilities.Wrappers; | ||
|
||
internal static class ResilienceContextFactory | ||
{ | ||
public static readonly ResiliencePropertyKey<Context> ContextKey = new("Polly.Legacy.Context"); | ||
|
||
public static ResilienceContext Create(Context context, CancellationToken cancellationToken, bool continueOnCapturedContext) | ||
{ | ||
var resilienceContext = ResilienceContext.Get(); | ||
resilienceContext.CancellationToken = cancellationToken; | ||
resilienceContext.ContinueOnCapturedContext = continueOnCapturedContext; | ||
|
||
foreach (var pair in context) | ||
{ | ||
var props = (IDictionary<string, object>)resilienceContext.Properties; | ||
props.Add(pair.Key, pair.Value); | ||
} | ||
|
||
resilienceContext.Properties.Set(ContextKey, context); | ||
|
||
return resilienceContext; | ||
} | ||
|
||
public static void Restore(ResilienceContext context) | ||
{ | ||
var originalContext = context.GetContext(); | ||
|
||
foreach (var pair in context.Properties) | ||
{ | ||
if (pair.Key == ContextKey.Key) | ||
{ | ||
continue; | ||
} | ||
|
||
originalContext[pair.Key] = pair.Value; | ||
} | ||
|
||
ResilienceContext.Return(context); | ||
} | ||
|
||
public static Context GetContext(this ResilienceContext resilienceContext) => resilienceContext.Properties.GetValue(ContextKey, null!); | ||
} |
28 changes: 28 additions & 0 deletions
28
src/Polly/Utilities/Wrappers/ResilienceStrategyAsyncPolicy.TResult.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
namespace Polly.Utilities.Wrappers; | ||
|
||
internal sealed class ResilienceStrategyAsyncPolicy<TResult> : AsyncPolicy<TResult> | ||
{ | ||
private readonly ResilienceStrategy<TResult> _strategy; | ||
|
||
public ResilienceStrategyAsyncPolicy(ResilienceStrategy<TResult> strategy) => _strategy = strategy; | ||
|
||
protected sealed override async Task<TResult> ImplementationAsync(Func<Context, CancellationToken, Task<TResult>> action, Context context, CancellationToken cancellationToken, bool continueOnCapturedContext) | ||
{ | ||
var resilienceContext = ResilienceContextFactory.Create(context, cancellationToken, continueOnCapturedContext); | ||
|
||
try | ||
{ | ||
return await _strategy.ExecuteAsync( | ||
static async (context, state) => | ||
{ | ||
return await state(context.GetContext(), context.CancellationToken).ConfigureAwait(context.ContinueOnCapturedContext); | ||
}, | ||
resilienceContext, | ||
action).ConfigureAwait(continueOnCapturedContext); | ||
} | ||
finally | ||
{ | ||
ResilienceContextFactory.Restore(resilienceContext); | ||
} | ||
} | ||
} |
56 changes: 56 additions & 0 deletions
56
src/Polly/Utilities/Wrappers/ResilienceStrategyAsyncPolicy.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
namespace Polly.Utilities.Wrappers; | ||
|
||
internal sealed class ResilienceStrategyAsyncPolicy : AsyncPolicy | ||
{ | ||
private readonly ResilienceStrategy _strategy; | ||
|
||
public ResilienceStrategyAsyncPolicy(ResilienceStrategy strategy) => _strategy = strategy; | ||
|
||
protected sealed override async Task ImplementationAsync( | ||
Func<Context, CancellationToken, Task> action, | ||
Context context, | ||
CancellationToken cancellationToken, | ||
bool continueOnCapturedContext) | ||
{ | ||
var resilienceContext = ResilienceContextFactory.Create(context, cancellationToken, continueOnCapturedContext); | ||
|
||
try | ||
{ | ||
await _strategy.ExecuteAsync( | ||
static async (context, state) => | ||
{ | ||
await state(context.GetContext(), context.CancellationToken).ConfigureAwait(context.ContinueOnCapturedContext); | ||
}, | ||
resilienceContext, | ||
action).ConfigureAwait(continueOnCapturedContext); | ||
} | ||
finally | ||
{ | ||
ResilienceContextFactory.Restore(resilienceContext); | ||
} | ||
} | ||
|
||
protected override async Task<TResult> ImplementationAsync<TResult>( | ||
Func<Context, CancellationToken, Task<TResult>> action, | ||
Context context, | ||
CancellationToken cancellationToken, | ||
bool continueOnCapturedContext) | ||
{ | ||
var resilienceContext = ResilienceContextFactory.Create(context, cancellationToken, continueOnCapturedContext); | ||
|
||
try | ||
{ | ||
return await _strategy.ExecuteAsync( | ||
static async (context, state) => | ||
{ | ||
return await state(context.GetContext(), context.CancellationToken).ConfigureAwait(context.ContinueOnCapturedContext); | ||
}, | ||
resilienceContext, | ||
action).ConfigureAwait(continueOnCapturedContext); | ||
} | ||
finally | ||
{ | ||
ResilienceContextFactory.Restore(resilienceContext); | ||
} | ||
} | ||
} |
Oops, something went wrong.