Skip to content

Commit

Permalink
Simmy v8 feedback (#1682)
Browse files Browse the repository at this point in the history
  • Loading branch information
vany0114 authored Oct 22, 2023
1 parent 86b4e29 commit 54da87d
Show file tree
Hide file tree
Showing 25 changed files with 647 additions and 678 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,19 @@ internal static class BehaviorPipelineBuilderExtensions
/// </summary>
/// <typeparam name="TBuilder">The builder type.</typeparam>
/// <param name="builder">The builder instance.</param>
/// <param name="enabled">A value that indicates whether or not the chaos strategy is enabled for a given execution.</param>
/// <param name="injectionRate">The injection rate for a given execution, which the value should be between [0, 1] (inclusive).</param>
/// <param name="behavior">The behavior to be injected.</param>
/// <returns>The same builder instance.</returns>
/// <exception cref="ArgumentNullException">Thrown when <paramref name="builder"/> is <see langword="null"/>.</exception>
/// <exception cref="ValidationException">Thrown when the options produced from the arguments are invalid.</exception>
public static TBuilder AddChaosBehavior<TBuilder>(this TBuilder builder, bool enabled, double injectionRate, Func<ValueTask> behavior)
public static TBuilder AddChaosBehavior<TBuilder>(this TBuilder builder, double injectionRate, Func<ValueTask> behavior)
where TBuilder : ResiliencePipelineBuilderBase
{
Guard.NotNull(builder);

return builder.AddChaosBehavior(new BehaviorStrategyOptions
{
Enabled = enabled,
Enabled = true,
InjectionRate = injectionRate,
BehaviorAction = (_) => behavior()
});
Expand Down
60 changes: 60 additions & 0 deletions src/Polly.Core/Simmy/Fault/FaultChaosStrategy.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
using Polly.Telemetry;

namespace Polly.Simmy.Fault;

internal class FaultChaosStrategy : MonkeyStrategy
{
private readonly ResilienceStrategyTelemetry _telemetry;

public FaultChaosStrategy(FaultStrategyOptions options, ResilienceStrategyTelemetry telemetry)
: base(options)
{
if (options.Fault is null && options.FaultGenerator is null)
{
throw new InvalidOperationException("Either Fault or FaultGenerator is required.");
}

_telemetry = telemetry;
Fault = options.Fault;
OnFaultInjected = options.OnFaultInjected;
FaultGenerator = options.FaultGenerator is not null ? options.FaultGenerator : (_) => new(options.Fault);
}

public Func<OnFaultInjectedArguments, ValueTask>? OnFaultInjected { get; }

public Func<FaultGeneratorArguments, ValueTask<Exception?>> FaultGenerator { get; }

public Exception? Fault { get; }

protected internal override async ValueTask<Outcome<TResult>> ExecuteCore<TResult, TState>(
Func<ResilienceContext, TState, ValueTask<Outcome<TResult>>> callback,
ResilienceContext context,
TState state)
{
try
{
if (await ShouldInjectAsync(context).ConfigureAwait(context.ContinueOnCapturedContext))
{
var fault = await FaultGenerator(new(context)).ConfigureAwait(context.ContinueOnCapturedContext);
if (fault is not null)
{
var args = new OnFaultInjectedArguments(context, fault);
_telemetry.Report(new(ResilienceEventSeverity.Information, FaultConstants.OnFaultInjectedEvent), context, args);

if (OnFaultInjected is not null)
{
await OnFaultInjected(args).ConfigureAwait(context.ContinueOnCapturedContext);
}

return new Outcome<TResult>(fault);
}
}

return await StrategyHelper.ExecuteCallbackSafeAsync(callback, context, state).ConfigureAwait(context.ContinueOnCapturedContext);
}
catch (OperationCanceledException e)
{
return new Outcome<TResult>(e);
}
}
}
6 changes: 6 additions & 0 deletions src/Polly.Core/Simmy/Fault/FaultConstants.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace Polly.Simmy.Fault;

internal static class FaultConstants
{
public const string OnFaultInjectedEvent = "OnFaultInjectedEvent";
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
namespace Polly.Simmy.Outcomes;
namespace Polly.Simmy.Fault;

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

Expand Down
72 changes: 72 additions & 0 deletions src/Polly.Core/Simmy/Fault/FaultPipelineBuilderExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
using System.Diagnostics.CodeAnalysis;
using Polly.Simmy.Fault;

namespace Polly.Simmy;

/// <summary>
/// Extension methods for adding outcome to a <see cref="ResiliencePipelineBuilder"/>.
/// </summary>
internal static class FaultPipelineBuilderExtensions
{
/// <summary>
/// Adds a fault chaos strategy to the builder.
/// </summary>
/// <param name="builder">The builder instance.</param>
/// <param name="injectionRate">The injection rate for a given execution, which the value should be between [0, 1] (inclusive).</param>
/// <param name="fault">The exception to inject.</param>
/// <returns>The builder instance with the retry strategy added.</returns>
public static TBuilder AddChaosFault<TBuilder>(this TBuilder builder, double injectionRate, Exception fault)
where TBuilder : ResiliencePipelineBuilderBase
{
builder.AddChaosFault(new FaultStrategyOptions
{
Enabled = true,
InjectionRate = injectionRate,
Fault = fault
});
return builder;
}

/// <summary>
/// Adds a fault chaos strategy to the builder.
/// </summary>
/// <param name="builder">The builder instance.</param>
/// <param name="injectionRate">The injection rate for a given execution, which the value should be between [0, 1] (inclusive).</param>
/// <param name="faultGenerator">The exception generator delegate.</param>
/// <returns>The builder instance with the retry strategy added.</returns>
public static TBuilder AddChaosFault<TBuilder>(this TBuilder builder, double injectionRate, Func<Exception?> faultGenerator)
where TBuilder : ResiliencePipelineBuilderBase
{
builder.AddChaosFault(new FaultStrategyOptions
{
Enabled = true,
InjectionRate = injectionRate,
FaultGenerator = (_) => new ValueTask<Exception?>(Task.FromResult(faultGenerator()))
});
return builder;
}

/// <summary>
/// Adds a fault chaos strategy to the builder.
/// </summary>
/// <typeparam name="TBuilder">The builder type.</typeparam>
/// <param name="builder">The builder instance.</param>
/// <param name="options">The fault strategy options.</param>
/// <returns>The builder instance with the retry strategy added.</returns>
[UnconditionalSuppressMessage(
"Trimming",
"IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code",
Justification = "All options members preserved.")]
public static TBuilder AddChaosFault<TBuilder>(this TBuilder builder, FaultStrategyOptions options)
where TBuilder : ResiliencePipelineBuilderBase
{
Guard.NotNull(builder);
Guard.NotNull(options);

builder.AddStrategy(
context => new FaultChaosStrategy(options, context.Telemetry),
options);

return builder;
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
namespace Polly.Simmy.Outcomes;
namespace Polly.Simmy.Fault;

#pragma warning disable CS8618 // Required members are not initialized in constructor since this is a DTO, default value is null

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
namespace Polly.Simmy.Outcomes;
namespace Polly.Simmy.Fault;

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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,19 @@ internal static class LatencyPipelineBuilderExtensions
/// </summary>
/// <typeparam name="TBuilder">The builder type.</typeparam>
/// <param name="builder">The builder instance.</param>
/// <param name="enabled">A value that indicates whether or not the chaos strategy is enabled for a given execution.</param>
/// <param name="injectionRate">The injection rate for a given execution, which the value should be between [0, 1] (inclusive).</param>
/// <param name="latency">The delay value.</param>
/// <returns>The same builder instance.</returns>
/// <exception cref="ArgumentNullException">Thrown when <paramref name="builder"/> is <see langword="null"/>.</exception>
/// <exception cref="ValidationException">Thrown when the options produced from the arguments are invalid.</exception>
public static TBuilder AddChaosLatency<TBuilder>(this TBuilder builder, bool enabled, double injectionRate, TimeSpan latency)
public static TBuilder AddChaosLatency<TBuilder>(this TBuilder builder, double injectionRate, TimeSpan latency)
where TBuilder : ResiliencePipelineBuilderBase
{
Guard.NotNull(builder);

return builder.AddChaosLatency(new LatencyStrategyOptions
{
Enabled = enabled,
Enabled = true,
InjectionRate = injectionRate,
Latency = latency
});
Expand Down
84 changes: 10 additions & 74 deletions src/Polly.Core/Simmy/Outcomes/OutcomeChaosStrategy.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,65 +8,34 @@ internal class OutcomeChaosStrategy<T> : MonkeyStrategy<T>
{
private readonly ResilienceStrategyTelemetry _telemetry;

public OutcomeChaosStrategy(FaultStrategyOptions options, ResilienceStrategyTelemetry telemetry)
: base(options)
{
if (options.Fault is null && options.FaultGenerator is null)
{
throw new InvalidOperationException("Either Fault or FaultGenerator is required.");
}

_telemetry = telemetry;
Fault = options.Fault;
OnFaultInjected = options.OnFaultInjected;
FaultGenerator = options.FaultGenerator is not null ? options.FaultGenerator : (_) => new(options.Fault);
}

public OutcomeChaosStrategy(OutcomeStrategyOptions<T> options, ResilienceStrategyTelemetry telemetry)
: base(options)
{
if (options.Outcome is null && options.OutcomeGenerator is null)
{
throw new InvalidOperationException("Either Outcome or OutcomeGenerator is required.");
}

_telemetry = telemetry;
Outcome = options.Outcome;
OnOutcomeInjected = options.OnOutcomeInjected;
OutcomeGenerator = options.OutcomeGenerator is not null ? options.OutcomeGenerator : (_) => new(options.Outcome);
OutcomeGenerator = options.OutcomeGenerator;
}

public Func<OnOutcomeInjectedArguments<T>, ValueTask>? OnOutcomeInjected { get; }

public Func<OnFaultInjectedArguments, ValueTask>? OnFaultInjected { get; }

public Func<OutcomeGeneratorArguments, ValueTask<Outcome<T>?>>? OutcomeGenerator { get; }

public Func<FaultGeneratorArguments, ValueTask<Exception?>>? FaultGenerator { get; }

public Outcome<T>? Outcome { get; }

public Exception? Fault { get; }
public Func<OutcomeGeneratorArguments, ValueTask<Outcome<T>?>> OutcomeGenerator { get; }

protected internal override async ValueTask<Outcome<T>> ExecuteCore<TState>(Func<ResilienceContext, TState, ValueTask<Outcome<T>>> callback, ResilienceContext context, TState state)
{
try
{
if (await ShouldInjectAsync(context).ConfigureAwait(context.ContinueOnCapturedContext))
{
if (FaultGenerator is not null)
{
var fault = await InjectFault(context).ConfigureAwait(context.ContinueOnCapturedContext);
if (fault is not null)
{
return new Outcome<T>(fault);
}
}
else if (OutcomeGenerator is not null)
var outcome = await OutcomeGenerator(new(context)).ConfigureAwait(context.ContinueOnCapturedContext);
var args = new OnOutcomeInjectedArguments<T>(context, outcome.Value);
_telemetry.Report(new(ResilienceEventSeverity.Information, OutcomeConstants.OnOutcomeInjectedEvent), context, args);

if (OnOutcomeInjected is not null)
{
var outcome = await InjectOutcome(context).ConfigureAwait(context.ContinueOnCapturedContext);
return new Outcome<T>(outcome.Value.Result);
await OnOutcomeInjected(args).ConfigureAwait(context.ContinueOnCapturedContext);
}

return new Outcome<T>(outcome.Value.Result);
}

return await StrategyHelper.ExecuteCallbackSafeAsync(callback, context, state).ConfigureAwait(context.ContinueOnCapturedContext);
Expand All @@ -76,37 +45,4 @@ protected internal override async ValueTask<Outcome<T>> ExecuteCore<TState>(Func
return new Outcome<T>(e);
}
}

private async ValueTask<Outcome<T>?> InjectOutcome(ResilienceContext context)
{
var outcome = await OutcomeGenerator!(new(context)).ConfigureAwait(context.ContinueOnCapturedContext);
var args = new OnOutcomeInjectedArguments<T>(context, outcome.Value);
_telemetry.Report(new(ResilienceEventSeverity.Information, OutcomeConstants.OnOutcomeInjectedEvent), context, args);

if (OnOutcomeInjected is not null)
{
await OnOutcomeInjected(args).ConfigureAwait(context.ContinueOnCapturedContext);
}

return outcome;
}

private async ValueTask<Exception?> InjectFault(ResilienceContext context)
{
var fault = await FaultGenerator!(new(context)).ConfigureAwait(context.ContinueOnCapturedContext);
if (fault is null)
{
return null;
}

var args = new OnFaultInjectedArguments(context, fault);
_telemetry.Report(new(ResilienceEventSeverity.Information, OutcomeConstants.OnFaultInjectedEvent), context, args);

if (OnFaultInjected is not null)
{
await OnFaultInjected(args).ConfigureAwait(context.ContinueOnCapturedContext);
}

return fault;
}
}
2 changes: 0 additions & 2 deletions src/Polly.Core/Simmy/Outcomes/OutcomeConstants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,4 @@
internal static class OutcomeConstants
{
public const string OnOutcomeInjectedEvent = "OnOutcomeInjected";

public const string OnFaultInjectedEvent = "OnFaultInjectedEvent";
}
Loading

0 comments on commit 54da87d

Please sign in to comment.