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

Drop OutcomeArguments struct #1513

Merged
merged 1 commit into from
Aug 24, 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
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