Skip to content

Commit

Permalink
Drop OutcomeArguments struct
Browse files Browse the repository at this point in the history
  • Loading branch information
martintmk committed Aug 24, 2023
1 parent 8aa9df2 commit e771ba1
Show file tree
Hide file tree
Showing 66 changed files with 494 additions and 351 deletions.
6 changes: 3 additions & 3 deletions bench/Polly.Core.Benchmarks/PredicateBenchmark.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@ namespace Polly.Core.Benchmarks;

public class PredicateBenchmark
{
private readonly OutcomeArguments<HttpResponseMessage, RetryPredicateArguments> _args = new(
private readonly RetryPredicateArguments<HttpResponseMessage> _args = new(
ResilienceContextPool.Shared.Get(),
Outcome.FromResult(new HttpResponseMessage(HttpStatusCode.OK)),
new RetryPredicateArguments(0));
0);

private readonly RetryStrategyOptions<HttpResponseMessage> _delegate = new()
{
ShouldHandle = args => args switch
ShouldHandle = args => args.Outcome switch
{
{ Result: { StatusCode: HttpStatusCode.InternalServerError } } => PredicateResult.True,
{ Exception: HttpRequestException } => PredicateResult.True,
Expand Down
2 changes: 1 addition & 1 deletion bench/Polly.Core.Benchmarks/Utils/Helper.CircuitBreaker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ public static object CreateCircuitBreaker(PollyVersion technology)
SamplingDuration = TimeSpan.FromSeconds(30),
MinimumThroughput = 10,
BreakDuration = TimeSpan.FromSeconds(5),
ShouldHandle = args => args switch
ShouldHandle = args => args.Outcome switch
{
{ Exception: InvalidOperationException } => PredicateResult.True,
{ Result: string result } when result == Failure => PredicateResult.True,
Expand Down
2 changes: 1 addition & 1 deletion bench/Polly.Core.Benchmarks/Utils/Helper.Hedging.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ public static ResiliencePipeline<string> CreateHedging()
{
builder.AddHedging(new HedgingStrategyOptions<string>
{
ShouldHandle = args => new ValueTask<bool>(args.Result == Failure),
ShouldHandle = args => new ValueTask<bool>(args.Outcome.Result == Failure),
HedgingActionGenerator = args => () => Outcome.FromResultAsTask("hedged response"),
});
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ internal static partial class Helper
BackoffType = RetryBackoffType.Constant,
RetryCount = 3,
BaseDelay = TimeSpan.FromSeconds(1),
ShouldHandle = args => args switch
ShouldHandle = args => args.Outcome switch
{
{ Exception: InvalidOperationException } => PredicateResult.True,
{ Result: var result } when result == Failure => PredicateResult.True,
Expand All @@ -41,7 +41,7 @@ internal static partial class Helper
SamplingDuration = TimeSpan.FromSeconds(30),
MinimumThroughput = 10,
BreakDuration = TimeSpan.FromSeconds(5),
ShouldHandle = args => args switch
ShouldHandle = args => args.Outcome switch
{
{ Exception: InvalidOperationException } => PredicateResult.True,
{ Result: string result } when result == Failure => PredicateResult.True,
Expand Down Expand Up @@ -71,7 +71,7 @@ public static ResiliencePipeline CreateNonGenericStrategyPipeline(bool telemetry
BackoffType = RetryBackoffType.Constant,
RetryCount = 3,
BaseDelay = TimeSpan.FromSeconds(1),
ShouldHandle = args => args switch
ShouldHandle = args => args.Outcome switch
{
{ Exception: InvalidOperationException } => PredicateResult.True,
{ Result: string result } when result == Failure => PredicateResult.True,
Expand All @@ -85,7 +85,7 @@ public static ResiliencePipeline CreateNonGenericStrategyPipeline(bool telemetry
SamplingDuration = TimeSpan.FromSeconds(30),
MinimumThroughput = 10,
BreakDuration = TimeSpan.FromSeconds(5),
ShouldHandle = args => args switch
ShouldHandle = args => args.Outcome switch
{
{ Exception: InvalidOperationException } => PredicateResult.True,
{ Result: string result } when result == Failure => PredicateResult.True,
Expand Down
2 changes: 1 addition & 1 deletion bench/Polly.Core.Benchmarks/Utils/Helper.Retry.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public static object CreateRetry(PollyVersion technology)
RetryCount = 3,
BackoffType = RetryBackoffType.Constant,
BaseDelay = delay,
ShouldHandle = args => args switch
ShouldHandle = args => args.Outcome switch
{
{ Exception: InvalidOperationException } => PredicateResult.True,
{ Result: string result } when result == Failure => PredicateResult.True,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,34 @@
namespace Polly.CircuitBreaker;

#pragma warning disable CA1815 // Override equals and operator equals on value types

/// <summary>
/// Arguments used by <see cref="CircuitBreakerStrategyOptions{TResult}.ShouldHandle"/> predicate.
/// </summary>
/// <typeparam name="TResult">The type of result.</typeparam>
/// <remarks>
/// Always use the constructor when creating this struct, otherwise we do not guarantee binary compatibility.
/// </remarks>
public readonly struct CircuitBreakerPredicateArguments
public readonly struct CircuitBreakerPredicateArguments<TResult> : IOutcomeArguments<TResult>
{
/// <summary>
/// Initializes a new instance of the <see cref="CircuitBreakerPredicateArguments{TResult}"/> struct.
/// </summary>
/// <param name="outcome">The context in which the resilience operation or event occurred.</param>
/// <param name="context">The outcome of the resilience operation or event.</param>
public CircuitBreakerPredicateArguments(ResilienceContext context, Outcome<TResult> outcome)
{
Context = context;
Outcome = outcome;
}

/// <summary>
/// Gets the outcome of the resilience operation or event.
/// </summary>
public Outcome<TResult> Outcome { get; }

/// <summary>
/// Gets the context in which the resilience operation or event occurred.
/// </summary>
public ResilienceContext Context { get; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@ namespace Polly.CircuitBreaker;

internal sealed class CircuitBreakerResilienceStrategy<T> : ResilienceStrategy<T>, IDisposable
{
private readonly Func<OutcomeArguments<T, CircuitBreakerPredicateArguments>, ValueTask<bool>> _handler;
private readonly Func<CircuitBreakerPredicateArguments<T>, ValueTask<bool>> _handler;
private readonly CircuitStateController<T> _controller;
private readonly IDisposable? _manualControlRegistration;

public CircuitBreakerResilienceStrategy(
Func<OutcomeArguments<T, CircuitBreakerPredicateArguments>, ValueTask<bool>> handler,
Func<CircuitBreakerPredicateArguments<T>, ValueTask<bool>> handler,
CircuitStateController<T> controller,
CircuitBreakerStateProvider? stateProvider,
CircuitBreakerManualControl? manualControl)
Expand Down Expand Up @@ -36,7 +36,7 @@ protected internal override async ValueTask<Outcome<T>> ExecuteCore<TState>(Func

outcome = await StrategyHelper.ExecuteCallbackSafeAsync(callback, context, state).ConfigureAwait(context.ContinueOnCapturedContext);

var args = new OutcomeArguments<T, CircuitBreakerPredicateArguments>(context, outcome, default);
var args = new CircuitBreakerPredicateArguments<T>(context, outcome);
if (await _handler(args).ConfigureAwait(context.ContinueOnCapturedContext))
{
await _controller.OnActionFailureAsync(outcome, context).ConfigureAwait(context.ContinueOnCapturedContext);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ public class CircuitBreakerStrategyOptions<TResult> : ResilienceStrategyOptions
/// This property is required.
/// </value>
[Required]
public Func<OutcomeArguments<TResult, CircuitBreakerPredicateArguments>, ValueTask<bool>> ShouldHandle { get; set; } = DefaultPredicates<CircuitBreakerPredicateArguments, TResult>.HandleOutcome;
public Func<CircuitBreakerPredicateArguments<TResult>, ValueTask<bool>> ShouldHandle { get; set; } = DefaultPredicates<CircuitBreakerPredicateArguments<TResult>, TResult>.HandleOutcome;

/// <summary>
/// Gets or sets the event that is raised when the circuit resets to a <see cref="CircuitState.Closed"/> state.
Expand All @@ -93,7 +93,7 @@ public class CircuitBreakerStrategyOptions<TResult> : ResilienceStrategyOptions
/// </para>
/// </remarks>
/// <value>The default value is <see langword="null"/>.</value>
public Func<OutcomeArguments<TResult, OnCircuitClosedArguments>, ValueTask>? OnClosed { get; set; }
public Func<OnCircuitClosedArguments<TResult>, ValueTask>? OnClosed { get; set; }

/// <summary>
/// Gets or sets the event that is raised when the circuit transitions to an <see cref="CircuitState.Open"/> state.
Expand All @@ -109,7 +109,7 @@ public class CircuitBreakerStrategyOptions<TResult> : ResilienceStrategyOptions
/// </para>
/// </remarks>
/// <value>The default value is <see langword="null"/>.</value>
public Func<OutcomeArguments<TResult, OnCircuitOpenedArguments>, ValueTask>? OnOpened { get; set; }
public Func<OnCircuitOpenedArguments<TResult>, ValueTask>? OnOpened { get; set; }

/// <summary>
/// Gets or sets the event that is raised when when the circuit transitions to an <see cref="CircuitState.HalfOpen"/> state.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ internal sealed class CircuitStateController<T> : IDisposable
{
private readonly object _lock = new();
private readonly ScheduledTaskExecutor _executor = new();
private readonly Func<OutcomeArguments<T, OnCircuitOpenedArguments>, ValueTask>? _onOpened;
private readonly Func<OutcomeArguments<T, OnCircuitClosedArguments>, ValueTask>? _onClosed;
private readonly Func<OnCircuitOpenedArguments<T>, ValueTask>? _onOpened;
private readonly Func<OnCircuitClosedArguments<T>, ValueTask>? _onClosed;
private readonly Func<OnCircuitHalfOpenedArguments, ValueTask>? _onHalfOpen;
private readonly TimeProvider _timeProvider;
private readonly ResilienceStrategyTelemetry _telemetry;
Expand All @@ -24,8 +24,8 @@ internal sealed class CircuitStateController<T> : IDisposable

public CircuitStateController(
TimeSpan breakDuration,
Func<OutcomeArguments<T, OnCircuitOpenedArguments>, ValueTask>? onOpened,
Func<OutcomeArguments<T, OnCircuitClosedArguments>, ValueTask>? onClosed,
Func<OnCircuitOpenedArguments<T>, ValueTask>? onOpened,
Func<OnCircuitClosedArguments<T>, ValueTask>? onClosed,
Func<OnCircuitHalfOpenedArguments, ValueTask>? onHalfOpen,
CircuitBehavior behavior,
TimeProvider timeProvider,
Expand Down Expand Up @@ -268,8 +268,8 @@ private void CloseCircuit_NeedsLock(Outcome<T> outcome, bool manual, ResilienceC

if (priorState != CircuitState.Closed)
{
var args = new OutcomeArguments<T, OnCircuitClosedArguments>(context, outcome, new OnCircuitClosedArguments(manual));
_telemetry.Report(new(ResilienceEventSeverity.Information, CircuitBreakerConstants.OnCircuitClosed), args);
var args = new OnCircuitClosedArguments<T>(context, outcome, manual);
_telemetry.Report<OnCircuitClosedArguments<T>, T>(new(ResilienceEventSeverity.Information, CircuitBreakerConstants.OnCircuitClosed), args);

if (_onClosed is not null)
{
Expand Down Expand Up @@ -319,8 +319,8 @@ private void OpenCircuitFor_NeedsLock(Outcome<T> outcome, TimeSpan breakDuration
var transitionedState = _circuitState;
_circuitState = CircuitState.Open;

var args = new OutcomeArguments<T, OnCircuitOpenedArguments>(context, outcome, new OnCircuitOpenedArguments(breakDuration, manual));
_telemetry.Report(new(ResilienceEventSeverity.Error, CircuitBreakerConstants.OnCircuitOpened), args);
var args = new OnCircuitOpenedArguments<T>(context, outcome, breakDuration, manual);
_telemetry.Report<OnCircuitOpenedArguments<T>, T>(new(ResilienceEventSeverity.Error, CircuitBreakerConstants.OnCircuitOpened), args);

if (_onOpened is not null)
{
Expand Down
24 changes: 21 additions & 3 deletions src/Polly.Core/CircuitBreaker/OnCircuitClosedArguments.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,34 @@ namespace Polly.CircuitBreaker;
/// <summary>
/// Arguments used by <see cref="CircuitBreakerStrategyOptions{TResult}.OnClosed"/> event.
/// </summary>
/// <typeparam name="TResult">The type of result.</typeparam>
/// <remarks>
/// Always use the constructor when creating this struct, otherwise we do not guarantee binary compatibility.
/// </remarks>
public readonly struct OnCircuitClosedArguments
public readonly struct OnCircuitClosedArguments<TResult> : IOutcomeArguments<TResult>
{
/// <summary>
/// Initializes a new instance of the <see cref="OnCircuitClosedArguments"/> struct.
/// Initializes a new instance of the <see cref="OnCircuitClosedArguments{TResult}"/> struct.
/// </summary>
/// <param name="outcome">The context in which the resilience operation or event occurred.</param>
/// <param name="context">The outcome of the resilience operation or event.</param>
/// <param name="isManual">Indicates whether the circuit was closed manually by using <see cref="CircuitBreakerManualControl"/>.</param>
public OnCircuitClosedArguments(bool isManual) => IsManual = isManual;
public OnCircuitClosedArguments(ResilienceContext context, Outcome<TResult> outcome, bool isManual)
{
Context = context;
Outcome = outcome;
IsManual = isManual;
}

/// <summary>
/// Gets the outcome of the resilience operation or event.
/// </summary>
public Outcome<TResult> Outcome { get; }

/// <summary>
/// Gets the context in which the resilience operation or event occurred.
/// </summary>
public ResilienceContext Context { get; }

/// <summary>
/// Gets a value indicating whether the circuit was closed manually by using <see cref="CircuitBreakerManualControl"/>.
Expand Down
21 changes: 18 additions & 3 deletions src/Polly.Core/CircuitBreaker/OnCircuitOpenedArguments.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,37 @@ namespace Polly.CircuitBreaker;
/// <summary>
/// Arguments used by <see cref="CircuitBreakerStrategyOptions{TResult}.OnOpened"/> event.
/// </summary>
/// <typeparam name="TResult">The type of result.</typeparam>
/// <remarks>
/// Always use the constructor when creating this struct, otherwise we do not guarantee binary compatibility.
/// </remarks>
public readonly struct OnCircuitOpenedArguments
public readonly struct OnCircuitOpenedArguments<TResult> : IOutcomeArguments<TResult>
{
/// <summary>
/// Initializes a new instance of the <see cref="OnCircuitOpenedArguments"/> struct.
/// Initializes a new instance of the <see cref="OnCircuitOpenedArguments{TResult}"/> struct.
/// </summary>
/// <param name="outcome">The context in which the resilience operation or event occurred.</param>
/// <param name="context">The outcome of the resilience operation or event.</param>
/// <param name="breakDuration">The duration of break.</param>
/// <param name="isManual">Indicates whether the circuit was opened manually by using <see cref="CircuitBreakerManualControl"/>.</param>
public OnCircuitOpenedArguments(TimeSpan breakDuration, bool isManual)
public OnCircuitOpenedArguments(ResilienceContext context, Outcome<TResult> outcome, TimeSpan breakDuration, bool isManual)
{
Context = context;
Outcome = outcome;
BreakDuration = breakDuration;
IsManual = isManual;
}

/// <summary>
/// Gets the outcome of the resilience operation or event.
/// </summary>
public Outcome<TResult> Outcome { get; }

/// <summary>
/// Gets the context in which the resilience operation or event occurred.
/// </summary>
public ResilienceContext Context { get; }

/// <summary>
/// Gets the duration of break.
/// </summary>
Expand Down
11 changes: 5 additions & 6 deletions src/Polly.Core/Fallback/FallbackHandler.cs
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
namespace Polly.Fallback;

internal sealed record class FallbackHandler<T>(
Func<OutcomeArguments<T, FallbackPredicateArguments>, ValueTask<bool>> ShouldHandle,
Func<OutcomeArguments<T, FallbackPredicateArguments>, ValueTask<Outcome<T>>> ActionGenerator)
Func<FallbackPredicateArguments<T>, ValueTask<bool>> ShouldHandle,
Func<FallbackPredicateArguments<T>, ValueTask<Outcome<T>>> ActionGenerator)
{
public async ValueTask<Outcome<TResult>> GetFallbackOutcomeAsync<TResult>(OutcomeArguments<TResult, FallbackPredicateArguments> args)
public async ValueTask<Outcome<TResult>> GetFallbackOutcomeAsync<TResult>(FallbackPredicateArguments<T> args)
{
var copiedArgs = new OutcomeArguments<T, FallbackPredicateArguments>(
var copiedArgs = new FallbackPredicateArguments<T>(
args.Context,
args.Outcome.AsOutcome<T>(),
args.Arguments);
args.Outcome.AsOutcome<T>());

return (await ActionGenerator(copiedArgs).ConfigureAwait(args.Context.ContinueOnCapturedContext)).AsOutcome<TResult>();
}
Expand Down
25 changes: 24 additions & 1 deletion src/Polly.Core/Fallback/FallbackPredicateArguments.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,34 @@
namespace Polly.Fallback;

#pragma warning disable CA1815 // Override equals and operator equals on value types

/// <summary>
/// Represents arguments used in fallback handling scenarios.
/// </summary>
/// <typeparam name="TResult">The type of result.</typeparam>
/// <remarks>
/// Always use the constructor when creating this struct, otherwise we do not guarantee binary compatibility.
/// </remarks>
public readonly struct FallbackPredicateArguments
public readonly struct FallbackPredicateArguments<TResult> : IOutcomeArguments<TResult>
{
/// <summary>
/// Initializes a new instance of the <see cref="FallbackPredicateArguments{TResult}"/> struct.
/// </summary>
/// <param name="outcome">The context in which the resilience operation or event occurred.</param>
/// <param name="context">The outcome of the resilience operation or event.</param>
public FallbackPredicateArguments(ResilienceContext context, Outcome<TResult> outcome)
{
Context = context;
Outcome = outcome;
}

/// <summary>
/// Gets the outcome of the resilience operation or event.
/// </summary>
public Outcome<TResult> Outcome { get; }

/// <summary>
/// Gets the context in which the resilience operation or event occurred.
/// </summary>
public ResilienceContext Context { get; }
}
Loading

0 comments on commit e771ba1

Please sign in to comment.