Skip to content

Commit

Permalink
Simmy API review Part 4 - Rename BehaviorAction to BehaviorGenerator (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
peter-csala authored Jan 23, 2024
1 parent 74f0a72 commit 5897459
Show file tree
Hide file tree
Showing 11 changed files with 95 additions and 41 deletions.
33 changes: 29 additions & 4 deletions docs/chaos/behavior.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,15 @@ The behavior chaos strategy is designed to inject custom behaviors into system o
// To use a custom delegated for injected behavior
var optionsWithBehaviorGenerator = new ChaosBehaviorStrategyOptions
{
BehaviorAction = static args => RestartRedisAsync(args.Context.CancellationToken),
BehaviorGenerator = static args => RestartRedisAsync(args.Context.CancellationToken),
Enabled = true,
InjectionRate = 0.05
};

// To get notifications when a behavior is injected
var optionsOnBehaviorInjected = new ChaosBehaviorStrategyOptions
{
BehaviorAction = static args => RestartRedisAsync(args.Context.CancellationToken),
BehaviorGenerator = static args => RestartRedisAsync(args.Context.CancellationToken),
Enabled = true,
InjectionRate = 0.05,
OnBehaviorInjected = static args =>
Expand Down Expand Up @@ -62,7 +62,7 @@ var pipeline = new ResiliencePipelineBuilder()
})
.AddChaosBehavior(new ChaosBehaviorStrategyOptions // Chaos strategies are usually placed as the last ones in the pipeline
{
BehaviorAction = static args => RestartRedisAsync(args.Context.CancellationToken),
BehaviorGenerator = static args => RestartRedisAsync(args.Context.CancellationToken),
Enabled = true,
InjectionRate = 0.05
})
Expand All @@ -75,7 +75,7 @@ var pipeline = new ResiliencePipelineBuilder()
| Property | Default Value | Description |
|----------------------|---------------|------------------------------------------------|
| `OnBehaviorInjected` | `null` | Action executed when the behavior is injected. |
| `BehaviorAction` | `null` | Custom behavior to be injected. |
| `BehaviorGenerator` | `null` | Custom behavior to be injected. |

## Diagrams

Expand Down Expand Up @@ -128,6 +128,31 @@ sequenceDiagram

Use behavior strategies to inject delays.

<!-- snippet: chaos-behavior-anti-pattern-inject-delay -->
```cs
var pipeline = new ResiliencePipelineBuilder()
.AddChaosBehavior(new ChaosBehaviorStrategyOptions
{
BehaviorGenerator = static async args =>
{
await Task.Delay(TimeSpan.FromSeconds(7), args.Context.CancellationToken);
}
})
.Build();
```
<!-- endSnippet -->

✅ DO

Use the latency chaos instead as the [`ChaosLatencyStrategy`](latency.md) already correctly handles synchronous/asynchronous delay executions, cancellations, etc.

<!-- snippet: chaos-behavior-pattern-inject-delay -->
```cs
var pipeline = new ResiliencePipelineBuilder()
.AddChaosLatency(new ChaosLatencyStrategyOptions
{
Latency = TimeSpan.FromSeconds(7),
})
.Build();
```
<!-- endSnippet -->
12 changes: 6 additions & 6 deletions src/Polly.Core/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
#nullable enable
Polly.CircuitBreaker.BreakDurationGeneratorArguments.BreakDurationGeneratorArguments(double failureRate, int failureCount, Polly.ResilienceContext! context, int halfOpenAttempts) -> void
Polly.CircuitBreaker.BreakDurationGeneratorArguments.HalfOpenAttempts.get -> int
Polly.Simmy.Behavior.BehaviorActionArguments
Polly.Simmy.Behavior.BehaviorActionArguments.BehaviorActionArguments() -> void
Polly.Simmy.Behavior.BehaviorActionArguments.BehaviorActionArguments(Polly.ResilienceContext! context) -> void
Polly.Simmy.Behavior.BehaviorActionArguments.Context.get -> Polly.ResilienceContext!
Polly.Simmy.Behavior.BehaviorGeneratorArguments
Polly.Simmy.Behavior.BehaviorGeneratorArguments.BehaviorGeneratorArguments() -> void
Polly.Simmy.Behavior.BehaviorGeneratorArguments.BehaviorGeneratorArguments(Polly.ResilienceContext! context) -> void
Polly.Simmy.Behavior.BehaviorGeneratorArguments.Context.get -> Polly.ResilienceContext!
Polly.Simmy.Behavior.ChaosBehaviorStrategyOptions
Polly.Simmy.Behavior.ChaosBehaviorStrategyOptions.BehaviorAction.get -> System.Func<Polly.Simmy.Behavior.BehaviorActionArguments, System.Threading.Tasks.ValueTask>?
Polly.Simmy.Behavior.ChaosBehaviorStrategyOptions.BehaviorAction.set -> void
Polly.Simmy.Behavior.ChaosBehaviorStrategyOptions.BehaviorGenerator.get -> System.Func<Polly.Simmy.Behavior.BehaviorGeneratorArguments, System.Threading.Tasks.ValueTask>?
Polly.Simmy.Behavior.ChaosBehaviorStrategyOptions.BehaviorGenerator.set -> void
Polly.Simmy.Behavior.ChaosBehaviorStrategyOptions.ChaosBehaviorStrategyOptions() -> void
Polly.Simmy.Behavior.ChaosBehaviorStrategyOptions.OnBehaviorInjected.get -> System.Func<Polly.Simmy.Behavior.OnBehaviorInjectedArguments, System.Threading.Tasks.ValueTask>?
Polly.Simmy.Behavior.ChaosBehaviorStrategyOptions.OnBehaviorInjected.set -> void
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@
/// <summary>
/// Arguments used by the behavior chaos strategy to execute a user's delegate custom action.
/// </summary>
public readonly struct BehaviorActionArguments
public readonly struct BehaviorGeneratorArguments
{
/// <summary>
/// Initializes a new instance of the <see cref="BehaviorActionArguments"/> struct.
/// Initializes a new instance of the <see cref="BehaviorGeneratorArguments"/> struct.
/// </summary>
/// <param name="context">The context associated with the execution of a user-provided callback.</param>
public BehaviorActionArguments(ResilienceContext context) => Context = context;
public BehaviorGeneratorArguments(ResilienceContext context) => Context = context;

/// <summary>
/// Gets the ResilienceContext instance.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public static TBuilder AddChaosBehavior<TBuilder>(this TBuilder builder, double
{
Enabled = true,
InjectionRate = injectionRate,
BehaviorAction = args => behavior(args.Context.CancellationToken)
BehaviorGenerator = args => behavior(args.Context.CancellationToken)
});
}

Expand Down
4 changes: 2 additions & 2 deletions src/Polly.Core/Simmy/Behavior/ChaosBehaviorStrategy.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,12 @@ public ChaosBehaviorStrategy(
{
_telemetry = telemetry;
OnBehaviorInjected = options.OnBehaviorInjected;
Behavior = options.BehaviorAction!;
Behavior = options.BehaviorGenerator!;
}

public Func<OnBehaviorInjectedArguments, ValueTask>? OnBehaviorInjected { get; }

public Func<BehaviorActionArguments, ValueTask> Behavior { get; }
public Func<BehaviorGeneratorArguments, ValueTask> Behavior { get; }

protected internal override async ValueTask<Outcome<TResult>> ExecuteCore<TResult, TState>(
Func<ResilienceContext, TState, ValueTask<Outcome<TResult>>> callback,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,5 @@ public class ChaosBehaviorStrategyOptions : ChaosStrategyOptions
/// Defaults to <see langword="null"/>.
/// </remarks>
[Required]
public Func<BehaviorActionArguments, ValueTask>? BehaviorAction { get; set; }
public Func<BehaviorGeneratorArguments, ValueTask>? BehaviorGenerator { get; set; }
}
35 changes: 32 additions & 3 deletions src/Snippets/Docs/Chaos.Behavior.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using Polly.Retry;
using Polly.Simmy;
using Polly.Simmy.Behavior;
using Polly.Simmy.Latency;

namespace Snippets.Docs;

Expand All @@ -13,15 +14,15 @@ public static void BehaviorUsage()
// To use a custom delegated for injected behavior
var optionsWithBehaviorGenerator = new ChaosBehaviorStrategyOptions
{
BehaviorAction = static args => RestartRedisAsync(args.Context.CancellationToken),
BehaviorGenerator = static args => RestartRedisAsync(args.Context.CancellationToken),
Enabled = true,
InjectionRate = 0.05
};

// To get notifications when a behavior is injected
var optionsOnBehaviorInjected = new ChaosBehaviorStrategyOptions
{
BehaviorAction = static args => RestartRedisAsync(args.Context.CancellationToken),
BehaviorGenerator = static args => RestartRedisAsync(args.Context.CancellationToken),
Enabled = true,
InjectionRate = 0.05,
OnBehaviorInjected = static args =>
Expand Down Expand Up @@ -51,13 +52,41 @@ public static void BehaviorUsage()
})
.AddChaosBehavior(new ChaosBehaviorStrategyOptions // Chaos strategies are usually placed as the last ones in the pipeline
{
BehaviorAction = static args => RestartRedisAsync(args.Context.CancellationToken),
BehaviorGenerator = static args => RestartRedisAsync(args.Context.CancellationToken),
Enabled = true,
InjectionRate = 0.05
})
.Build();
#endregion
}

public static void AntiPattern_InjectDelay()
{
#region chaos-behavior-anti-pattern-inject-delay
var pipeline = new ResiliencePipelineBuilder()
.AddChaosBehavior(new ChaosBehaviorStrategyOptions
{
BehaviorGenerator = static async args =>
{
await Task.Delay(TimeSpan.FromSeconds(7), args.Context.CancellationToken);
}
})
.Build();

#endregion
}

public static void Pattern_InjectDelay()
{
#region chaos-behavior-pattern-inject-delay
var pipeline = new ResiliencePipelineBuilder()
.AddChaosLatency(new ChaosLatencyStrategyOptions
{
Latency = TimeSpan.FromSeconds(7),
})
.Build();
#endregion
}
}

internal class RedisConnectionException : Exception
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@

namespace Polly.Core.Tests.Simmy.Behavior;

public class BehaviorActionArgumentsTests
public class BehaviorGeneratorArgumentsTests
{
[Fact]
public void Ctor_Ok()
{
var args = new BehaviorActionArguments(ResilienceContextPool.Shared.Get());
var args = new BehaviorGeneratorArguments(ResilienceContextPool.Shared.Get());
args.Context.Should().NotBeNull();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ public void AddBehavior_Options_Ok()
{
Enabled = true,
InjectionRate = 1,
BehaviorAction = (_) => default
BehaviorGenerator = (_) => default
})
.Build();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public void Ctor_Ok()
sut.EnabledGenerator.Should().BeNull();
sut.InjectionRate.Should().Be(ChaosStrategyConstants.DefaultInjectionRate);
sut.InjectionRateGenerator.Should().BeNull();
sut.BehaviorAction.Should().BeNull();
sut.BehaviorGenerator.Should().BeNull();
sut.OnBehaviorInjected.Should().BeNull();
}

Expand All @@ -33,7 +33,7 @@ public void InvalidOptions()
Invalid Options
Validation Errors:
The BehaviorAction field is required.
The BehaviorGenerator field is required.
""");
}
}
32 changes: 16 additions & 16 deletions test/Polly.Core.Tests/Simmy/Behavior/ChaosBehaviorStrategyTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ public class ChaosBehaviorStrategyTests
private readonly ResilienceStrategyTelemetry _telemetry;
private readonly ChaosBehaviorStrategyOptions _options;
private readonly List<TelemetryEventArguments<object, object>> _args = [];
private bool _behaviorActionExecuted;
private bool _behaviorGeneratorExecuted;
private bool _onBehaviorInjectedExecuted;
private bool _userDelegateExecuted;

Expand All @@ -17,7 +17,7 @@ public ChaosBehaviorStrategyTests()
_telemetry = TestUtilities.CreateResilienceTelemetry(arg => _args.Add(arg));
_options = new();
_userDelegateExecuted = false;
_behaviorActionExecuted = false;
_behaviorGeneratorExecuted = false;
_onBehaviorInjectedExecuted = false;
}

Expand All @@ -27,13 +27,13 @@ public void Given_not_enabled_should_not_inject_behavior()
_options.InjectionRate = 0.6;
_options.Enabled = false;
_options.Randomizer = () => 0.5;
_options.BehaviorAction = (_) => { _behaviorActionExecuted = true; return default; };
_options.BehaviorGenerator = (_) => { _behaviorGeneratorExecuted = true; return default; };

var sut = CreateSut();
sut.Execute(() => { _userDelegateExecuted = true; });

_userDelegateExecuted.Should().BeTrue();
_behaviorActionExecuted.Should().BeFalse();
_behaviorGeneratorExecuted.Should().BeFalse();
}

[Fact]
Expand All @@ -42,13 +42,13 @@ public async Task Given_enabled_and_randomly_within_threshold_should_inject_beha
_options.InjectionRate = 0.6;
_options.Enabled = true;
_options.Randomizer = () => 0.5;
_options.BehaviorAction = (_) => { _behaviorActionExecuted = true; return default; };
_options.BehaviorGenerator = (_) => { _behaviorGeneratorExecuted = true; return default; };

var sut = CreateSut();
await sut.ExecuteAsync((_) => { _userDelegateExecuted = true; return default; });

_userDelegateExecuted.Should().BeTrue();
_behaviorActionExecuted.Should().BeTrue();
_behaviorGeneratorExecuted.Should().BeTrue();
_onBehaviorInjectedExecuted.Should().BeFalse();
}

Expand All @@ -58,7 +58,7 @@ public async Task Given_enabled_and_randomly_within_threshold_ensure_on_behavior
_options.InjectionRate = 0.6;
_options.Enabled = true;
_options.Randomizer = () => 0.5;
_options.BehaviorAction = (_) => { _behaviorActionExecuted = true; return default; };
_options.BehaviorGenerator = (_) => { _behaviorGeneratorExecuted = true; return default; };
_options.OnBehaviorInjected = args =>
{
args.Context.Should().NotBeNull();
Expand All @@ -72,7 +72,7 @@ public async Task Given_enabled_and_randomly_within_threshold_ensure_on_behavior

_onBehaviorInjectedExecuted.Should().BeTrue();
_userDelegateExecuted.Should().BeTrue();
_behaviorActionExecuted.Should().BeTrue();
_behaviorGeneratorExecuted.Should().BeTrue();
_args.Should().HaveCount(1);
_args[0].Arguments.Should().BeOfType<OnBehaviorInjectedArguments>();
}
Expand All @@ -83,13 +83,13 @@ public async Task Given_enabled_and_randomly_not_within_threshold_should_not_inj
_options.InjectionRate = 0.4;
_options.Enabled = false;
_options.Randomizer = () => 0.5;
_options.BehaviorAction = (_) => { _behaviorActionExecuted = true; return default; };
_options.BehaviorGenerator = (_) => { _behaviorGeneratorExecuted = true; return default; };

var sut = CreateSut();
await sut.ExecuteAsync((_) => { _userDelegateExecuted = true; return default; });

_userDelegateExecuted.Should().BeTrue();
_behaviorActionExecuted.Should().BeFalse();
_behaviorGeneratorExecuted.Should().BeFalse();
_onBehaviorInjectedExecuted.Should().BeFalse();
}

Expand All @@ -99,18 +99,18 @@ public async Task Should_inject_behavior_before_executing_user_delegate()
_options.InjectionRate = 0.6;
_options.Enabled = true;
_options.Randomizer = () => 0.5;
_options.BehaviorAction = (_) =>
_options.BehaviorGenerator = (_) =>
{
_userDelegateExecuted.Should().BeFalse(); // Not yet executed at the time the injected behavior runs.
_behaviorActionExecuted = true;
_behaviorGeneratorExecuted = true;
return default;
};

var sut = CreateSut();
await sut.ExecuteAsync((_) => { _userDelegateExecuted = true; return default; });

_userDelegateExecuted.Should().BeTrue();
_behaviorActionExecuted.Should().BeTrue();
_behaviorGeneratorExecuted.Should().BeTrue();
_onBehaviorInjectedExecuted.Should().BeFalse();
}

Expand All @@ -121,10 +121,10 @@ public async Task Should_not_execute_user_delegate_when_it_was_cancelled_running
_options.InjectionRate = 0.6;
_options.Enabled = true;
_options.Randomizer = () => 0.5;
_options.BehaviorAction = (_) =>
_options.BehaviorGenerator = (_) =>
{
cts.Cancel();
_behaviorActionExecuted = true;
_behaviorGeneratorExecuted = true;
return default;
};

Expand All @@ -134,7 +134,7 @@ public async Task Should_not_execute_user_delegate_when_it_was_cancelled_running
.ThrowAsync<OperationCanceledException>();

_userDelegateExecuted.Should().BeFalse();
_behaviorActionExecuted.Should().BeTrue();
_behaviorGeneratorExecuted.Should().BeTrue();
_onBehaviorInjectedExecuted.Should().BeFalse();
}

Expand Down

0 comments on commit 5897459

Please sign in to comment.