From 208e5dc5c924ed73a7c692a07bf07fa3a4e255e5 Mon Sep 17 00:00:00 2001 From: peter-csala Date: Fri, 19 Jan 2024 09:52:53 +0100 Subject: [PATCH 01/12] Rename OnLatency to OnLatencyInjected --- docs/chaos/latency.md | 16 ++++++++-------- .../Simmy/Latency/LatencyChaosStrategy.cs | 12 ++++++------ src/Polly.Core/Simmy/Latency/LatencyConstants.cs | 2 +- .../Simmy/Latency/LatencyStrategyOptions.cs | 2 +- ...rguments.cs => OnLatencyInjectedArguments.cs} | 6 +++--- src/Snippets/Docs/Chaos.Latency.cs | 6 +++--- .../Simmy/Latency/LatencyChaosStrategyTests.cs | 6 +++--- .../Simmy/Latency/LatencyConstantsTests.cs | 2 +- .../Simmy/Latency/LatencyStrategyOptionsTests.cs | 2 +- ...sts.cs => OnLatencyInjectedArgumentsTests.cs} | 4 ++-- 10 files changed, 29 insertions(+), 29 deletions(-) rename src/Polly.Core/Simmy/Latency/{OnLatencyArguments.cs => OnLatencyInjectedArguments.cs} (77%) rename test/Polly.Core.Tests/Simmy/Latency/{OnLatencyArgumentsTests.cs => OnLatencyInjectedArgumentsTests.cs} (58%) diff --git a/docs/chaos/latency.md b/docs/chaos/latency.md index 8503acd1c4f..b3595e17a48 100644 --- a/docs/chaos/latency.md +++ b/docs/chaos/latency.md @@ -45,14 +45,14 @@ var optionsWithLatencyGenerator = new LatencyStrategyOptions }; // To get notifications when a delay is injected -var optionsOnBehaviorInjected = new LatencyStrategyOptions +var optionsOnLatencyInjected = new LatencyStrategyOptions { Latency = TimeSpan.FromSeconds(30), Enabled = true, InjectionRate = 0.1, - OnLatency = static args => + OnLatencyInjected = static args => { - Console.WriteLine($"OnLatency, Latency: {args.Latency}, Operation: {args.Context.OperationKey}."); + Console.WriteLine($"OnLatencyInjected, Latency: {args.Latency}, Operation: {args.Context.OperationKey}."); return default; } }; @@ -92,11 +92,11 @@ var pipeline = new ResiliencePipelineBuilder() ## Defaults -| Property | Default Value | Description | -|--------------------|---------------|--------------------------------------------------------| -| `Latency` | `30 seconds` | A `TimeSpan` indicating the delay to be injected. | -| `LatencyGenerator` | `null` | Generates the latency to inject for a given execution. | -| `OnLatency` | `null` | Action executed when latency is injected. | +| Property | Default Value | Description | +|---------------------|---------------|--------------------------------------------------------| +| `Latency` | `30 seconds` | A `TimeSpan` indicating the delay to be injected. | +| `LatencyGenerator` | `null` | Generates the latency to inject for a given execution. | +| `OnLatencyInjected` | `null` | Action executed when latency is injected. | ## Diagrams diff --git a/src/Polly.Core/Simmy/Latency/LatencyChaosStrategy.cs b/src/Polly.Core/Simmy/Latency/LatencyChaosStrategy.cs index 409c9a4881f..cdeb5b8efcb 100644 --- a/src/Polly.Core/Simmy/Latency/LatencyChaosStrategy.cs +++ b/src/Polly.Core/Simmy/Latency/LatencyChaosStrategy.cs @@ -15,13 +15,13 @@ public LatencyChaosStrategy( { Latency = options.Latency; LatencyGenerator = options.LatencyGenerator is not null ? options.LatencyGenerator : (_) => new(options.Latency); - OnLatency = options.OnLatency; + OnLatencyInjected = options.OnLatencyInjected; _telemetry = telemetry; _timeProvider = timeProvider; } - public Func? OnLatency { get; } + public Func? OnLatencyInjected { get; } public Func> LatencyGenerator { get; } @@ -43,14 +43,14 @@ protected internal override async ValueTask> ExecuteCore /// Defaults to . /// - public Func? OnLatency { get; set; } + public Func? OnLatencyInjected { get; set; } /// /// Gets or sets the latency generator that generates the delay for a given execution. diff --git a/src/Polly.Core/Simmy/Latency/OnLatencyArguments.cs b/src/Polly.Core/Simmy/Latency/OnLatencyInjectedArguments.cs similarity index 77% rename from src/Polly.Core/Simmy/Latency/OnLatencyArguments.cs rename to src/Polly.Core/Simmy/Latency/OnLatencyInjectedArguments.cs index 9a3896d2904..b82423ede42 100644 --- a/src/Polly.Core/Simmy/Latency/OnLatencyArguments.cs +++ b/src/Polly.Core/Simmy/Latency/OnLatencyInjectedArguments.cs @@ -5,14 +5,14 @@ /// /// Arguments used by the latency chaos strategy to notify that a delayed occurred. /// -public readonly struct OnLatencyArguments +public readonly struct OnLatencyInjectedArguments { /// - /// Initializes a new instance of the struct. + /// Initializes a new instance of the struct. /// /// The context associated with the execution of a user-provided callback. /// The latency that was injected. - public OnLatencyArguments(ResilienceContext context, TimeSpan latency) + public OnLatencyInjectedArguments(ResilienceContext context, TimeSpan latency) { Context = context; Latency = latency; diff --git a/src/Snippets/Docs/Chaos.Latency.cs b/src/Snippets/Docs/Chaos.Latency.cs index 75fdf174d9e..e1cd6d49f30 100644 --- a/src/Snippets/Docs/Chaos.Latency.cs +++ b/src/Snippets/Docs/Chaos.Latency.cs @@ -42,14 +42,14 @@ public static void LatencyUsage() }; // To get notifications when a delay is injected - var optionsOnBehaviorInjected = new LatencyStrategyOptions + var optionsOnLatencyInjected = new LatencyStrategyOptions { Latency = TimeSpan.FromSeconds(30), Enabled = true, InjectionRate = 0.1, - OnLatency = static args => + OnLatencyInjected = static args => { - Console.WriteLine($"OnLatency, Latency: {args.Latency}, Operation: {args.Context.OperationKey}."); + Console.WriteLine($"OnLatencyInjected, Latency: {args.Latency}, Operation: {args.Context.OperationKey}."); return default; } }; diff --git a/test/Polly.Core.Tests/Simmy/Latency/LatencyChaosStrategyTests.cs b/test/Polly.Core.Tests/Simmy/Latency/LatencyChaosStrategyTests.cs index 361ab2d75ba..b44aa136e2e 100644 --- a/test/Polly.Core.Tests/Simmy/Latency/LatencyChaosStrategyTests.cs +++ b/test/Polly.Core.Tests/Simmy/Latency/LatencyChaosStrategyTests.cs @@ -32,7 +32,7 @@ public async Task Given_enabled_and_randomly_within_threshold_should_inject_late _options.Enabled = true; _options.Latency = _delay; _options.Randomizer = () => 0.5; - _options.OnLatency = args => + _options.OnLatencyInjected = args => { args.Context.Should().NotBeNull(); args.Context.CancellationToken.IsCancellationRequested.Should().BeFalse(); @@ -51,7 +51,7 @@ public async Task Given_enabled_and_randomly_within_threshold_should_inject_late (after - before).Should().Be(_delay); _args.Should().HaveCount(1); - _args[0].Arguments.Should().BeOfType(); + _args[0].Arguments.Should().BeOfType(); onLatencyExecuted.Should().BeTrue(); } @@ -110,7 +110,7 @@ public async Task Given_latency_is_negative_should_not_inject_latency(double lat _options.Latency = TimeSpan.FromSeconds(latency); _options.Randomizer = () => 0.5; - _options.OnLatency = args => + _options.OnLatencyInjected = args => { args.Context.Should().NotBeNull(); args.Context.CancellationToken.IsCancellationRequested.Should().BeFalse(); diff --git a/test/Polly.Core.Tests/Simmy/Latency/LatencyConstantsTests.cs b/test/Polly.Core.Tests/Simmy/Latency/LatencyConstantsTests.cs index 1d27e28bf8f..ea5e02f513e 100644 --- a/test/Polly.Core.Tests/Simmy/Latency/LatencyConstantsTests.cs +++ b/test/Polly.Core.Tests/Simmy/Latency/LatencyConstantsTests.cs @@ -7,7 +7,7 @@ public class LatencyConstantsTests [Fact] public void EnsureDefaults() { - LatencyConstants.OnLatencyEvent.Should().Be("OnLatency"); + LatencyConstants.OnLatencyInjectedEvent.Should().Be("OnLatencyInjected"); LatencyConstants.DefaultLatency.Should().Be(TimeSpan.FromSeconds(30)); } } diff --git a/test/Polly.Core.Tests/Simmy/Latency/LatencyStrategyOptionsTests.cs b/test/Polly.Core.Tests/Simmy/Latency/LatencyStrategyOptionsTests.cs index 571b5c2df7d..2999b798082 100644 --- a/test/Polly.Core.Tests/Simmy/Latency/LatencyStrategyOptionsTests.cs +++ b/test/Polly.Core.Tests/Simmy/Latency/LatencyStrategyOptionsTests.cs @@ -16,6 +16,6 @@ public void Ctor_Ok() sut.InjectionRateGenerator.Should().BeNull(); sut.Latency.Should().Be(LatencyConstants.DefaultLatency); sut.LatencyGenerator.Should().BeNull(); - sut.OnLatency.Should().BeNull(); + sut.OnLatencyInjected.Should().BeNull(); } } diff --git a/test/Polly.Core.Tests/Simmy/Latency/OnLatencyArgumentsTests.cs b/test/Polly.Core.Tests/Simmy/Latency/OnLatencyInjectedArgumentsTests.cs similarity index 58% rename from test/Polly.Core.Tests/Simmy/Latency/OnLatencyArgumentsTests.cs rename to test/Polly.Core.Tests/Simmy/Latency/OnLatencyInjectedArgumentsTests.cs index 5c834634e5f..838ea641690 100644 --- a/test/Polly.Core.Tests/Simmy/Latency/OnLatencyArgumentsTests.cs +++ b/test/Polly.Core.Tests/Simmy/Latency/OnLatencyInjectedArgumentsTests.cs @@ -2,12 +2,12 @@ namespace Polly.Core.Tests.Simmy.Latency; -public class OnLatencyArgumentsTests +public class OnLatencyInjectedArgumentsTests { [Fact] public void Ctor_Ok() { - var args = new OnLatencyArguments(ResilienceContextPool.Shared.Get(), TimeSpan.FromSeconds(10)); + var args = new OnLatencyInjectedArguments(ResilienceContextPool.Shared.Get(), TimeSpan.FromSeconds(10)); args.Context.Should().NotBeNull(); args.Latency.Should().Be(TimeSpan.FromSeconds(10)); } From 61cbae6e3cb8c347f41b3bbec91becfab3808f0a Mon Sep 17 00:00:00 2001 From: peter-csala Date: Fri, 19 Jan 2024 09:53:51 +0100 Subject: [PATCH 02/12] Remove Event suffix from the OnFaultInjectedEvent constant --- src/Polly.Core/Simmy/Fault/FaultConstants.cs | 2 +- test/Polly.Core.Tests/Simmy/Fault/FaultConstantsTests.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Polly.Core/Simmy/Fault/FaultConstants.cs b/src/Polly.Core/Simmy/Fault/FaultConstants.cs index 1e8f325af5b..0f7dc2c93c8 100644 --- a/src/Polly.Core/Simmy/Fault/FaultConstants.cs +++ b/src/Polly.Core/Simmy/Fault/FaultConstants.cs @@ -2,5 +2,5 @@ internal static class FaultConstants { - public const string OnFaultInjectedEvent = "OnFaultInjectedEvent"; + public const string OnFaultInjectedEvent = "OnFaultInjected"; } diff --git a/test/Polly.Core.Tests/Simmy/Fault/FaultConstantsTests.cs b/test/Polly.Core.Tests/Simmy/Fault/FaultConstantsTests.cs index ffc1cfd1ff4..647bccc67ff 100644 --- a/test/Polly.Core.Tests/Simmy/Fault/FaultConstantsTests.cs +++ b/test/Polly.Core.Tests/Simmy/Fault/FaultConstantsTests.cs @@ -7,6 +7,6 @@ public class FaultConstantsTests [Fact] public void EnsureDefaults() { - FaultConstants.OnFaultInjectedEvent.Should().Be("OnFaultInjectedEvent"); + FaultConstants.OnFaultInjectedEvent.Should().Be("OnFaultInjected"); } } From d4f39adb08590f43c67252910490690efbfd279f Mon Sep 17 00:00:00 2001 From: peter-csala Date: Fri, 19 Jan 2024 09:54:15 +0100 Subject: [PATCH 03/12] Remove obsolete documentation comment --- src/Polly.Core/Simmy/Fault/FaultStrategyOptions.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Polly.Core/Simmy/Fault/FaultStrategyOptions.cs b/src/Polly.Core/Simmy/Fault/FaultStrategyOptions.cs index deb8d61a602..0cf8f9b9199 100644 --- a/src/Polly.Core/Simmy/Fault/FaultStrategyOptions.cs +++ b/src/Polly.Core/Simmy/Fault/FaultStrategyOptions.cs @@ -19,8 +19,7 @@ public class FaultStrategyOptions : MonkeyStrategyOptions /// Gets or sets the fault generator to be injected for a given execution. /// /// - /// Defaults to . Either or this property is required. - /// When this property is the is used. + /// Defaults to . /// [Required] public Func>? FaultGenerator { get; set; } = default!; From 0e1a3a853e6f730137b7ddbc22739269eca36c7e Mon Sep 17 00:00:00 2001 From: peter-csala Date: Fri, 19 Jan 2024 09:54:32 +0100 Subject: [PATCH 04/12] Fix markdown table --- docs/chaos/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/chaos/index.md b/docs/chaos/index.md index d9be339cd1f..2882f58f296 100644 --- a/docs/chaos/index.md +++ b/docs/chaos/index.md @@ -48,6 +48,6 @@ All the strategies' options implement the [`MonkeyStrategyOptions`](xref:Polly.S | `InjectionRate` | 0.001 ms | A decimal between 0 and 1 inclusive. The strategy will inject the chaos, randomly, that proportion of the time, e.g.: if 0.2, twenty percent of calls will be randomly affected; if 0.01, one percent of calls; if 1, all calls. | | `InjectionRateGenerator` | `null` | Generates the injection rate for a given execution, which the value should be between [0, 1] (inclusive). | | `Enabled` | `false` | Determines whether the strategy is enabled or not. | -| `EnabledGenerator` | `null` | The generator that indicates whether the chaos strategy is enabled for a given execution. | +| `EnabledGenerator` | `null` | The generator that indicates whether the chaos strategy is enabled for a given execution. | [simmy]: https://github.com/Polly-Contrib/Simmy From 1c6b603b2374a0a9bddfcc198014c5ef85af8870 Mon Sep 17 00:00:00 2001 From: peter-csala Date: Fri, 19 Jan 2024 10:11:23 +0100 Subject: [PATCH 05/12] Update unshipped --- src/Polly.Core/PublicAPI.Unshipped.txt | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Polly.Core/PublicAPI.Unshipped.txt b/src/Polly.Core/PublicAPI.Unshipped.txt index 97bb9afd8e9..68d606407b8 100644 --- a/src/Polly.Core/PublicAPI.Unshipped.txt +++ b/src/Polly.Core/PublicAPI.Unshipped.txt @@ -50,13 +50,13 @@ Polly.Simmy.Latency.LatencyStrategyOptions.Latency.set -> void Polly.Simmy.Latency.LatencyStrategyOptions.LatencyGenerator.get -> System.Func>? Polly.Simmy.Latency.LatencyStrategyOptions.LatencyGenerator.set -> void Polly.Simmy.Latency.LatencyStrategyOptions.LatencyStrategyOptions() -> void -Polly.Simmy.Latency.LatencyStrategyOptions.OnLatency.get -> System.Func? -Polly.Simmy.Latency.LatencyStrategyOptions.OnLatency.set -> void -Polly.Simmy.Latency.OnLatencyArguments -Polly.Simmy.Latency.OnLatencyArguments.Context.get -> Polly.ResilienceContext! -Polly.Simmy.Latency.OnLatencyArguments.Latency.get -> System.TimeSpan -Polly.Simmy.Latency.OnLatencyArguments.OnLatencyArguments() -> void -Polly.Simmy.Latency.OnLatencyArguments.OnLatencyArguments(Polly.ResilienceContext! context, System.TimeSpan latency) -> void +Polly.Simmy.Latency.LatencyStrategyOptions.OnLatencyInjected.get -> System.Func? +Polly.Simmy.Latency.LatencyStrategyOptions.OnLatencyInjected.set -> void +Polly.Simmy.Latency.OnLatencyInjectedArguments +Polly.Simmy.Latency.OnLatencyInjectedArguments.Context.get -> Polly.ResilienceContext! +Polly.Simmy.Latency.OnLatencyInjectedArguments.Latency.get -> System.TimeSpan +Polly.Simmy.Latency.OnLatencyInjectedArguments.OnLatencyInjectedArguments() -> void +Polly.Simmy.Latency.OnLatencyInjectedArguments.OnLatencyInjectedArguments(Polly.ResilienceContext! context, System.TimeSpan latency) -> void Polly.Simmy.LatencyPipelineBuilderExtensions Polly.Simmy.MonkeyStrategy Polly.Simmy.MonkeyStrategy.MonkeyStrategy(Polly.Simmy.MonkeyStrategyOptions! options) -> void From c5064714475918f19977243b022dc226eea97c62 Mon Sep 17 00:00:00 2001 From: peter-csala Date: Fri, 19 Jan 2024 11:25:24 +0100 Subject: [PATCH 06/12] Apply suggestions for Fault chaos strategy --- src/Polly.Core/Simmy/Fault/FaultConstants.cs | 4 +++- src/Polly.Core/Simmy/Fault/FaultStrategyOptions.cs | 5 +++++ test/Polly.Core.Tests/Simmy/Fault/FaultConstantsTests.cs | 3 ++- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/Polly.Core/Simmy/Fault/FaultConstants.cs b/src/Polly.Core/Simmy/Fault/FaultConstants.cs index 0f7dc2c93c8..a38c84a9fcc 100644 --- a/src/Polly.Core/Simmy/Fault/FaultConstants.cs +++ b/src/Polly.Core/Simmy/Fault/FaultConstants.cs @@ -2,5 +2,7 @@ internal static class FaultConstants { - public const string OnFaultInjectedEvent = "OnFaultInjected"; + public const string DefaultName = "Chaos.Fault"; + + public const string OnFaultInjectedEvent = "Chaos.OnFault"; } diff --git a/src/Polly.Core/Simmy/Fault/FaultStrategyOptions.cs b/src/Polly.Core/Simmy/Fault/FaultStrategyOptions.cs index 0cf8f9b9199..abb10ebd639 100644 --- a/src/Polly.Core/Simmy/Fault/FaultStrategyOptions.cs +++ b/src/Polly.Core/Simmy/Fault/FaultStrategyOptions.cs @@ -7,6 +7,11 @@ namespace Polly.Simmy.Fault; /// public class FaultStrategyOptions : MonkeyStrategyOptions { + /// + /// Initializes a new instance of the class. + /// + public FaultStrategyOptions() => Name = FaultConstants.DefaultName; + /// /// Gets or sets the delegate that's raised when the outcome is injected. /// diff --git a/test/Polly.Core.Tests/Simmy/Fault/FaultConstantsTests.cs b/test/Polly.Core.Tests/Simmy/Fault/FaultConstantsTests.cs index 647bccc67ff..2a52b8cd2d1 100644 --- a/test/Polly.Core.Tests/Simmy/Fault/FaultConstantsTests.cs +++ b/test/Polly.Core.Tests/Simmy/Fault/FaultConstantsTests.cs @@ -7,6 +7,7 @@ public class FaultConstantsTests [Fact] public void EnsureDefaults() { - FaultConstants.OnFaultInjectedEvent.Should().Be("OnFaultInjected"); + FaultConstants.DefaultName.Should().Be("Chaos.Fault"); + FaultConstants.OnFaultInjectedEvent.Should().Be("Chaos.OnFault"); } } From 324f4c145dc52b4d149440075ac6ed8d688d669d Mon Sep 17 00:00:00 2001 From: peter-csala Date: Fri, 19 Jan 2024 11:28:56 +0100 Subject: [PATCH 07/12] Apply suggestions for Latency chaos strategy --- src/Polly.Core/Simmy/Latency/LatencyConstants.cs | 4 +++- src/Polly.Core/Simmy/Latency/LatencyStrategyOptions.cs | 5 +++++ test/Polly.Core.Tests/Simmy/Latency/LatencyConstantsTests.cs | 3 ++- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/Polly.Core/Simmy/Latency/LatencyConstants.cs b/src/Polly.Core/Simmy/Latency/LatencyConstants.cs index fdb1a5ab6c3..e226d8d4062 100644 --- a/src/Polly.Core/Simmy/Latency/LatencyConstants.cs +++ b/src/Polly.Core/Simmy/Latency/LatencyConstants.cs @@ -2,7 +2,9 @@ internal static class LatencyConstants { - public const string OnLatencyInjectedEvent = "OnLatencyInjected"; + public const string DefaultName = "Chaos.Latency"; + + public const string OnLatencyInjectedEvent = "Chaos.OnLatency"; public static readonly TimeSpan DefaultLatency = TimeSpan.FromSeconds(30); } diff --git a/src/Polly.Core/Simmy/Latency/LatencyStrategyOptions.cs b/src/Polly.Core/Simmy/Latency/LatencyStrategyOptions.cs index ec7327e878b..364c6625aca 100644 --- a/src/Polly.Core/Simmy/Latency/LatencyStrategyOptions.cs +++ b/src/Polly.Core/Simmy/Latency/LatencyStrategyOptions.cs @@ -7,6 +7,11 @@ /// public class LatencyStrategyOptions : MonkeyStrategyOptions { + /// + /// Initializes a new instance of the class. + /// + public LatencyStrategyOptions() => Name = LatencyConstants.DefaultName; + /// /// Gets or sets the delegate that's raised when a delay occurs. /// diff --git a/test/Polly.Core.Tests/Simmy/Latency/LatencyConstantsTests.cs b/test/Polly.Core.Tests/Simmy/Latency/LatencyConstantsTests.cs index ea5e02f513e..b1c3b4462b3 100644 --- a/test/Polly.Core.Tests/Simmy/Latency/LatencyConstantsTests.cs +++ b/test/Polly.Core.Tests/Simmy/Latency/LatencyConstantsTests.cs @@ -7,7 +7,8 @@ public class LatencyConstantsTests [Fact] public void EnsureDefaults() { - LatencyConstants.OnLatencyInjectedEvent.Should().Be("OnLatencyInjected"); + LatencyConstants.DefaultName.Should().Be("Chaos.Latency"); + LatencyConstants.OnLatencyInjectedEvent.Should().Be("Chaos.OnLatency"); LatencyConstants.DefaultLatency.Should().Be(TimeSpan.FromSeconds(30)); } } From 3f12fbff2c07be4918b33cbb66b0177ee7407914 Mon Sep 17 00:00:00 2001 From: peter-csala Date: Fri, 19 Jan 2024 11:32:51 +0100 Subject: [PATCH 08/12] Apply suggestions for Behavior chaos strategy --- src/Polly.Core/Simmy/Behavior/BehaviorConstants.cs | 4 +++- src/Polly.Core/Simmy/Behavior/BehaviorStrategyOptions.cs | 5 +++++ .../Simmy/Behavior/BehaviorConstantsTests.cs | 3 ++- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/Polly.Core/Simmy/Behavior/BehaviorConstants.cs b/src/Polly.Core/Simmy/Behavior/BehaviorConstants.cs index 91341bdb948..188e392f32b 100644 --- a/src/Polly.Core/Simmy/Behavior/BehaviorConstants.cs +++ b/src/Polly.Core/Simmy/Behavior/BehaviorConstants.cs @@ -2,5 +2,7 @@ internal static class BehaviorConstants { - public const string OnBehaviorInjectedEvent = "OnBehaviorInjected"; + public const string DefaultName = "Chaos.Behavior"; + + public const string OnBehaviorInjectedEvent = "Chaos.OnBehavior"; } diff --git a/src/Polly.Core/Simmy/Behavior/BehaviorStrategyOptions.cs b/src/Polly.Core/Simmy/Behavior/BehaviorStrategyOptions.cs index 1541fbc9b3f..84fcb3703ab 100644 --- a/src/Polly.Core/Simmy/Behavior/BehaviorStrategyOptions.cs +++ b/src/Polly.Core/Simmy/Behavior/BehaviorStrategyOptions.cs @@ -7,6 +7,11 @@ namespace Polly.Simmy.Behavior; /// public class BehaviorStrategyOptions : MonkeyStrategyOptions { + /// + /// Initializes a new instance of the class. + /// + public BehaviorStrategyOptions() => Name = BehaviorConstants.DefaultName; + /// /// Gets or sets the delegate that's raised when the custom behavior is injected. /// diff --git a/test/Polly.Core.Tests/Simmy/Behavior/BehaviorConstantsTests.cs b/test/Polly.Core.Tests/Simmy/Behavior/BehaviorConstantsTests.cs index e25fffb3fe0..13f0d4a6578 100644 --- a/test/Polly.Core.Tests/Simmy/Behavior/BehaviorConstantsTests.cs +++ b/test/Polly.Core.Tests/Simmy/Behavior/BehaviorConstantsTests.cs @@ -7,6 +7,7 @@ public class BehaviorConstantsTests [Fact] public void EnsureDefaults() { - BehaviorConstants.OnBehaviorInjectedEvent.Should().Be("OnBehaviorInjected"); + BehaviorConstants.DefaultName.Should().Be("Chaos.Behavior"); + BehaviorConstants.OnBehaviorInjectedEvent.Should().Be("Chaos.OnBehavior"); } } From 1b6f696572e0b843e69501ffde6024ff6bcb89c3 Mon Sep 17 00:00:00 2001 From: peter-csala Date: Fri, 19 Jan 2024 11:34:23 +0100 Subject: [PATCH 09/12] Apply suggestions for Outcome chaos strategy --- src/Polly.Core/Simmy/Outcomes/OutcomeConstants.cs | 4 +++- .../Simmy/Outcomes/OutcomeStrategyOptions.TResult.cs | 5 +++++ .../Polly.Core.Tests/Simmy/Outcomes/OutcomeConstantsTests.cs | 3 ++- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/Polly.Core/Simmy/Outcomes/OutcomeConstants.cs b/src/Polly.Core/Simmy/Outcomes/OutcomeConstants.cs index d061a0579f1..1ac0882db04 100644 --- a/src/Polly.Core/Simmy/Outcomes/OutcomeConstants.cs +++ b/src/Polly.Core/Simmy/Outcomes/OutcomeConstants.cs @@ -2,5 +2,7 @@ internal static class OutcomeConstants { - public const string OnOutcomeInjectedEvent = "OnOutcomeInjected"; + public const string DefaultName = "Chaos.Outcome"; + + public const string OnOutcomeInjectedEvent = "Chaos.OnOutcome"; } diff --git a/src/Polly.Core/Simmy/Outcomes/OutcomeStrategyOptions.TResult.cs b/src/Polly.Core/Simmy/Outcomes/OutcomeStrategyOptions.TResult.cs index 4236d870254..44a2ba1e9bb 100644 --- a/src/Polly.Core/Simmy/Outcomes/OutcomeStrategyOptions.TResult.cs +++ b/src/Polly.Core/Simmy/Outcomes/OutcomeStrategyOptions.TResult.cs @@ -8,6 +8,11 @@ namespace Polly.Simmy.Outcomes; /// The type of the outcome that was injected. public class OutcomeStrategyOptions : MonkeyStrategyOptions { + /// + /// Initializes a new instance of the class. + /// + public OutcomeStrategyOptions() => Name = OutcomeConstants.DefaultName; + /// /// Gets or sets the delegate that's raised when the outcome is injected. /// diff --git a/test/Polly.Core.Tests/Simmy/Outcomes/OutcomeConstantsTests.cs b/test/Polly.Core.Tests/Simmy/Outcomes/OutcomeConstantsTests.cs index dcd92910518..f2c085bdd01 100644 --- a/test/Polly.Core.Tests/Simmy/Outcomes/OutcomeConstantsTests.cs +++ b/test/Polly.Core.Tests/Simmy/Outcomes/OutcomeConstantsTests.cs @@ -7,6 +7,7 @@ public class OutcomeConstantsTests [Fact] public void EnsureDefaults() { - OutcomeConstants.OnOutcomeInjectedEvent.Should().Be("OnOutcomeInjected"); + OutcomeConstants.DefaultName.Should().Be("Chaos.Outcome"); + OutcomeConstants.OnOutcomeInjectedEvent.Should().Be("Chaos.OnOutcome"); } } From 7732a076a3c092bb6aeabd8cda98defa16c055de Mon Sep 17 00:00:00 2001 From: peter-csala <57183693+peter-csala@users.noreply.github.com> Date: Fri, 19 Jan 2024 12:49:28 +0100 Subject: [PATCH 10/12] [Docs] Add banner to chaos docs (#1910) --- docs/chaos/behavior.md | 3 +++ docs/chaos/fault.md | 3 +++ docs/chaos/index.md | 3 +++ docs/chaos/latency.md | 3 +++ docs/chaos/result.md | 3 +++ 5 files changed, 15 insertions(+) diff --git a/docs/chaos/behavior.md b/docs/chaos/behavior.md index 777de831800..31a39d38328 100644 --- a/docs/chaos/behavior.md +++ b/docs/chaos/behavior.md @@ -1,5 +1,8 @@ # Behavior chaos strategy +> [!IMPORTANT] +> This documentation page describes an upcoming feature of Polly. + ## About - **Options**: [`BehaviorStrategyOptions`](xref:Polly.Simmy.Behavior.BehaviorStrategyOptions) diff --git a/docs/chaos/fault.md b/docs/chaos/fault.md index 8f4c2deb3cf..1f7f4182083 100644 --- a/docs/chaos/fault.md +++ b/docs/chaos/fault.md @@ -1,5 +1,8 @@ # Fault chaos strategy +> [!IMPORTANT] +> This documentation page describes an upcoming feature of Polly. + ## About - **Options**: [`FaultStrategyOptions`](xref:Polly.Simmy.Fault.FaultStrategyOptions) diff --git a/docs/chaos/index.md b/docs/chaos/index.md index 2882f58f296..484abccfcff 100644 --- a/docs/chaos/index.md +++ b/docs/chaos/index.md @@ -1,5 +1,8 @@ # Chaos engineering with Simmy +> [!IMPORTANT] +> This documentation page describes an upcoming feature of Polly. + [Simmy][simmy] is a major new addition to Polly library, adding a chaos engineering and fault-injection dimension to Polly, through the provision of strategies to selectively inject faults, latency, custom behavior or fake results. ![Simmy](../media/simmy-logo.png) diff --git a/docs/chaos/latency.md b/docs/chaos/latency.md index b3595e17a48..9fcf430852d 100644 --- a/docs/chaos/latency.md +++ b/docs/chaos/latency.md @@ -1,5 +1,8 @@ # Latency chaos strategy +> [!IMPORTANT] +> This documentation page describes an upcoming feature of Polly. + ## About - **Options**: [`LatencyStrategyOptions`](xref:Polly.Simmy.Latency.LatencyStrategyOptions) diff --git a/docs/chaos/result.md b/docs/chaos/result.md index 0ec40f41cbe..c2ccffdf12c 100644 --- a/docs/chaos/result.md +++ b/docs/chaos/result.md @@ -1,5 +1,8 @@ # Outcome chaos strategy +> [!IMPORTANT] +> This documentation page describes an upcoming feature of Polly. + ## About - **Options**: From 8f6813e366b1504b590adfa86592fe8edd370d78 Mon Sep 17 00:00:00 2001 From: martintmk <103487740+martintmk@users.noreply.github.com> Date: Sun, 21 Jan 2024 21:09:18 +0100 Subject: [PATCH 11/12] Introduce `FaultGenerator` and `OutcomeGenerator` (#1911) --- src/Polly.Core/PublicAPI.Unshipped.txt | 16 ++- src/Polly.Core/Simmy/Fault/FaultGenerator.cs | 84 ++++++++++++ .../Simmy/Outcomes/OutcomeGenerator.cs | 120 ++++++++++++++++++ .../OutcomeStrategyOptions.TResult.cs | 29 ----- .../Simmy/Outcomes/OutcomeStrategyOptions.cs | 24 +++- src/Polly.Core/Simmy/Utils/GeneratorHelper.cs | 52 ++++++++ src/Polly.Core/Utils/RandomUtil.cs | 2 + .../Simmy/Fault/FaultGeneratorTests.cs | 49 +++++++ .../Simmy/Outcomes/OutcomeGeneratorTests.cs | 77 +++++++++++ .../Simmy/Utils/GeneratorHelperTests.cs | 56 ++++++++ 10 files changed, 475 insertions(+), 34 deletions(-) create mode 100644 src/Polly.Core/Simmy/Fault/FaultGenerator.cs create mode 100644 src/Polly.Core/Simmy/Outcomes/OutcomeGenerator.cs delete mode 100644 src/Polly.Core/Simmy/Outcomes/OutcomeStrategyOptions.TResult.cs create mode 100644 src/Polly.Core/Simmy/Utils/GeneratorHelper.cs create mode 100644 test/Polly.Core.Tests/Simmy/Fault/FaultGeneratorTests.cs create mode 100644 test/Polly.Core.Tests/Simmy/Outcomes/OutcomeGeneratorTests.cs create mode 100644 test/Polly.Core.Tests/Simmy/Utils/GeneratorHelperTests.cs diff --git a/src/Polly.Core/PublicAPI.Unshipped.txt b/src/Polly.Core/PublicAPI.Unshipped.txt index 68d606407b8..af7eab4a0e0 100644 --- a/src/Polly.Core/PublicAPI.Unshipped.txt +++ b/src/Polly.Core/PublicAPI.Unshipped.txt @@ -20,6 +20,11 @@ Polly.Simmy.EnabledGeneratorArguments Polly.Simmy.EnabledGeneratorArguments.Context.get -> Polly.ResilienceContext! Polly.Simmy.EnabledGeneratorArguments.EnabledGeneratorArguments() -> void Polly.Simmy.EnabledGeneratorArguments.EnabledGeneratorArguments(Polly.ResilienceContext! context) -> void +Polly.Simmy.Fault.FaultGenerator +Polly.Simmy.Fault.FaultGenerator.AddException(System.Func! generator, int weight = 100) -> Polly.Simmy.Fault.FaultGenerator! +Polly.Simmy.Fault.FaultGenerator.AddException(System.Func! generator, int weight = 100) -> Polly.Simmy.Fault.FaultGenerator! +Polly.Simmy.Fault.FaultGenerator.AddException(int weight = 100) -> Polly.Simmy.Fault.FaultGenerator! +Polly.Simmy.Fault.FaultGenerator.FaultGenerator() -> void Polly.Simmy.Fault.FaultGeneratorArguments Polly.Simmy.Fault.FaultGeneratorArguments.Context.get -> Polly.ResilienceContext! Polly.Simmy.Fault.FaultGeneratorArguments.FaultGeneratorArguments() -> void @@ -84,12 +89,17 @@ Polly.Simmy.Outcomes.OnOutcomeInjectedArguments.Context.get -> Polly.Re Polly.Simmy.Outcomes.OnOutcomeInjectedArguments.OnOutcomeInjectedArguments() -> void Polly.Simmy.Outcomes.OnOutcomeInjectedArguments.OnOutcomeInjectedArguments(Polly.ResilienceContext! context, Polly.Outcome outcome) -> void Polly.Simmy.Outcomes.OnOutcomeInjectedArguments.Outcome.get -> Polly.Outcome +Polly.Simmy.Outcomes.OutcomeGenerator +Polly.Simmy.Outcomes.OutcomeGenerator.AddException(System.Func! generator, int weight = 100) -> Polly.Simmy.Outcomes.OutcomeGenerator! +Polly.Simmy.Outcomes.OutcomeGenerator.AddException(System.Func! generator, int weight = 100) -> Polly.Simmy.Outcomes.OutcomeGenerator! +Polly.Simmy.Outcomes.OutcomeGenerator.AddException(int weight = 100) -> Polly.Simmy.Outcomes.OutcomeGenerator! +Polly.Simmy.Outcomes.OutcomeGenerator.AddResult(System.Func! generator, int weight = 100) -> Polly.Simmy.Outcomes.OutcomeGenerator! +Polly.Simmy.Outcomes.OutcomeGenerator.AddResult(System.Func! generator, int weight = 100) -> Polly.Simmy.Outcomes.OutcomeGenerator! +Polly.Simmy.Outcomes.OutcomeGenerator.OutcomeGenerator() -> void Polly.Simmy.Outcomes.OutcomeGeneratorArguments Polly.Simmy.Outcomes.OutcomeGeneratorArguments.Context.get -> Polly.ResilienceContext! Polly.Simmy.Outcomes.OutcomeGeneratorArguments.OutcomeGeneratorArguments() -> void Polly.Simmy.Outcomes.OutcomeGeneratorArguments.OutcomeGeneratorArguments(Polly.ResilienceContext! context) -> void -Polly.Simmy.Outcomes.OutcomeStrategyOptions -Polly.Simmy.Outcomes.OutcomeStrategyOptions.OutcomeStrategyOptions() -> void Polly.Simmy.Outcomes.OutcomeStrategyOptions Polly.Simmy.Outcomes.OutcomeStrategyOptions.OnOutcomeInjected.get -> System.Func, System.Threading.Tasks.ValueTask>? Polly.Simmy.Outcomes.OutcomeStrategyOptions.OnOutcomeInjected.set -> void @@ -98,9 +108,11 @@ Polly.Simmy.Outcomes.OutcomeStrategyOptions.OutcomeGenerator.set -> voi Polly.Simmy.Outcomes.OutcomeStrategyOptions.OutcomeStrategyOptions() -> void static Polly.Simmy.BehaviorPipelineBuilderExtensions.AddChaosBehavior(this TBuilder! builder, double injectionRate, System.Func! behavior) -> TBuilder! static Polly.Simmy.BehaviorPipelineBuilderExtensions.AddChaosBehavior(this TBuilder! builder, Polly.Simmy.Behavior.BehaviorStrategyOptions! options) -> TBuilder! +static Polly.Simmy.Fault.FaultGenerator.implicit operator System.Func>!(Polly.Simmy.Fault.FaultGenerator! generator) -> System.Func>! static Polly.Simmy.FaultPipelineBuilderExtensions.AddChaosFault(this TBuilder! builder, double injectionRate, System.Func! faultGenerator) -> TBuilder! static Polly.Simmy.FaultPipelineBuilderExtensions.AddChaosFault(this TBuilder! builder, Polly.Simmy.Fault.FaultStrategyOptions! options) -> TBuilder! static Polly.Simmy.LatencyPipelineBuilderExtensions.AddChaosLatency(this TBuilder! builder, double injectionRate, System.TimeSpan latency) -> TBuilder! static Polly.Simmy.LatencyPipelineBuilderExtensions.AddChaosLatency(this TBuilder! builder, Polly.Simmy.Latency.LatencyStrategyOptions! options) -> TBuilder! static Polly.Simmy.OutcomePipelineBuilderExtensions.AddChaosResult(this Polly.ResiliencePipelineBuilder! builder, double injectionRate, System.Func! resultGenerator) -> Polly.ResiliencePipelineBuilder! static Polly.Simmy.OutcomePipelineBuilderExtensions.AddChaosResult(this Polly.ResiliencePipelineBuilder! builder, Polly.Simmy.Outcomes.OutcomeStrategyOptions! options) -> Polly.ResiliencePipelineBuilder! +static Polly.Simmy.Outcomes.OutcomeGenerator.implicit operator System.Func?>>!(Polly.Simmy.Outcomes.OutcomeGenerator! generator) -> System.Func?>>! diff --git a/src/Polly.Core/Simmy/Fault/FaultGenerator.cs b/src/Polly.Core/Simmy/Fault/FaultGenerator.cs new file mode 100644 index 00000000000..374faee6a70 --- /dev/null +++ b/src/Polly.Core/Simmy/Fault/FaultGenerator.cs @@ -0,0 +1,84 @@ +using System.ComponentModel; +using Polly.Simmy.Utils; + +namespace Polly.Simmy.Fault; + +#pragma warning disable CA2225 // Operator overloads have named alternates +#pragma warning disable RS0026 // Do not add multiple public overloads with optional parameters + +/// +/// A generator for creating faults (exceptions) using registered delegate functions. +/// +/// +/// An instance of this class can be assigned to the property. +/// +public sealed class FaultGenerator +{ + private const int DefaultWeight = 100; + + private readonly GeneratorHelper _helper; + + /// + /// Initializes a new instance of the class. + /// + public FaultGenerator() + => _helper = new GeneratorHelper(RandomUtil.Instance.Next); + + /// + /// Registers an exception generator delegate. + /// + /// The delegate that generates the exception. + /// The weight assigned to this generator. Defaults to 100. + /// The current instance of . + public FaultGenerator AddException(Func generator, int weight = DefaultWeight) + { + Guard.NotNull(generator); + + _helper.AddOutcome(_ => Outcome.FromException(generator()), weight); + + return this; + } + + /// + /// Registers an exception generator delegate that accepts a . + /// + /// The delegate that generates the exception, accepting a . + /// The weight assigned to this generator. Defaults to 100. + /// The current instance of . + public FaultGenerator AddException(Func generator, int weight = DefaultWeight) + { + Guard.NotNull(generator); + + _helper.AddOutcome(context => Outcome.FromException(generator(context)), weight); + + return this; + } + + /// + /// Registers an exception generator for a specific exception type, using the default constructor of that exception. + /// + /// The type of the exception to generate. + /// The weight assigned to this generator. Defaults to 100. + /// The current instance of . + public FaultGenerator AddException(int weight = DefaultWeight) + where TException : Exception, new() + { + _helper.AddOutcome(_ => Outcome.FromException(new TException()), weight); + + return this; + } + + /// + /// Provides an implicit conversion from to a delegate compatible with . + /// + /// The instance of . + [EditorBrowsable(EditorBrowsableState.Never)] + public static implicit operator Func>(FaultGenerator generator) + { + Guard.NotNull(generator); + + var generatorDelegate = generator._helper.CreateGenerator(); + + return args => new ValueTask(generatorDelegate(args.Context)!.Value.Exception); + } +} diff --git a/src/Polly.Core/Simmy/Outcomes/OutcomeGenerator.cs b/src/Polly.Core/Simmy/Outcomes/OutcomeGenerator.cs new file mode 100644 index 00000000000..3d1439ab80f --- /dev/null +++ b/src/Polly.Core/Simmy/Outcomes/OutcomeGenerator.cs @@ -0,0 +1,120 @@ +using System.ComponentModel; +using Polly.Simmy.Utils; + +namespace Polly.Simmy.Outcomes; + +#pragma warning disable CA2225 // Operator overloads have named alternates +#pragma warning disable RS0026 // Do not add multiple public overloads with optional parameters + +/// +/// Generator that produces faults such as exceptions or results. +/// +/// The type of the result. +/// +/// An instance of this class is assignable to . +/// +public sealed class OutcomeGenerator +{ + private const int DefaultWeight = 100; + private readonly GeneratorHelper _helper; + + /// + /// Initializes a new instance of the class. + /// + public OutcomeGenerator() + : this(RandomUtil.Instance.Next) + { + } + + internal OutcomeGenerator(Func weightGenerator) + => _helper = new GeneratorHelper(weightGenerator); + + /// + /// Registers an exception generator delegate. + /// + /// The delegate that generates the exception. + /// The weight assigned to this generator. Defaults to 100. + /// The current instance of . + public OutcomeGenerator AddException(Func generator, int weight = DefaultWeight) + { + Guard.NotNull(generator); + + _helper.AddOutcome(_ => Outcome.FromException(generator()), weight); + + return this; + } + + /// + /// Registers an exception generator delegate that accepts a . + /// + /// The delegate that generates the exception, accepting a . + /// The weight assigned to this generator. Defaults to 100. + /// The current instance of . + public OutcomeGenerator AddException(Func generator, int weight = DefaultWeight) + { + Guard.NotNull(generator); + + _helper.AddOutcome(context => Outcome.FromException(generator(context)), weight); + + return this; + } + + /// + /// Registers an exception generator for a specific exception type, using the default constructor of that exception. + /// + /// The type of the exception to generate. + /// The weight assigned to this generator. Defaults to 100. + /// The current instance of . + public OutcomeGenerator AddException(int weight = DefaultWeight) + where TException : Exception, new() + { + _helper.AddOutcome(_ => Outcome.FromException(new TException()), weight); + + return this; + } + + /// + /// Registers a result generator. + /// + /// The delegate that generates the result. + /// The weight assigned to this generator. Defaults to 100. + /// The current instance of . + public OutcomeGenerator AddResult(Func generator, int weight = DefaultWeight) + { + Guard.NotNull(generator); + + _helper.AddOutcome(_ => Outcome.FromResult(generator()), weight); + + return this; + } + + /// + /// Registers a result generator. + /// + /// The delegate that generates the result, accepting a . + /// The weight assigned to this generator. Defaults to 100. + /// The current instance of . + public OutcomeGenerator AddResult(Func generator, int weight = DefaultWeight) + { + Guard.NotNull(generator); + + _helper.AddOutcome(context => Outcome.FromResult(generator(context)), weight); + + return this; + } + + /// + /// Implicit conversion to . + /// + /// The generator instance. + [EditorBrowsable(EditorBrowsableState.Never)] + public static implicit operator Func?>>(OutcomeGenerator generator) + { + Guard.NotNull(generator); + + var generatorDelegate = generator._helper.CreateGenerator(); + + return args => new ValueTask?>(generatorDelegate(args.Context)); + } +} + diff --git a/src/Polly.Core/Simmy/Outcomes/OutcomeStrategyOptions.TResult.cs b/src/Polly.Core/Simmy/Outcomes/OutcomeStrategyOptions.TResult.cs deleted file mode 100644 index 44a2ba1e9bb..00000000000 --- a/src/Polly.Core/Simmy/Outcomes/OutcomeStrategyOptions.TResult.cs +++ /dev/null @@ -1,29 +0,0 @@ -using System.ComponentModel.DataAnnotations; - -namespace Polly.Simmy.Outcomes; - -/// -/// Represents the options for the Outcome chaos strategy. -/// -/// The type of the outcome that was injected. -public class OutcomeStrategyOptions : MonkeyStrategyOptions -{ - /// - /// Initializes a new instance of the class. - /// - public OutcomeStrategyOptions() => Name = OutcomeConstants.DefaultName; - - /// - /// Gets or sets the delegate that's raised when the outcome is injected. - /// - /// - /// Defaults to . - /// - public Func, ValueTask>? OnOutcomeInjected { get; set; } - - /// - /// Gets or sets the outcome generator to be injected for a given execution. - /// - [Required] - public Func?>> OutcomeGenerator { get; set; } = default!; -} diff --git a/src/Polly.Core/Simmy/Outcomes/OutcomeStrategyOptions.cs b/src/Polly.Core/Simmy/Outcomes/OutcomeStrategyOptions.cs index 7a29aa8ef5d..fa8f21fddad 100644 --- a/src/Polly.Core/Simmy/Outcomes/OutcomeStrategyOptions.cs +++ b/src/Polly.Core/Simmy/Outcomes/OutcomeStrategyOptions.cs @@ -1,6 +1,24 @@ -namespace Polly.Simmy.Outcomes; +using System.ComponentModel.DataAnnotations; -/// -public class OutcomeStrategyOptions : OutcomeStrategyOptions +namespace Polly.Simmy.Outcomes; + +/// +/// Represents the options for the Outcome chaos strategy. +/// +/// The type of the outcome that was injected. +public class OutcomeStrategyOptions : MonkeyStrategyOptions { + /// + /// Gets or sets the delegate that's invoked when the outcome is injected. + /// + /// + /// Defaults to . + /// + public Func, ValueTask>? OnOutcomeInjected { get; set; } + + /// + /// Gets or sets the outcome generator to be injected for a given execution. + /// + [Required] + public Func?>> OutcomeGenerator { get; set; } = default!; } diff --git a/src/Polly.Core/Simmy/Utils/GeneratorHelper.cs b/src/Polly.Core/Simmy/Utils/GeneratorHelper.cs new file mode 100644 index 00000000000..6ec4090a0f2 --- /dev/null +++ b/src/Polly.Core/Simmy/Utils/GeneratorHelper.cs @@ -0,0 +1,52 @@ +namespace Polly.Simmy.Utils; + +internal sealed class GeneratorHelper +{ + private readonly Func _weightGenerator; + + private readonly List _weights = []; + private readonly List>> _factories = []; + private int _totalWeight; + + public GeneratorHelper(Func weightGenerator) => _weightGenerator = weightGenerator; + + public void AddOutcome(Func> generator, int weight) + { + Guard.NotNull(generator); + + _totalWeight += weight; + _factories.Add(generator); + _weights.Add(weight); + } + + internal Func?> CreateGenerator() + { + if (_factories.Count == 0) + { + return _ => null; + } + + var totalWeight = _totalWeight; + var factories = _factories.ToArray(); + var weights = _weights.ToArray(); + var generator = _weightGenerator; + + return context => + { + var generatedWeight = generator(totalWeight); + var weight = 0; + + for (var i = 0; i < factories.Length; i++) + { + weight += weights[i]; + if (generatedWeight < weight) + { + return factories[i](context); + } + } + + return null; + }; + } +} + diff --git a/src/Polly.Core/Utils/RandomUtil.cs b/src/Polly.Core/Utils/RandomUtil.cs index aac063fcc7a..de7bc6ff6ce 100644 --- a/src/Polly.Core/Utils/RandomUtil.cs +++ b/src/Polly.Core/Utils/RandomUtil.cs @@ -13,4 +13,6 @@ internal sealed class RandomUtil public RandomUtil(int? seed) => _random = new ThreadLocal(() => seed == null ? new Random() : new Random(seed.Value)); public double NextDouble() => _random.Value!.NextDouble(); + + public int Next(int maxValue) => _random.Value!.Next(maxValue); } diff --git a/test/Polly.Core.Tests/Simmy/Fault/FaultGeneratorTests.cs b/test/Polly.Core.Tests/Simmy/Fault/FaultGeneratorTests.cs new file mode 100644 index 00000000000..0ffe3c121cf --- /dev/null +++ b/test/Polly.Core.Tests/Simmy/Fault/FaultGeneratorTests.cs @@ -0,0 +1,49 @@ +using System; +using Polly.Simmy.Fault; + +namespace Polly.Core.Tests.Simmy.Fault; + +public class FaultGeneratorTests +{ + [Fact] + public void AddException_Generic_Ok() + { + var generator = new FaultGenerator(); + + generator.AddException(); + + Generate(generator).Should().BeOfType(); + } + + [Fact] + public void AddException_Factory_Ok() + { + var generator = new FaultGenerator(); + + generator.AddException(() => new InvalidOperationException()); + + Generate(generator).Should().BeOfType(); + } + + [Fact] + public void AddException_FactoryWithResilienceContext_Ok() + { + var generator = new FaultGenerator(); + + generator.AddException(context => + { + context.Should().NotBeNull(); + + return new InvalidOperationException(); + }); + + Generate(generator).Should().BeOfType(); + } + + private static Exception? Generate(FaultGenerator generator) + { + Func> func = generator; + + return func(new FaultGeneratorArguments(ResilienceContextPool.Shared.Get())).AsTask().Result; + } +} diff --git a/test/Polly.Core.Tests/Simmy/Outcomes/OutcomeGeneratorTests.cs b/test/Polly.Core.Tests/Simmy/Outcomes/OutcomeGeneratorTests.cs new file mode 100644 index 00000000000..924de797690 --- /dev/null +++ b/test/Polly.Core.Tests/Simmy/Outcomes/OutcomeGeneratorTests.cs @@ -0,0 +1,77 @@ +using System; +using Polly.Simmy.Outcomes; + +namespace Polly.Core.Tests.Simmy.Outcomes; + +public class OutcomeGeneratorTests +{ + [Fact] + public void AddException_Generic_Ok() + { + var generator = new OutcomeGenerator(); + + generator.AddException(); + + Generate(generator)!.Value.Exception.Should().BeOfType(); + } + + [Fact] + public void AddException_Factory_Ok() + { + var generator = new OutcomeGenerator(); + + generator.AddException(() => new InvalidOperationException()); + + Generate(generator)!.Value.Exception.Should().BeOfType(); + } + + [Fact] + public void AddException_FactoryWithResilienceContext_Ok() + { + var generator = new OutcomeGenerator(); + + generator.AddException(context => + { + context.Should().NotBeNull(); + + return new InvalidOperationException(); + }); + + Generate(generator)!.Value.Exception.Should().BeOfType(); + } + + [Fact] + public void AddResult_Factory_Ok() + { + var generator = new OutcomeGenerator(); + + generator.AddResult(() => + { + return "dummy"; + }); + + Generate(generator)!.Value.Result.Should().Be("dummy"); + } + + [Fact] + public void AddResult_FactoryWithResilienceContext_Ok() + { + var generator = new OutcomeGenerator(); + + generator.AddResult(context => + { + context.Should().NotBeNull(); + + return "dummy"; + }); + + Generate(generator)!.Value.Result.Should().Be("dummy"); + } + + private static Outcome? Generate(OutcomeGenerator generator) + { + Func?>> func = generator; + + return func(new OutcomeGeneratorArguments(ResilienceContextPool.Shared.Get())).AsTask().Result; + } +} diff --git a/test/Polly.Core.Tests/Simmy/Utils/GeneratorHelperTests.cs b/test/Polly.Core.Tests/Simmy/Utils/GeneratorHelperTests.cs new file mode 100644 index 00000000000..258124cec77 --- /dev/null +++ b/test/Polly.Core.Tests/Simmy/Utils/GeneratorHelperTests.cs @@ -0,0 +1,56 @@ +using System; +using Polly.Simmy.Utils; + +namespace Polly.Core.Tests.Simmy.Utils; + +public class GeneratorHelperTests +{ + [Fact] + public void CreateGenerator_NoGenerators_Ok() + { + var helper = new GeneratorHelper(_ => 10); + + helper.CreateGenerator()(ResilienceContextPool.Shared.Get()).Should().BeNull(); + } + + [Fact] + public void AddOutcome_EnsureWeightRespected() + { + int weight = 0; + int maxWeight = 0; + var context = ResilienceContextPool.Shared.Get(); + + var helper = new GeneratorHelper(max => + { + maxWeight = max; + return weight; + }); + + helper.AddOutcome(_ => Outcome.FromResult(1), 40); + helper.AddOutcome(_ => Outcome.FromResult(2), 80); + + var generator = helper.CreateGenerator(); + + weight = 0; + generator(context)!.Value.Result.Should().Be(1); + weight = 39; + generator(context)!.Value.Result.Should().Be(1); + + weight = 40; + generator(context)!.Value.Result.Should().Be(2); + + maxWeight.Should().Be(120); + } + + [Fact] + public void Generator_OutsideRange_ReturnsNull() + { + var context = ResilienceContextPool.Shared.Get(); + var helper = new GeneratorHelper(_ => 1000); + + helper.AddOutcome(_ => Outcome.FromResult(1), 40); + + var generator = helper.CreateGenerator(); + generator(context).Should().BeNull(); + } +} From ad256009957ebfb3ab5a03c66a2dc3157868e8fd Mon Sep 17 00:00:00 2001 From: peter-csala Date: Fri, 19 Jan 2024 11:34:23 +0100 Subject: [PATCH 12/12] Apply suggestions for Outcome chaos strategy --- src/Polly.Core/Simmy/Outcomes/OutcomeStrategyOptions.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Polly.Core/Simmy/Outcomes/OutcomeStrategyOptions.cs b/src/Polly.Core/Simmy/Outcomes/OutcomeStrategyOptions.cs index fa8f21fddad..31867aafbf2 100644 --- a/src/Polly.Core/Simmy/Outcomes/OutcomeStrategyOptions.cs +++ b/src/Polly.Core/Simmy/Outcomes/OutcomeStrategyOptions.cs @@ -8,6 +8,11 @@ namespace Polly.Simmy.Outcomes; /// The type of the outcome that was injected. public class OutcomeStrategyOptions : MonkeyStrategyOptions { + /// + /// Initializes a new instance of the class. + /// + public OutcomeStrategyOptions() => Name = OutcomeConstants.DefaultName; + /// /// Gets or sets the delegate that's invoked when the outcome is injected. ///