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

Simmy v8 feedback #1682

Merged
merged 8 commits into from
Oct 22, 2023
Merged
Show file tree
Hide file tree
Changes from 4 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
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
56 changes: 56 additions & 0 deletions src/Polly.Core/Simmy/Fault/FaultChaosStrategy.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
using Polly.Telemetry;

namespace Polly.Simmy.Fault;
vany0114 marked this conversation as resolved.
Show resolved Hide resolved
internal class FaultChaosStrategy<T> : MonkeyStrategy<T>
vany0114 marked this conversation as resolved.
Show resolved Hide resolved
{
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; }
vany0114 marked this conversation as resolved.
Show resolved Hide resolved

public Exception? Fault { 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))
{
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<T>(fault);
}
}

return await StrategyHelper.ExecuteCallbackSafeAsync(callback, context, state).ConfigureAwait(context.ContinueOnCapturedContext);
}
catch (OperationCanceledException e)
{
return new Outcome<T>(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
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
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 partial class FaultPipelineBuilderExtensions
{
/// <summary>
/// Adds a fault chaos strategy to the builder.
/// </summary>
/// <typeparam name="TResult">The type of result the retry strategy handles.</typeparam>
/// <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 ResiliencePipelineBuilder<TResult> AddChaosFault<TResult>(this ResiliencePipelineBuilder<TResult> builder, double injectionRate, Exception fault)
{
Guard.NotNull(builder);

builder.AddFaultCore(new FaultStrategyOptions
{
Enabled = true,
InjectionRate = injectionRate,
Fault = fault
});
return builder;
}

/// <summary>
/// Adds a fault chaos strategy to the builder.
/// </summary>
/// <typeparam name="TResult">The type of result the retry strategy handles.</typeparam>
/// <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 ResiliencePipelineBuilder<TResult> AddChaosFault<TResult>(
this ResiliencePipelineBuilder<TResult> builder, double injectionRate, Func<Exception?> faultGenerator)
{
Guard.NotNull(builder);

builder.AddFaultCore(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="TResult">The type of result the retry strategy handles.</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>
public static ResiliencePipelineBuilder<TResult> AddChaosFault<TResult>(this ResiliencePipelineBuilder<TResult> builder, FaultStrategyOptions options)
{
Guard.NotNull(builder);
Guard.NotNull(options);

builder.AddFaultCore<TResult>(options);
return builder;
}

[UnconditionalSuppressMessage(
"Trimming",
"IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code",
Justification = "All options members preserved.")]
private static void AddFaultCore<TResult>(
this ResiliencePipelineBuilder<TResult> builder,
FaultStrategyOptions options)
{
builder.AddStrategy(
context => new FaultChaosStrategy<TResult>(options, context.Telemetry),
options);
}
}
79 changes: 79 additions & 0 deletions src/Polly.Core/Simmy/Fault/FaultPipelineBuilderExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
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 partial 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 ResiliencePipelineBuilder AddChaosFault(this ResiliencePipelineBuilder builder, double injectionRate, Exception fault)
vany0114 marked this conversation as resolved.
Show resolved Hide resolved
{
Guard.NotNull(builder);

builder.AddFaultCore(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 ResiliencePipelineBuilder AddChaosFault(
this ResiliencePipelineBuilder builder, double injectionRate, Func<Exception?> faultGenerator)
{
Guard.NotNull(builder);

builder.AddFaultCore(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>
/// <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>
public static ResiliencePipelineBuilder AddChaosFault(this ResiliencePipelineBuilder builder, FaultStrategyOptions options)
{
Guard.NotNull(builder);
Guard.NotNull(options);

builder.AddFaultCore(options);
return builder;
}

[UnconditionalSuppressMessage(
"Trimming",
"IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code",
Justification = "All options members preserved.")]
private static void AddFaultCore(this ResiliencePipelineBuilder builder, FaultStrategyOptions options)
{
builder.AddStrategy(context =>
new FaultChaosStrategy<object>(
options,
context.Telemetry),
options);
}
}
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
Loading