diff --git a/CHANGELOG.md b/CHANGELOG.md index dc55530..43fa5c2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,2 +1,6 @@ +## 0.2.0 +- Makes InjectLatency policies cancellable (both sync and async) +- Add support for cancellation on async configuration-providing delegates + ## 0.1.0 -- Initial launch \ No newline at end of file +- Initial launch diff --git a/GitVersionConfig.yaml b/GitVersionConfig.yaml index c12fe19..362dfe7 100644 --- a/GitVersionConfig.yaml +++ b/GitVersionConfig.yaml @@ -1 +1 @@ -next-version: 0.1.0 \ No newline at end of file +next-version: 0.2.0 \ No newline at end of file diff --git a/README.md b/README.md index 28429db..57d9feb 100644 --- a/README.md +++ b/README.md @@ -43,27 +43,27 @@ Simmy offers the following chaos-injection policies: ## Inject fault ```csharp var chaosPolicy = MonkeyPolicy.InjectFault( - Exception | Func fault, - double | Func injectionRate, - Func | Func enabled + Exception | Func fault, + double | Func injectionRate, + Func | Func enabled ); ``` ## Inject latency ```csharp var chaosPolicy = MonkeyPolicy.InjectLatency( - TimeSpan | Func latency, - double | Func injectionRate, - Func | Func enabled + TimeSpan | Func latency, + double | Func injectionRate, + Func | Func enabled ); ``` ## Inject behavior ```csharp var chaosPolicy = MonkeyPolicy.InjectBehaviour( - Action | Action behaviour, - double | Func injectionRate, - Func | Func enabled + Action | Action behaviour, + double | Func injectionRate, + Func | Func enabled ); ``` @@ -183,4 +183,4 @@ Simmy was [the brainchild of](https://github.com/App-vNext/Polly/issues/499) [@m ### Samples * [Dylan Reisenberger](http://www.thepollyproject.org/author/dylan/) presents an [intentionally simple example](https://github.com/Polly-Contrib/Polly.Contrib.SimmyDemo_WebApi) .NET Core WebAPI app demonstrating how we can set up Simmy chaos policies for certain environments and without changing any existing configuration code injecting faults or chaos by modifying external configuration. -* [Geovanny Alzate Sandoval](https://github.com/vany0114) made a [microservices based sample application](https://github.com/vany0114/chaos-injection-using-simmy) to demonstrate how chaos engineering works with Simmy using chaos policies in a distributed system and how we can inject even a custom behavior given our needs or infrastructure, this time injecting custom behavior to generate chaos in our Service Fabric Cluster. \ No newline at end of file +* [Geovanny Alzate Sandoval](https://github.com/vany0114) made a [microservices based sample application](https://github.com/vany0114/chaos-injection-using-simmy) to demonstrate how chaos engineering works with Simmy using chaos policies in a distributed system and how we can inject even a custom behavior given our needs or infrastructure, this time injecting custom behavior to generate chaos in our Service Fabric Cluster. diff --git a/src/Polly.Contrib.Simmy.Specs/Fault/InjectFaultAsyncSpecs.cs b/src/Polly.Contrib.Simmy.Specs/Fault/InjectFaultAsyncSpecs.cs index a6669d9..1b33ef7 100644 --- a/src/Polly.Contrib.Simmy.Specs/Fault/InjectFaultAsyncSpecs.cs +++ b/src/Polly.Contrib.Simmy.Specs/Fault/InjectFaultAsyncSpecs.cs @@ -69,7 +69,7 @@ public void InjectFault_With_Context_Should_not_execute_user_delegate_async() var policy = MonkeyPolicy.InjectFaultAsync( new Exception("test"), 0.6, - async (ctx) => + async (ctx, ct) => { return await Task.FromResult((bool)ctx["ShouldFail"]); }); @@ -89,7 +89,7 @@ public void InjectFault_With_Context_Should_execute_user_delegate_async() var policy = MonkeyPolicy.InjectFaultAsync( new Exception("test"), 0.4, - async (ctx) => + async (ctx, ct) => { return await Task.FromResult((bool)ctx["ShouldFail"]); }); @@ -108,7 +108,7 @@ public void InjectFault_With_Context_Should_execute_user_delegate_async_with_ena var policy = MonkeyPolicy.InjectFaultAsync( new Exception("test"), 0.6, - async (ctx) => + async (ctx, ct) => { return await Task.FromResult((bool)ctx["ShouldFail"]); }); @@ -128,8 +128,8 @@ public void InjectFault_should_throw_if_injection_rate_is_out_of_range_too_low() Context context = new Context(); Func> fault = (ctx, cts) => Task.FromResult(new Exception()); - Func> enabled = (ctx) => Task.FromResult(true); - Func> injectionRate = (ctx) => Task.FromResult(-0.1); + Func> enabled = (ctx, ct) => Task.FromResult(true); + Func> injectionRate = (ctx, ct) => Task.FromResult(-0.1); var policy = MonkeyPolicy.InjectFaultAsync(fault, injectionRate, enabled); Func actionAsync = (_) => { executed = true; return TaskHelper.EmptyTask; }; @@ -144,8 +144,8 @@ public void InjectFault_should_throw_if_injection_rate_is_out_of_range_too_high( Context context = new Context(); Func> fault = (ctx, cts) => Task.FromResult(new Exception()); - Func> enabled = (ctx) => Task.FromResult(true); - Func> injectionRate = (ctx) => Task.FromResult(1.01); + Func> enabled = (ctx, ct) => Task.FromResult(true); + Func> injectionRate = (ctx, ct) => Task.FromResult(1.01); var policy = MonkeyPolicy.InjectFaultAsync(fault, injectionRate, enabled); Func actionAsync = (_) => { executed = true; return TaskHelper.EmptyTask; }; @@ -160,8 +160,8 @@ public void InjectFault_With_Context_Should_not_execute_user_delegate_async_basi Context context = new Context(); Func> fault = (ctx, cts) => Task.FromResult(new Exception()); - Func> enabled = (ctx) => Task.FromResult(true); - Func> injectionRate = (ctx) => Task.FromResult(0.6); + Func> enabled = (ctx, ct) => Task.FromResult(true); + Func> injectionRate = (ctx, ct) => Task.FromResult(0.6); var policy = MonkeyPolicy.InjectFaultAsync(fault, injectionRate, enabled); Func actionAsync = (_) => { executed = true; return TaskHelper.EmptyTask; }; @@ -190,7 +190,7 @@ public void InjectFault_With_Context_Should_not_execute_user_delegate_async_with return Task.FromResult(new Exception()); }; - Func> injectionRate = (ctx) => + Func> injectionRate = (ctx, ct) => { double rate = 0; if (ctx["InjectionRate"] != null) @@ -201,7 +201,7 @@ public void InjectFault_With_Context_Should_not_execute_user_delegate_async_with return Task.FromResult(rate); }; - Func> enabled = (ctx) => + Func> enabled = (ctx, ct) => { return Task.FromResult((bool)ctx["ShouldFail"]); }; @@ -214,5 +214,225 @@ public void InjectFault_With_Context_Should_not_execute_user_delegate_async_with executed.Should().BeFalse(); } #endregion + + #region Cancellable scenarios + + [Fact] + public void InjectFault_With_Context_Should_not_execute_user_delegate_if_user_cancelationtoken_cancelled_before_to_start_execution() + { + string failureMessage = "Failure Message"; + Boolean executed = false; + Context context = new Context(); + context["ShouldFail"] = true; + context["Message"] = failureMessage; + context["InjectionRate"] = 0.6; + Func actionAsync = (ctx, ct) => + { + executed = true; + return TaskHelper.EmptyTask; + }; + + Func> fault = (ctx, cts) => + { + if (ctx["Message"] != null) + { + Exception ex = new InvalidOperationException(ctx["Message"].ToString()); + return Task.FromResult(ex); + } + + return Task.FromResult(new Exception()); + }; + + Func> injectionRate = (ctx, ct) => + { + double rate = 0; + if (ctx["InjectionRate"] != null) + { + rate = (double)ctx["InjectionRate"]; + } + + return Task.FromResult(rate); + }; + + Func> enabled = (ctx, ct) => + { + return Task.FromResult((bool)ctx["ShouldFail"]); + }; + + var policy = MonkeyPolicy.InjectFaultAsync(fault, injectionRate, enabled); + using (CancellationTokenSource cts = new CancellationTokenSource()) + { + cts.Cancel(); + + policy.Awaiting(async x => await x.ExecuteAsync(actionAsync, context, cts.Token)) + .ShouldThrow(); + } + + executed.Should().BeFalse(); + } + + [Fact] + public void InjectFault_With_Context_Should_not_execute_user_delegate_if_user_cancelationtoken_cancelled_on_enabled_config_delegate() + { + string failureMessage = "Failure Message"; + Boolean executed = false; + Context context = new Context(); + context["ShouldFail"] = true; + context["Message"] = failureMessage; + context["InjectionRate"] = 0.6; + Func actionAsync = (ctx, ct) => + { + executed = true; + return TaskHelper.EmptyTask; + }; + + Func> fault = (ctx, cts) => + { + if (ctx["Message"] != null) + { + Exception ex = new InvalidOperationException(ctx["Message"].ToString()); + return Task.FromResult(ex); + } + + return Task.FromResult(new Exception()); + }; + + Func> injectionRate = (ctx, ct) => + { + double rate = 0; + if (ctx["InjectionRate"] != null) + { + rate = (double)ctx["InjectionRate"]; + } + + return Task.FromResult(rate); + }; + + using (CancellationTokenSource cts = new CancellationTokenSource()) + { + Func> enabled = (ctx, ct) => + { + cts.Cancel(); + return Task.FromResult((bool)ctx["ShouldFail"]); + }; + + var policy = MonkeyPolicy.InjectFaultAsync(fault, injectionRate, enabled); + + policy.Awaiting(async x => await x.ExecuteAsync(actionAsync, context, cts.Token)) + .ShouldThrow(); + } + + executed.Should().BeFalse(); + } + + [Fact] + public void InjectFault_With_Context_Should_not_execute_user_delegate_if_user_cancelationtoken_cancelled_on_injectionrate_config_delegate() + { + string failureMessage = "Failure Message"; + Boolean executed = false; + Context context = new Context(); + context["ShouldFail"] = true; + context["Message"] = failureMessage; + context["InjectionRate"] = 0.6; + Func actionAsync = (ctx, ct) => + { + executed = true; + return TaskHelper.EmptyTask; + }; + + Func> fault = (ctx, cts) => + { + if (ctx["Message"] != null) + { + Exception ex = new InvalidOperationException(ctx["Message"].ToString()); + return Task.FromResult(ex); + } + + return Task.FromResult(new Exception()); + }; + + using (CancellationTokenSource cts = new CancellationTokenSource()) + { + Func> enabled = (ctx, ct) => + { + return Task.FromResult((bool)ctx["ShouldFail"]); + }; + + Func> injectionRate = (ctx, ct) => + { + double rate = 0; + if (ctx["InjectionRate"] != null) + { + rate = (double)ctx["InjectionRate"]; + } + + cts.Cancel(); + return Task.FromResult(rate); + }; + + var policy = MonkeyPolicy.InjectFaultAsync(fault, injectionRate, enabled); + + policy.Awaiting(async x => await x.ExecuteAsync(actionAsync, context, cts.Token)) + .ShouldThrow(); + } + + executed.Should().BeFalse(); + } + + [Fact] + public void InjectFault_With_Context_Should_not_execute_user_delegate_if_user_cancelationtoken_cancelled_on_fault_config_delegate() + { + string failureMessage = "Failure Message"; + Boolean executed = false; + Context context = new Context(); + context["ShouldFail"] = true; + context["Message"] = failureMessage; + context["InjectionRate"] = 0.6; + Func actionAsync = (ctx, ct) => + { + executed = true; + return TaskHelper.EmptyTask; + }; + + using (CancellationTokenSource cts = new CancellationTokenSource()) + { + Func> enabled = (ctx, ct) => + { + return Task.FromResult((bool)ctx["ShouldFail"]); + }; + + Func> injectionRate = (ctx, ct) => + { + double rate = 0; + if (ctx["InjectionRate"] != null) + { + rate = (double)ctx["InjectionRate"]; + } + + return Task.FromResult(rate); + }; + + Func> fault = (ctx, ct) => + { + cts.Cancel(); + if (ctx["Message"] != null) + { + Exception ex = new InvalidOperationException(ctx["Message"].ToString()); + return Task.FromResult(ex); + } + + return Task.FromResult(new Exception()); + }; + + var policy = MonkeyPolicy.InjectFaultAsync(fault, injectionRate, enabled); + + policy.Awaiting(async x => await x.ExecuteAsync(actionAsync, context, cts.Token)) + .ShouldThrow(); + } + + executed.Should().BeFalse(); + } + + #endregion } } diff --git a/src/Polly.Contrib.Simmy.Specs/Fault/InjectFaultSpecs.cs b/src/Polly.Contrib.Simmy.Specs/Fault/InjectFaultSpecs.cs index 46cdd1e..45433fc 100644 --- a/src/Polly.Contrib.Simmy.Specs/Fault/InjectFaultSpecs.cs +++ b/src/Polly.Contrib.Simmy.Specs/Fault/InjectFaultSpecs.cs @@ -1,4 +1,5 @@ using System; +using System.Threading; using FluentAssertions; using Polly.Contrib.Simmy.Utilities; using Xunit; @@ -71,7 +72,7 @@ public void InjectFault_With_Context_Should_not_execute_user_delegate() var policy = MonkeyPolicy.InjectFault( new Exception("test"), 0.6, - (ctx) => + (ctx, ct) => { return ((bool)ctx["ShouldFail"]); }); @@ -92,7 +93,7 @@ public void InjectFault_With_Context_Should_execute_user_delegate() var policy = MonkeyPolicy.InjectFault( new Exception("test"), 0.4, - (ctx) => + (ctx, ct) => { return ((bool)ctx["ShouldFail"]); }); @@ -113,7 +114,7 @@ public void InjectFault_With_Context_Should_execute_user_delegate_with_enabled_l var policy = MonkeyPolicy.InjectFault( new Exception("test"), 0.6, - (ctx) => + (ctx, ct) => { return ((bool)ctx["ShouldFail"]); }); @@ -133,9 +134,9 @@ public void InjectFault_should_throw_if_injection_rate_is_out_of_range_too_low() Boolean executed = false; Context context = new Context(); - Func fault = (ctx) => new Exception(); - Func injectionRate = (ctx) => -0.1; - Func enabled = (ctx) => true; + Func fault = (ctx, ct) => new Exception(); + Func injectionRate = (ctx, ct) => -0.1; + Func enabled = (ctx, ct) => true; var policy = MonkeyPolicy.InjectFault(fault, injectionRate, enabled); policy.Invoking(x => x.Execute((ctx) => { executed = true; }, context)) .ShouldThrowExactly(); @@ -149,9 +150,9 @@ public void InjectFault_should_throw_if_injection_rate_is_out_of_range_too_high( Boolean executed = false; Context context = new Context(); - Func fault = (ctx) => new Exception(); - Func injectionRate = (ctx) => 1.01; - Func enabled = (ctx) => true; + Func fault = (ctx, ct) => new Exception(); + Func injectionRate = (ctx, ct) => 1.01; + Func enabled = (ctx, ct) => true; var policy = MonkeyPolicy.InjectFault(fault, injectionRate, enabled); policy.Invoking(x => x.Execute((ctx) => { executed = true; }, context)) .ShouldThrowExactly(); @@ -165,9 +166,9 @@ public void InjectFault_With_Context_Should_not_execute_user_delegate_with_defau Boolean executed = false; Context context = new Context(); - Func fault = (ctx) => new Exception(); - Func injectionRate = (ctx) => 0.6; - Func enabled = (ctx) => true; + Func fault = (ctx, ct) => new Exception(); + Func injectionRate = (ctx, ct) => 0.6; + Func enabled = (ctx, ct) => true; var policy = MonkeyPolicy.InjectFault(fault, injectionRate, enabled); policy.Invoking(x => x.Execute((ctx) => { executed = true; }, context)) .ShouldThrowExactly(); @@ -185,7 +186,7 @@ public void InjectFault_With_Context_Should_not_execute_user_delegate_with_all_c context["Message"] = failureMessage; context["InjectionRate"] = 0.6; - Func fault = (ctx) => + Func fault = (ctx, ct) => { if (ctx["Message"] != null) { @@ -195,7 +196,7 @@ public void InjectFault_With_Context_Should_not_execute_user_delegate_with_all_c return new Exception(); }; - Func injectionRate = (ctx) => + Func injectionRate = (ctx, ct) => { if (ctx["InjectionRate"] != null) { @@ -205,7 +206,7 @@ public void InjectFault_With_Context_Should_not_execute_user_delegate_with_all_c return 0; }; - Func enabled = (ctx) => + Func enabled = (ctx, ct) => { return ((bool)ctx["ShouldFail"]); }; @@ -217,5 +218,205 @@ public void InjectFault_With_Context_Should_not_execute_user_delegate_with_all_c executed.Should().BeFalse(); } #endregion + + #region Cancellable scenarios + + [Fact] + public void InjectFault_With_Context_Should_not_execute_user_delegate_if_user_cancelationtoken_cancelled_before_to_start_execution() + { + string failureMessage = "Failure Message"; + Boolean executed = false; + Context context = new Context(); + context["ShouldFail"] = true; + context["Message"] = failureMessage; + context["InjectionRate"] = 0.6; + + Func fault = (ctx, cts) => + { + if (ctx["Message"] != null) + { + Exception ex = new InvalidOperationException(ctx["Message"].ToString()); + return ex; + } + + return new Exception(); + }; + + Func injectionRate = (ctx, ct) => + { + double rate = 0; + if (ctx["InjectionRate"] != null) + { + rate = (double)ctx["InjectionRate"]; + } + + return rate; + }; + + Func enabled = (ctx, ct) => + { + return (bool)ctx["ShouldFail"]; + }; + + var policy = MonkeyPolicy.InjectFault(fault, injectionRate, enabled); + using (CancellationTokenSource cts = new CancellationTokenSource()) + { + cts.Cancel(); + + policy.Invoking(x => x.Execute((ctx, ct) => { executed = true; }, context, cts.Token)) + .ShouldThrow(); + } + + executed.Should().BeFalse(); + } + + [Fact] + public void InjectFault_With_Context_Should_not_execute_user_delegate_if_user_cancelationtoken_cancelled_on_enabled_config_delegate() + { + string failureMessage = "Failure Message"; + Boolean executed = false; + Context context = new Context(); + context["ShouldFail"] = true; + context["Message"] = failureMessage; + context["InjectionRate"] = 0.6; + + Func fault = (ctx, cts) => + { + if (ctx["Message"] != null) + { + Exception ex = new InvalidOperationException(ctx["Message"].ToString()); + return ex; + } + + return new Exception(); + }; + + Func injectionRate = (ctx, ct) => + { + double rate = 0; + if (ctx["InjectionRate"] != null) + { + rate = (double)ctx["InjectionRate"]; + } + + return rate; + }; + + using (CancellationTokenSource cts = new CancellationTokenSource()) + { + Func enabled = (ctx, ct) => + { + cts.Cancel(); + return (bool)ctx["ShouldFail"]; + }; + + var policy = MonkeyPolicy.InjectFault(fault, injectionRate, enabled); + + policy.Invoking(x => x.Execute((ctx, ct) => { executed = true; }, context, cts.Token)) + .ShouldThrow(); + } + + executed.Should().BeFalse(); + } + + [Fact] + public void InjectFault_With_Context_Should_not_execute_user_delegate_if_user_cancelationtoken_cancelled_on_injectionrate_config_delegate() + { + string failureMessage = "Failure Message"; + Boolean executed = false; + Context context = new Context(); + context["ShouldFail"] = true; + context["Message"] = failureMessage; + context["InjectionRate"] = 0.6; + + Func fault = (ctx, cts) => + { + if (ctx["Message"] != null) + { + Exception ex = new InvalidOperationException(ctx["Message"].ToString()); + return ex; + } + + return new Exception(); + }; + + using (CancellationTokenSource cts = new CancellationTokenSource()) + { + Func enabled = (ctx, ct) => + { + return (bool)ctx["ShouldFail"]; + }; + + Func injectionRate = (ctx, ct) => + { + double rate = 0; + if (ctx["InjectionRate"] != null) + { + rate = (double)ctx["InjectionRate"]; + } + + cts.Cancel(); + return rate; + }; + + var policy = MonkeyPolicy.InjectFault(fault, injectionRate, enabled); + + policy.Invoking(x => x.Execute((ctx, ct) => { executed = true; }, context, cts.Token)) + .ShouldThrow(); + } + + executed.Should().BeFalse(); + } + + [Fact] + public void InjectFault_With_Context_Should_not_execute_user_delegate_if_user_cancelationtoken_cancelled_on_fault_config_delegate() + { + string failureMessage = "Failure Message"; + Boolean executed = false; + Context context = new Context(); + context["ShouldFail"] = true; + context["Message"] = failureMessage; + context["InjectionRate"] = 0.6; + + using (CancellationTokenSource cts = new CancellationTokenSource()) + { + Func enabled = (ctx, ct) => + { + return (bool)ctx["ShouldFail"]; + }; + + Func injectionRate = (ctx, ct) => + { + double rate = 0; + if (ctx["InjectionRate"] != null) + { + rate = (double)ctx["InjectionRate"]; + } + + return rate; + }; + + Func fault = (ctx, ct) => + { + cts.Cancel(); + if (ctx["Message"] != null) + { + Exception ex = new InvalidOperationException(ctx["Message"].ToString()); + return ex; + } + + return new Exception(); + }; + + var policy = MonkeyPolicy.InjectFault(fault, injectionRate, enabled); + + policy.Invoking(x => x.Execute((ctx, ct) => { executed = true; }, context, cts.Token)) + .ShouldThrow(); + } + + executed.Should().BeFalse(); + } + + #endregion } } \ No newline at end of file diff --git a/src/Polly.Contrib.Simmy.Specs/Fault/InjectFaultTResultAsyncSpecs.cs b/src/Polly.Contrib.Simmy.Specs/Fault/InjectFaultTResultAsyncSpecs.cs index f73d968..604aedc 100644 --- a/src/Polly.Contrib.Simmy.Specs/Fault/InjectFaultTResultAsyncSpecs.cs +++ b/src/Polly.Contrib.Simmy.Specs/Fault/InjectFaultTResultAsyncSpecs.cs @@ -68,7 +68,7 @@ public void InjectFault_With_Context_Should_not_execute_user_delegate_async() var policy = MonkeyPolicy.InjectFaultAsync( new Exception(), 0.6, - async (ctx) => + async (ctx, ct) => { return await Task.FromResult((bool)ctx["ShouldFail"]); }); @@ -91,7 +91,7 @@ public void InjectFault_With_Context_Should_execute_user_delegate_async() var policy = MonkeyPolicy.InjectFaultAsync( new Exception(), 0.4, - async (ctx) => + async (ctx, ct) => { return await Task.FromResult((bool)ctx["ShouldFail"]); }); @@ -116,7 +116,7 @@ public void InjectFault_With_Context_Should_execute_user_delegate_async_with_ena var policy = MonkeyPolicy.InjectFaultAsync( new Exception(), 0.4, - async (ctx) => + async (ctx, ct) => { return await Task.FromResult((bool)ctx["ShouldFail"]); }); @@ -142,8 +142,8 @@ public void InjectFault_With_Context_Should_not_execute_user_delegate_async_with }; Func> fault = (ctx, cts) => Task.FromResult(new Exception()); - Func> injectionRate = (ctx) => Task.FromResult(0.6); - Func> enabled = (ctx) => Task.FromResult(true); + Func> injectionRate = (ctx, ct) => Task.FromResult(0.6); + Func> enabled = (ctx, ct) => Task.FromResult(true); var policy = MonkeyPolicy.InjectFaultAsync(fault, injectionRate, enabled); policy.Awaiting(async x => await x.ExecuteAsync(actionAsync, context)) .ShouldThrowExactly(); @@ -176,7 +176,7 @@ public void InjectFault_With_Context_Should_not_execute_user_delegate_async_with return Task.FromResult(new Exception()); }; - Func> injectionRate = (ctx) => + Func> injectionRate = (ctx, ct) => { double rate = 0; if (ctx["InjectionRate"] != null) @@ -187,7 +187,7 @@ public void InjectFault_With_Context_Should_not_execute_user_delegate_async_with return Task.FromResult(rate); }; - Func> enabled = (ctx) => + Func> enabled = (ctx, ct) => { return Task.FromResult((bool)ctx["ShouldFail"]); }; @@ -197,6 +197,7 @@ public void InjectFault_With_Context_Should_not_execute_user_delegate_async_with .ShouldThrowExactly(); executed.Should().BeFalse(); } + #endregion #region TResult Based Monkey Policies @@ -249,7 +250,7 @@ public async Task InjectFault_With_Context_Enabled_Should_Return_Fault_async() }; ResultPrimitive fault = ResultPrimitive.Fault; - Func> enabled = (ctx) => + Func> enabled = (ctx, ct) => { return Task.FromResult((bool)ctx["ShouldFail"]); }; @@ -273,7 +274,7 @@ public async Task InjectFault_With_Context_Enabled_Should_Not_Return_Fault_async }; ResultPrimitive fault = ResultPrimitive.Fault; - Func> enabled = (ctx) => + Func> enabled = (ctx, ct) => { return Task.FromResult((bool)ctx["ShouldFail"]); }; @@ -302,7 +303,7 @@ public async Task InjectFault_With_Context_InjectionRate_Should_Return_Fault_asy return Task.FromResult(ResultPrimitive.Fault); }; - Func> injectionRate = (ctx) => + Func> injectionRate = (ctx, ct) => { double rate = 0; if (ctx["InjectionRate"] != null) @@ -313,7 +314,7 @@ public async Task InjectFault_With_Context_InjectionRate_Should_Return_Fault_asy return Task.FromResult(rate); }; - Func> enabled = (ctx) => + Func> enabled = (ctx, ct) => { return Task.FromResult(true); }; @@ -342,7 +343,7 @@ public async Task InjectFault_With_Context_InjectionRate_Should_Not_Return_Fault return Task.FromResult(ResultPrimitive.Fault); }; - Func> injectionRate = (ctx) => + Func> injectionRate = (ctx, ct) => { double rate = 0; if (ctx["InjectionRate"] != null) @@ -353,7 +354,7 @@ public async Task InjectFault_With_Context_InjectionRate_Should_Not_Return_Fault return Task.FromResult(rate); }; - Func> enabled = (ctx) => + Func> enabled = (ctx, ct) => { return Task.FromResult(true); }; @@ -364,5 +365,225 @@ public async Task InjectFault_With_Context_InjectionRate_Should_Not_Return_Fault executed.Should().BeTrue(); } #endregion + + #region Cancellable scenarios + + [Fact] + public void InjectFault_With_Context_Should_not_execute_user_delegate_if_user_cancelationtoken_cancelled_before_to_start_execution() + { + string failureMessage = "Failure Message"; + Boolean executed = false; + Context context = new Context(); + context["ShouldFail"] = true; + context["Message"] = failureMessage; + context["InjectionRate"] = 0.6; + Func> actionAsync = (ctx, ct) => + { + executed = true; + return Task.FromResult(ResultPrimitive.Good); + }; + + Func> fault = (ctx, cts) => + { + if (ctx["Message"] != null) + { + Exception ex = new InvalidOperationException(ctx["Message"].ToString()); + return Task.FromResult(ex); + } + + return Task.FromResult(new Exception()); + }; + + Func> injectionRate = (ctx, ct) => + { + double rate = 0; + if (ctx["InjectionRate"] != null) + { + rate = (double)ctx["InjectionRate"]; + } + + return Task.FromResult(rate); + }; + + Func> enabled = (ctx, ct) => + { + return Task.FromResult((bool)ctx["ShouldFail"]); + }; + + var policy = MonkeyPolicy.InjectFaultAsync(fault, injectionRate, enabled); + using (CancellationTokenSource cts = new CancellationTokenSource()) + { + cts.Cancel(); + + policy.Awaiting(async x => await x.ExecuteAsync(actionAsync, context, cts.Token)) + .ShouldThrow(); + } + + executed.Should().BeFalse(); + } + + [Fact] + public void InjectFault_With_Context_Should_not_execute_user_delegate_if_user_cancelationtoken_cancelled_on_enabled_config_delegate() + { + string failureMessage = "Failure Message"; + Boolean executed = false; + Context context = new Context(); + context["ShouldFail"] = true; + context["Message"] = failureMessage; + context["InjectionRate"] = 0.6; + Func> actionAsync = (ctx, ct) => + { + executed = true; + return Task.FromResult(ResultPrimitive.Good); + }; + + Func> fault = (ctx, cts) => + { + if (ctx["Message"] != null) + { + Exception ex = new InvalidOperationException(ctx["Message"].ToString()); + return Task.FromResult(ex); + } + + return Task.FromResult(new Exception()); + }; + + Func> injectionRate = (ctx, ct) => + { + double rate = 0; + if (ctx["InjectionRate"] != null) + { + rate = (double)ctx["InjectionRate"]; + } + + return Task.FromResult(rate); + }; + + using (CancellationTokenSource cts = new CancellationTokenSource()) + { + Func> enabled = (ctx, ct) => + { + cts.Cancel(); + return Task.FromResult((bool)ctx["ShouldFail"]); + }; + + var policy = MonkeyPolicy.InjectFaultAsync(fault, injectionRate, enabled); + + policy.Awaiting(async x => await x.ExecuteAsync(actionAsync, context, cts.Token)) + .ShouldThrow(); + } + + executed.Should().BeFalse(); + } + + [Fact] + public void InjectFault_With_Context_Should_not_execute_user_delegate_if_user_cancelationtoken_cancelled_on_injectionrate_config_delegate() + { + string failureMessage = "Failure Message"; + Boolean executed = false; + Context context = new Context(); + context["ShouldFail"] = true; + context["Message"] = failureMessage; + context["InjectionRate"] = 0.6; + Func> actionAsync = (ctx, ct) => + { + executed = true; + return Task.FromResult(ResultPrimitive.Good); + }; + + Func> fault = (ctx, cts) => + { + if (ctx["Message"] != null) + { + Exception ex = new InvalidOperationException(ctx["Message"].ToString()); + return Task.FromResult(ex); + } + + return Task.FromResult(new Exception()); + }; + + using (CancellationTokenSource cts = new CancellationTokenSource()) + { + Func> enabled = (ctx, ct) => + { + return Task.FromResult((bool)ctx["ShouldFail"]); + }; + + Func> injectionRate = (ctx, ct) => + { + double rate = 0; + if (ctx["InjectionRate"] != null) + { + rate = (double)ctx["InjectionRate"]; + } + + cts.Cancel(); + return Task.FromResult(rate); + }; + + var policy = MonkeyPolicy.InjectFaultAsync(fault, injectionRate, enabled); + + policy.Awaiting(async x => await x.ExecuteAsync(actionAsync, context, cts.Token)) + .ShouldThrow(); + } + + executed.Should().BeFalse(); + } + + [Fact] + public void InjectFault_With_Context_Should_not_execute_user_delegate_if_user_cancelationtoken_cancelled_on_fault_config_delegate() + { + string failureMessage = "Failure Message"; + Boolean executed = false; + Context context = new Context(); + context["ShouldFail"] = true; + context["Message"] = failureMessage; + context["InjectionRate"] = 0.6; + Func> actionAsync = (ctx, ct) => + { + executed = true; + return Task.FromResult(ResultPrimitive.Good); + }; + + using (CancellationTokenSource cts = new CancellationTokenSource()) + { + Func> enabled = (ctx, ct) => + { + return Task.FromResult((bool)ctx["ShouldFail"]); + }; + + Func> injectionRate = (ctx, ct) => + { + double rate = 0; + if (ctx["InjectionRate"] != null) + { + rate = (double)ctx["InjectionRate"]; + } + + return Task.FromResult(rate); + }; + + Func> fault = (ctx, ct) => + { + cts.Cancel(); + if (ctx["Message"] != null) + { + Exception ex = new InvalidOperationException(ctx["Message"].ToString()); + return Task.FromResult(ex); + } + + return Task.FromResult(new Exception()); + }; + + var policy = MonkeyPolicy.InjectFaultAsync(fault, injectionRate, enabled); + + policy.Awaiting(async x => await x.ExecuteAsync(actionAsync, context, cts.Token)) + .ShouldThrow(); + } + + executed.Should().BeFalse(); + } + + #endregion } } \ No newline at end of file diff --git a/src/Polly.Contrib.Simmy.Specs/Fault/InjectFaultTResultSpecs.cs b/src/Polly.Contrib.Simmy.Specs/Fault/InjectFaultTResultSpecs.cs index e85d8dd..38c3f68 100644 --- a/src/Polly.Contrib.Simmy.Specs/Fault/InjectFaultTResultSpecs.cs +++ b/src/Polly.Contrib.Simmy.Specs/Fault/InjectFaultTResultSpecs.cs @@ -1,6 +1,6 @@ using System; +using System.Threading; using FluentAssertions; -using Polly; using Polly.Contrib.Simmy.Specs.Helpers; using Polly.Contrib.Simmy.Utilities; using Xunit; @@ -64,7 +64,7 @@ public void InjectFaultWith_Context_Should_not_execute_user_delegate() var policy = MonkeyPolicy.InjectFault( new Exception(), 0.6, - (ctx) => + (ctx, ct) => { return ((bool)ctx["ShouldFail"]); }); @@ -86,7 +86,7 @@ public void InjectFaultWith_Context_Should_execute_user_delegate() var policy = MonkeyPolicy.InjectFault( new Exception(), 0.4, - (ctx) => + (ctx, ct) => { return ((bool)ctx["ShouldFail"]); }); @@ -108,7 +108,7 @@ public void InjectFaultWith_Context_Should_execute_user_delegate_with_enabled_la var policy = MonkeyPolicy.InjectFault( new Exception(), 0.6, - (ctx) => + (ctx, ct) => { return ((bool)ctx["ShouldFail"]); }); @@ -128,9 +128,9 @@ public void InjectFaultWith_Context_Should_not_execute_user_delegate_default_con Context context = new Context(); Func action = (ctx) => { executed = true; return ResultPrimitive.Good; }; - Func fault = (ctx) => new Exception(); - Func injectionRate = (ctx) => 0.6; - Func enabled = (ctx) => true; + Func fault = (ctx, ct) => new Exception(); + Func injectionRate = (ctx, ct) => 0.6; + Func enabled = (ctx, ct) => true; var policy = MonkeyPolicy.InjectFault(fault, injectionRate, enabled); policy.Invoking(x => x.Execute(action, context)) .ShouldThrow(); @@ -149,7 +149,7 @@ public void InjectFaultWith_Context_Should_not_execute_user_delegate_full_contex context["InjectionRate"] = 0.6; Func action = (ctx) => { executed = true; return ResultPrimitive.Good; }; - Func fault = (ctx) => + Func fault = (ctx, ct) => { if (ctx["Message"] != null) { @@ -159,7 +159,7 @@ public void InjectFaultWith_Context_Should_not_execute_user_delegate_full_contex return new Exception(); }; - Func injectionRate = (ctx) => + Func injectionRate = (ctx, ct) => { if (ctx["InjectionRate"] != null) { @@ -169,7 +169,7 @@ public void InjectFaultWith_Context_Should_not_execute_user_delegate_full_contex return 0; }; - Func enabled = (ctx) => + Func enabled = (ctx, ct) => { return ((bool)ctx["ShouldFail"]); }; @@ -217,7 +217,7 @@ public void InjectFaultWith_Context_Enabled_Should_Return_Fault() context["ShouldFail"] = true; Func action = (ctx) => { executed = true; return ResultPrimitive.Good; }; ResultPrimitive fault = ResultPrimitive.Fault; - Func enabled = (ctx) => + Func enabled = (ctx, ct) => { return ((bool)ctx["ShouldFail"]); }; @@ -236,7 +236,7 @@ public void InjectFaultWith_Context_Enabled_Should_Not_Return_Fault() context["ShouldFail"] = false; Func action = (ctx) => { executed = true; return ResultPrimitive.Good; }; ResultPrimitive fault = ResultPrimitive.Fault; - Func enabled = (ctx) => + Func enabled = (ctx, ct) => { return ((bool)ctx["ShouldFail"]); }; @@ -255,12 +255,12 @@ public void InjectFaultWith_Context_InjectionRate_Should_Return_Fault() context["InjectionRate"] = 0.6; Func action = (ctx) => { executed = true; return ResultPrimitive.Good; }; - Func fault = (ctx) => + Func fault = (ctx, ct) => { return ResultPrimitive.Fault; }; - Func injectionRate = (ctx) => + Func injectionRate = (ctx, ct) => { if (ctx["InjectionRate"] != null) { @@ -270,7 +270,7 @@ public void InjectFaultWith_Context_InjectionRate_Should_Return_Fault() return 0; }; - Func enabled = (ctx) => + Func enabled = (ctx, ct) => { return true; }; @@ -289,12 +289,12 @@ public void InjectFaultWith_Context_InjectionRate_Should_Not_Return_Fault() context["InjectionRate"] = 0.4; Func action = (ctx) => { executed = true; return ResultPrimitive.Good; }; - Func fault = (ctx) => + Func fault = (ctx, ct) => { return ResultPrimitive.Fault; }; - Func injectionRate = (ctx) => + Func injectionRate = (ctx, ct) => { if (ctx["InjectionRate"] != null) { @@ -304,7 +304,7 @@ public void InjectFaultWith_Context_InjectionRate_Should_Not_Return_Fault() return 0; }; - Func enabled = (ctx) => + Func enabled = (ctx, ct) => { return true; }; @@ -315,5 +315,213 @@ public void InjectFaultWith_Context_InjectionRate_Should_Not_Return_Fault() executed.Should().BeTrue(); } #endregion + + #region Cancellable scenarios + + [Fact] + public void InjectFault_With_Context_Should_not_execute_user_delegate_if_user_cancelationtoken_cancelled_before_to_start_execution() + { + string failureMessage = "Failure Message"; + Boolean executed = false; + Context context = new Context(); + context["ShouldFail"] = true; + context["Message"] = failureMessage; + context["InjectionRate"] = 0.6; + + Func fault = (ctx, cts) => + { + if (ctx["Message"] != null) + { + Exception ex = new InvalidOperationException(ctx["Message"].ToString()); + return ex; + } + + return new Exception(); + }; + + Func injectionRate = (ctx, ct) => + { + double rate = 0; + if (ctx["InjectionRate"] != null) + { + rate = (double)ctx["InjectionRate"]; + } + + return rate; + }; + + Func enabled = (ctx, ct) => + { + return (bool)ctx["ShouldFail"]; + }; + + Func action = (ctx, ct) => { executed = true; return ResultPrimitive.Good; }; + + var policy = MonkeyPolicy.InjectFault(fault, injectionRate, enabled); + using (CancellationTokenSource cts = new CancellationTokenSource()) + { + cts.Cancel(); + + policy.Invoking(x => x.Execute((ctx, ct) => action, context, cts.Token)) + .ShouldThrow(); + } + + executed.Should().BeFalse(); + } + + [Fact] + public void InjectFault_With_Context_Should_not_execute_user_delegate_if_user_cancelationtoken_cancelled_on_enabled_config_delegate() + { + string failureMessage = "Failure Message"; + Boolean executed = false; + Context context = new Context(); + context["ShouldFail"] = true; + context["Message"] = failureMessage; + context["InjectionRate"] = 0.6; + + Func fault = (ctx, cts) => + { + if (ctx["Message"] != null) + { + Exception ex = new InvalidOperationException(ctx["Message"].ToString()); + return ex; + } + + return new Exception(); + }; + + Func injectionRate = (ctx, ct) => + { + double rate = 0; + if (ctx["InjectionRate"] != null) + { + rate = (double)ctx["InjectionRate"]; + } + + return rate; + }; + + Func action = (ctx, ct) => { executed = true; return ResultPrimitive.Good; }; + + using (CancellationTokenSource cts = new CancellationTokenSource()) + { + Func enabled = (ctx, ct) => + { + cts.Cancel(); + return (bool)ctx["ShouldFail"]; + }; + + var policy = MonkeyPolicy.InjectFault(fault, injectionRate, enabled); + + policy.Invoking(x => x.Execute(action, context, cts.Token)) + .ShouldThrow(); + } + + executed.Should().BeFalse(); + } + + [Fact] + public void InjectFault_With_Context_Should_not_execute_user_delegate_if_user_cancelationtoken_cancelled_on_injectionrate_config_delegate() + { + string failureMessage = "Failure Message"; + Boolean executed = false; + Context context = new Context(); + context["ShouldFail"] = true; + context["Message"] = failureMessage; + context["InjectionRate"] = 0.6; + + Func fault = (ctx, cts) => + { + if (ctx["Message"] != null) + { + Exception ex = new InvalidOperationException(ctx["Message"].ToString()); + return ex; + } + + return new Exception(); + }; + + Func action = (ctx, ct) => { executed = true; return ResultPrimitive.Good; }; + + using (CancellationTokenSource cts = new CancellationTokenSource()) + { + Func enabled = (ctx, ct) => + { + return (bool)ctx["ShouldFail"]; + }; + + Func injectionRate = (ctx, ct) => + { + double rate = 0; + if (ctx["InjectionRate"] != null) + { + rate = (double)ctx["InjectionRate"]; + } + + cts.Cancel(); + return rate; + }; + + var policy = MonkeyPolicy.InjectFault(fault, injectionRate, enabled); + + policy.Invoking(x => x.Execute(action, context, cts.Token)) + .ShouldThrow(); + } + + executed.Should().BeFalse(); + } + + [Fact] + public void InjectFault_With_Context_Should_not_execute_user_delegate_if_user_cancelationtoken_cancelled_on_fault_config_delegate() + { + string failureMessage = "Failure Message"; + Boolean executed = false; + Context context = new Context(); + context["ShouldFail"] = true; + context["Message"] = failureMessage; + context["InjectionRate"] = 0.6; + + using (CancellationTokenSource cts = new CancellationTokenSource()) + { + Func enabled = (ctx, ct) => + { + return (bool)ctx["ShouldFail"]; + }; + + Func injectionRate = (ctx, ct) => + { + double rate = 0; + if (ctx["InjectionRate"] != null) + { + rate = (double)ctx["InjectionRate"]; + } + + return rate; + }; + + Func fault = (ctx, ct) => + { + cts.Cancel(); + if (ctx["Message"] != null) + { + Exception ex = new InvalidOperationException(ctx["Message"].ToString()); + return ex; + } + + return new Exception(); + }; + + Func action = (ctx, ct) => { executed = true; return ResultPrimitive.Good; }; + + var policy = MonkeyPolicy.InjectFault(fault, injectionRate, enabled); + + policy.Invoking(x => x.Execute(action, context, cts.Token)) + .ShouldThrow(); + } + + executed.Should().BeFalse(); + } + + #endregion } } diff --git a/src/Polly.Contrib.Simmy.Specs/Latency/InjectLatencyAsyncSpecs.cs b/src/Polly.Contrib.Simmy.Specs/Latency/InjectLatencyAsyncSpecs.cs index bdae871..6c6a6a5 100644 --- a/src/Polly.Contrib.Simmy.Specs/Latency/InjectLatencyAsyncSpecs.cs +++ b/src/Polly.Contrib.Simmy.Specs/Latency/InjectLatencyAsyncSpecs.cs @@ -1,8 +1,10 @@ using System; +using System.Diagnostics; using System.Threading; using System.Threading.Tasks; using FluentAssertions; using Polly.Contrib.Simmy.Utilities; +using Polly.Timeout; using Polly.Utilities; using Xunit; @@ -75,7 +77,7 @@ public async Task InjectLatency_Context_Free_Should_Not_Introduce_Delay_If_Injec var delay = TimeSpan.FromMilliseconds(500); var policy = MonkeyPolicy.InjectLatencyAsync(delay, 0.3, () => true); var executed = false; - + Func actionAsync = () => { executed = true; return TaskHelper.EmptyTask; }; await policy.ExecuteAsync(actionAsync); @@ -94,7 +96,7 @@ public async Task InjectLatency_With_Context_With_Enabled_Lambda_Should_Introduc var context = new Context(); context["Enabled"] = true; - Func> enabled = async (ctx) => + Func> enabled = async (ctx, ct) => { return await Task.FromResult((bool)ctx["Enabled"]); }; @@ -116,7 +118,7 @@ public async Task InjectLatency_With_Context_With_Enabled_Lambda_Should_Not_Intr var context = new Context(); context["Enabled"] = false; - Func> enabled = async (ctx) => + Func> enabled = async (ctx, ct) => { return await Task.FromResult((bool)ctx["Enabled"]); }; @@ -138,7 +140,7 @@ public async Task InjectLatency_With_Context_With_Enabled_Lambda_Should_Not_Intr var context = new Context(); context["Enabled"] = true; - Func> enabled = async (ctx) => + Func> enabled = async (ctx, ct) => { return await Task.FromResult((bool)ctx["Enabled"]); }; @@ -161,16 +163,16 @@ public async Task InjectLatency_With_Context_With_InjectionRate_Lambda_Should_In context["Enabled"] = true; context["InjectionRate"] = 0.6; - Func> enabled = async (ctx) => + Func> enabled = async (ctx, ct) => { return await Task.FromResult((bool)ctx["Enabled"]); }; - Func> injectionRate = async (ctx) => + Func> injectionRate = async (ctx, ct) => { if (ctx["InjectionRate"] != null) { - return await Task.FromResult((double) ctx["InjectionRate"]); + return await Task.FromResult((double)ctx["InjectionRate"]); } return await Task.FromResult(0); @@ -194,16 +196,16 @@ public async Task InjectLatency_With_Context_With_InjectionRate_Lambda_Should_No context["Enabled"] = true; context["InjectionRate"] = 0.3; - Func> enabled = async (ctx) => + Func> enabled = async (ctx, ct) => { return await Task.FromResult((bool)ctx["Enabled"]); }; - Func> injectionRate = async (ctx) => + Func> injectionRate = async (ctx, ct) => { if (ctx["InjectionRate"] != null) { - return await Task.FromResult((double) ctx["InjectionRate"]); + return await Task.FromResult((double)ctx["InjectionRate"]); } return await Task.FromResult(0); @@ -238,16 +240,16 @@ public async Task InjectLatency_With_Context_With_Latency_Lambda_Should_Introduc return await Task.FromResult(TimeSpan.FromMilliseconds(0)); }; - Func> enabled = async (ctx) => + Func> enabled = async (ctx, ct) => { return await Task.FromResult((bool)ctx["Enabled"]); }; - Func> injectionRate = async (ctx) => + Func> injectionRate = async (ctx, ct) => { if (ctx["InjectionRate"] != null) { - return await Task.FromResult((double) ctx["InjectionRate"]); + return await Task.FromResult((double)ctx["InjectionRate"]); } return await Task.FromResult(0); @@ -282,16 +284,16 @@ public async Task InjectLatency_With_Context_With_Latency_Lambda_Should_Not_Intr return await Task.FromResult(TimeSpan.FromMilliseconds(0)); }; - Func> enabled = async (ctx) => + Func> enabled = async (ctx, ct) => { return await Task.FromResult((bool)ctx["Enabled"]); }; - Func> injectionRate = async (ctx) => + Func> injectionRate = async (ctx, ct) => { if (ctx["InjectionRate"] != null) { - return await Task.FromResult((double) ctx["InjectionRate"]); + return await Task.FromResult((double)ctx["InjectionRate"]); } return await Task.FromResult(0); @@ -326,16 +328,16 @@ public async Task InjectLatency_With_Context_With_Latency_Lambda_Should_Not_Intr return await Task.FromResult(TimeSpan.FromMilliseconds(0)); }; - Func> enabled = async (ctx) => + Func> enabled = async (ctx, ct) => { return await Task.FromResult((bool)ctx["Enabled"]); }; - Func> injectionRate = async (ctx) => + Func> injectionRate = async (ctx, ct) => { if (ctx["InjectionRate"] != null) { - return await Task.FromResult((double) ctx["InjectionRate"]); + return await Task.FromResult((double)ctx["InjectionRate"]); } return await Task.FromResult(0); @@ -352,5 +354,273 @@ public async Task InjectLatency_With_Context_With_Latency_Lambda_Should_Not_Intr } #endregion + + #region Cancellable scenarios + + [Fact] + public void InjectLatency_With_Context_Should_not_execute_user_delegate_if_user_cancelationtoken_cancelled_before_to_start_execution() + { + var delay = TimeSpan.FromMilliseconds(500); + var context = new Context(); + context["ShouldInjectLatency"] = true; + context["Enabled"] = true; + context["InjectionRate"] = 0.6; + + Func> latencyProvider = async (ctx, ct) => + { + if ((bool)ctx["ShouldInjectLatency"]) + { + return await Task.FromResult(delay); + } + + return await Task.FromResult(TimeSpan.FromMilliseconds(0)); + }; + + Func> enabled = async (ctx, ct) => + { + return await Task.FromResult((bool)ctx["Enabled"]); + }; + + Func> injectionRate = async (ctx, ct) => + { + if (ctx["InjectionRate"] != null) + { + return await Task.FromResult((double)ctx["InjectionRate"]); + } + + return await Task.FromResult(0); + }; + + Boolean executed = false; + var policy = MonkeyPolicy.InjectLatencyAsync(latencyProvider, injectionRate, enabled); + Func actionAsync = (_, ct) => { executed = true; return TaskHelper.EmptyTask; }; + + using (CancellationTokenSource cts = new CancellationTokenSource()) + { + cts.Cancel(); + + policy.Awaiting(async x => await x.ExecuteAsync(actionAsync, context, cts.Token)) + .ShouldThrow(); + } + + executed.Should().BeFalse(); + _totalTimeSlept.Should().Be(0); + } + + [Fact] + public void InjectLatency_With_Context_Should_not_execute_user_delegate_if_user_cancelationtoken_cancelled_on_enabled_config_delegate() + { + var delay = TimeSpan.FromMilliseconds(500); + var context = new Context(); + context["ShouldInjectLatency"] = true; + context["Enabled"] = true; + context["InjectionRate"] = 0.6; + + Func> latencyProvider = async (ctx, ct) => + { + if ((bool)ctx["ShouldInjectLatency"]) + { + return await Task.FromResult(delay); + } + + return await Task.FromResult(TimeSpan.FromMilliseconds(0)); + }; + + Func> injectionRate = async (ctx, ct) => + { + if (ctx["InjectionRate"] != null) + { + return await Task.FromResult((double)ctx["InjectionRate"]); + } + + return await Task.FromResult(0); + }; + + Boolean executed = false; + Func actionAsync = (_, ct) => { executed = true; return TaskHelper.EmptyTask; }; + + using (CancellationTokenSource cts = new CancellationTokenSource()) + { + Func> enabled = async (ctx, ct) => + { + cts.Cancel(); + return await Task.FromResult((bool)ctx["Enabled"]); + }; + + var policy = MonkeyPolicy.InjectLatencyAsync(latencyProvider, injectionRate, enabled); + + policy.Awaiting(async x => await x.ExecuteAsync(actionAsync, context, cts.Token)) + .ShouldThrow(); + } + + executed.Should().BeFalse(); + _totalTimeSlept.Should().Be(0); + } + + [Fact] + public void InjectLatency_With_Context_Should_not_execute_user_delegate_if_user_cancelationtoken_cancelled_on_injectionrate_config_delegate() + { + var delay = TimeSpan.FromMilliseconds(500); + var context = new Context(); + context["ShouldInjectLatency"] = true; + context["Enabled"] = true; + context["InjectionRate"] = 0.6; + + Func> latencyProvider = async (ctx, ct) => + { + if ((bool)ctx["ShouldInjectLatency"]) + { + return await Task.FromResult(delay); + } + + return await Task.FromResult(TimeSpan.FromMilliseconds(0)); + }; + + Boolean executed = false; + Func actionAsync = (_, ct) => { executed = true; return TaskHelper.EmptyTask; }; + + using (CancellationTokenSource cts = new CancellationTokenSource()) + { + Func> enabled = async (ctx, ct) => + { + return await Task.FromResult((bool)ctx["Enabled"]); + }; + + Func> injectionRate = async (ctx, ct) => + { + cts.Cancel(); + + if (ctx["InjectionRate"] != null) + { + return await Task.FromResult((double)ctx["InjectionRate"]); + } + + return await Task.FromResult(0); + }; + + var policy = MonkeyPolicy.InjectLatencyAsync(latencyProvider, injectionRate, enabled); + + policy.Awaiting(async x => await x.ExecuteAsync(actionAsync, context, cts.Token)) + .ShouldThrow(); + } + + executed.Should().BeFalse(); + _totalTimeSlept.Should().Be(0); + } + + [Fact] + public void InjectLatency_With_Context_Should_not_execute_user_delegate_if_user_cancelationtoken_cancelled_on_latency_config_delegate() + { + var delay = TimeSpan.FromMilliseconds(500); + var context = new Context(); + context["ShouldInjectLatency"] = true; + context["Enabled"] = true; + context["InjectionRate"] = 0.6; + + Boolean executed = false; + Func actionAsync = (_, ct) => { executed = true; return TaskHelper.EmptyTask; }; + + using (CancellationTokenSource cts = new CancellationTokenSource()) + { + Func> enabled = async (ctx, ct) => + { + return await Task.FromResult((bool)ctx["Enabled"]); + }; + + Func> injectionRate = async (ctx, ct) => + { + if (ctx["InjectionRate"] != null) + { + return await Task.FromResult((double)ctx["InjectionRate"]); + } + + return await Task.FromResult(0); + }; + + Func> latencyProvider = async (ctx, ct) => + { + cts.Cancel(); + + if ((bool)ctx["ShouldInjectLatency"]) + { + return await Task.FromResult(delay); + } + + return await Task.FromResult(TimeSpan.FromMilliseconds(0)); + }; + + var policy = MonkeyPolicy.InjectLatencyAsync(latencyProvider, injectionRate, enabled); + + policy.Awaiting(async x => await x.ExecuteAsync(actionAsync, context, cts.Token)) + .ShouldThrow(); + } + + executed.Should().BeFalse(); + _totalTimeSlept.Should().Be(0); + } + + [Theory] + [InlineData(TimeoutStrategy.Optimistic)] + [InlineData(TimeoutStrategy.Pessimistic)] + public void InjectLatency_With_Context_Should_not_inject_the_whole_latency_if_user_cancelationtoken_is_signaled_from_timeout(TimeoutStrategy timeoutStrategy) + { + SystemClock.Reset(); + var timeout = TimeSpan.FromSeconds(5); + var delay = TimeSpan.FromSeconds(10); + var context = new Context(); + context["ShouldInjectLatency"] = true; + context["Enabled"] = true; + context["InjectionRate"] = 0.6; + + Boolean executed = false; + Stopwatch watch = new Stopwatch(); + + using (CancellationTokenSource cts = new CancellationTokenSource()) + { + Func> enabled = async (ctx, ct) => + { + return await Task.FromResult((bool)ctx["Enabled"]); + }; + + Func> injectionRate = async (ctx, ct) => + { + if (ctx["InjectionRate"] != null) + { + return await Task.FromResult((double)ctx["InjectionRate"]); + } + + return await Task.FromResult(0); + }; + + Func> latencyProvider = async (ctx, ct) => + { + if ((bool)ctx["ShouldInjectLatency"]) + { + return await Task.FromResult(delay); + } + + return await Task.FromResult(TimeSpan.FromMilliseconds(0)); + }; + + Func actionAsync = (_, ct) => + { + executed = true; + return TaskHelper.EmptyTask; + }; + + var policy = Policy.TimeoutAsync(timeout, timeoutStrategy) + .WrapAsync(MonkeyPolicy.InjectLatencyAsync(latencyProvider, injectionRate, enabled)); + + watch.Start(); + policy.Awaiting(async x => { await x.ExecuteAsync(actionAsync, context, cts.Token); }) + .ShouldThrow(); + watch.Stop(); + } + + executed.Should().BeFalse(); + watch.Elapsed.Should().BeCloseTo(timeout, ((int)TimeSpan.FromSeconds(3).TotalMilliseconds)); + } + + #endregion } } diff --git a/src/Polly.Contrib.Simmy.Specs/Latency/InjectLatencySpecs.cs b/src/Polly.Contrib.Simmy.Specs/Latency/InjectLatencySpecs.cs index 56a9cb6..f981ef1 100644 --- a/src/Polly.Contrib.Simmy.Specs/Latency/InjectLatencySpecs.cs +++ b/src/Polly.Contrib.Simmy.Specs/Latency/InjectLatencySpecs.cs @@ -1,6 +1,9 @@ using System; +using System.Diagnostics; +using System.Threading; using FluentAssertions; using Polly.Contrib.Simmy.Utilities; +using Polly.Timeout; using Polly.Utilities; using Xunit; @@ -88,7 +91,7 @@ public void InjectLatency_With_Context_With_Enabled_Lambda_Should_Introduce_Dela var context = new Context(); context["Enabled"] = true; - Func enabled = (ctx) => + Func enabled = (ctx, ct) => { return ((bool)ctx["Enabled"]); }; @@ -109,7 +112,7 @@ public void InjectLatency_With_Context_With_Enabled_Lambda_Should_Not_Introduce_ var context = new Context(); context["Enabled"] = false; - Func enabled = (ctx) => + Func enabled = (ctx, ct) => { return ((bool)ctx["Enabled"]); }; @@ -130,7 +133,7 @@ public void InjectLatency_With_Context_With_Enabled_Lambda_Should_Not_Introduce_ var context = new Context(); context["Enabled"] = true; - Func enabled = (ctx) => + Func enabled = (ctx, ct) => { return ((bool)ctx["Enabled"]); }; @@ -152,12 +155,12 @@ public void InjectLatency_With_Context_With_InjectionRate_Lambda_Should_Introduc context["Enabled"] = true; context["InjectionRate"] = 0.6; - Func enabled = (ctx) => + Func enabled = (ctx, ct) => { return ((bool)ctx["Enabled"]); }; - Func injectionRate = (ctx) => + Func injectionRate = (ctx, ct) => { if (ctx["InjectionRate"] != null) { @@ -184,12 +187,12 @@ public void InjectLatency_With_Context_With_InjectionRate_Lambda_Should_Not_Intr context["Enabled"] = true; context["InjectionRate"] = 0.3; - Func enabled = (ctx) => + Func enabled = (ctx, ct) => { return ((bool)ctx["Enabled"]); }; - Func injectionRate = (ctx) => + Func injectionRate = (ctx, ct) => { if (ctx["InjectionRate"] != null) { @@ -217,7 +220,7 @@ public void InjectLatency_With_Context_With_Latency_Lambda_Should_Introduce_Dela context["Enabled"] = true; context["InjectionRate"] = 0.6; - Func latencyProvider = (ctx) => + Func latencyProvider = (ctx, ct) => { if ((bool)ctx["ShouldInjectLatency"]) { @@ -227,12 +230,12 @@ public void InjectLatency_With_Context_With_Latency_Lambda_Should_Introduce_Dela return TimeSpan.FromMilliseconds(0); }; - Func enabled = (ctx) => + Func enabled = (ctx, ct) => { return ((bool)ctx["Enabled"]); }; - Func injectionRate = (ctx) => + Func injectionRate = (ctx, ct) => { if (ctx["InjectionRate"] != null) { @@ -260,7 +263,7 @@ public void InjectLatency_With_Context_With_Latency_Lambda_Should_Not_Introduce_ context["Enabled"] = true; context["InjectionRate"] = 0.6; - Func latencyProvider = (ctx) => + Func latencyProvider = (ctx, ct) => { if ((bool)ctx["ShouldInjectLatency"]) { @@ -270,12 +273,12 @@ public void InjectLatency_With_Context_With_Latency_Lambda_Should_Not_Introduce_ return TimeSpan.FromMilliseconds(0); }; - Func enabled = (ctx) => + Func enabled = (ctx, ct) => { return ((bool)ctx["Enabled"]); }; - Func injectionRate = (ctx) => + Func injectionRate = (ctx, ct) => { if (ctx["InjectionRate"] != null) { @@ -303,7 +306,7 @@ public void InjectLatency_With_Context_With_Latency_Lambda_Should_Not_Introduce_ context["Enabled"] = true; context["InjectionRate"] = 0.3; - Func latencyProvider = (ctx) => + Func latencyProvider = (ctx, ct) => { if ((bool)ctx["ShouldInjectLatency"]) { @@ -313,12 +316,12 @@ public void InjectLatency_With_Context_With_Latency_Lambda_Should_Not_Introduce_ return TimeSpan.FromMilliseconds(0); }; - Func enabled = (ctx) => + Func enabled = (ctx, ct) => { return ((bool)ctx["Enabled"]); }; - Func injectionRate = (ctx) => + Func injectionRate = (ctx, ct) => { if (ctx["InjectionRate"] != null) { @@ -338,5 +341,259 @@ public void InjectLatency_With_Context_With_Latency_Lambda_Should_Not_Introduce_ } #endregion + + #region Cancellable scenarios + + [Fact] + public void InjectLatency_With_Context_Should_not_execute_user_delegate_if_user_cancelationtoken_cancelled_before_to_start_execution() + { + var delay = TimeSpan.FromMilliseconds(500); + var context = new Context(); + context["ShouldInjectLatency"] = true; + context["Enabled"] = true; + context["InjectionRate"] = 0.6; + + Func latencyProvider = (ctx, ct) => + { + if ((bool)ctx["ShouldInjectLatency"]) + { + return delay; + } + + return TimeSpan.FromMilliseconds(0); + }; + + Func enabled = (ctx, ct) => + { + return (bool)ctx["Enabled"]; + }; + + Func injectionRate = (ctx, ct) => + { + if (ctx["InjectionRate"] != null) + { + return (double)ctx["InjectionRate"]; + } + + return 0; + }; + + Boolean executed = false; + var policy = MonkeyPolicy.InjectLatency(latencyProvider, injectionRate, enabled); + + using (CancellationTokenSource cts = new CancellationTokenSource()) + { + cts.Cancel(); + + policy.Invoking(x => x.Execute((ctx, ct) => { executed = true; }, context, cts.Token)) + .ShouldThrow(); + } + + executed.Should().BeFalse(); + _totalTimeSlept.Should().Be(0); + } + + [Fact] + public void InjectLatency_With_Context_Should_not_execute_user_delegate_if_user_cancelationtoken_cancelled_on_enabled_config_delegate() + { + var delay = TimeSpan.FromMilliseconds(500); + var context = new Context(); + context["ShouldInjectLatency"] = true; + context["Enabled"] = true; + context["InjectionRate"] = 0.6; + + Func latencyProvider = (ctx, ct) => + { + if ((bool)ctx["ShouldInjectLatency"]) + { + return delay; + } + + return TimeSpan.FromMilliseconds(0); + }; + + Func injectionRate = (ctx, ct) => + { + if (ctx["InjectionRate"] != null) + { + return (double)ctx["InjectionRate"]; + } + + return 0; + }; + + Boolean executed = false; + using (CancellationTokenSource cts = new CancellationTokenSource()) + { + Func enabled = (ctx, ct) => + { + cts.Cancel(); + return (bool)ctx["Enabled"]; + }; + + var policy = MonkeyPolicy.InjectLatency(latencyProvider, injectionRate, enabled); + policy.Invoking(x => x.Execute((ctx, ct) => { executed = true; }, context, cts.Token)) + .ShouldThrow(); + } + + executed.Should().BeFalse(); + _totalTimeSlept.Should().Be(0); + } + + [Fact] + public void InjectLatency_With_Context_Should_not_execute_user_delegate_if_user_cancelationtoken_cancelled_on_injectionrate_config_delegate() + { + var delay = TimeSpan.FromMilliseconds(500); + var context = new Context(); + context["ShouldInjectLatency"] = true; + context["Enabled"] = true; + context["InjectionRate"] = 0.6; + + Func latencyProvider = (ctx, ct) => + { + if ((bool)ctx["ShouldInjectLatency"]) + { + return delay; + } + + return TimeSpan.FromMilliseconds(0); + }; + + Boolean executed = false; + using (CancellationTokenSource cts = new CancellationTokenSource()) + { + Func enabled = (ctx, ct) => + { + return (bool)ctx["Enabled"]; + }; + + Func injectionRate = (ctx, ct) => + { + cts.Cancel(); + + if (ctx["InjectionRate"] != null) + { + return (double)ctx["InjectionRate"]; + } + + return 0; + }; + + var policy = MonkeyPolicy.InjectLatency(latencyProvider, injectionRate, enabled); + + policy.Invoking(x => x.Execute((ctx, ct) => { executed = true; }, context, cts.Token)) + .ShouldThrow(); + } + + executed.Should().BeFalse(); + _totalTimeSlept.Should().Be(0); + } + + [Fact] + public void InjectLatency_With_Context_Should_not_execute_user_delegate_if_user_cancelationtoken_cancelled_on_latency_config_delegate() + { + var delay = TimeSpan.FromMilliseconds(500); + var context = new Context(); + context["ShouldInjectLatency"] = true; + context["Enabled"] = true; + context["InjectionRate"] = 0.6; + + Boolean executed = false; + using (CancellationTokenSource cts = new CancellationTokenSource()) + { + Func enabled = (ctx, ct) => + { + return (bool)ctx["Enabled"]; + }; + + Func injectionRate = (ctx, ct) => + { + if (ctx["InjectionRate"] != null) + { + return (double)ctx["InjectionRate"]; + } + + return 0; + }; + + Func latencyProvider = (ctx, ct) => + { + cts.Cancel(); + + if ((bool)ctx["ShouldInjectLatency"]) + { + return delay; + } + + return TimeSpan.FromMilliseconds(0); + }; + + var policy = MonkeyPolicy.InjectLatency(latencyProvider, injectionRate, enabled); + + policy.Invoking(x => x.Execute((ctx, ct) => { executed = true; }, context, cts.Token)) + .ShouldThrow(); + } + + executed.Should().BeFalse(); + _totalTimeSlept.Should().Be(0); + } + + [Theory] + [InlineData(TimeoutStrategy.Optimistic)] + [InlineData(TimeoutStrategy.Pessimistic)] + public void InjectLatency_With_Context_Should_not_inject_the_whole_latency_if_user_cancelationtoken_is_signaled_from_timeout(TimeoutStrategy timeoutStrategy) + { + SystemClock.Reset(); + var timeout = TimeSpan.FromSeconds(5); + var delay = TimeSpan.FromSeconds(10); + var context = new Context(); + context["ShouldInjectLatency"] = true; + context["Enabled"] = true; + context["InjectionRate"] = 0.6; + + Boolean executed = false; + Stopwatch watch = new Stopwatch(); + + using (CancellationTokenSource cts = new CancellationTokenSource()) + { + Func enabled = (ctx, ct) => + { + return (bool)ctx["Enabled"]; + }; + + Func injectionRate = (ctx, ct) => + { + if (ctx["InjectionRate"] != null) + { + return (double)ctx["InjectionRate"]; + } + + return 0; + }; + + Func latencyProvider = (ctx, ct) => + { + if ((bool)ctx["ShouldInjectLatency"]) + { + return delay; + } + + return TimeSpan.FromMilliseconds(0); + }; + + var policy = Policy.Timeout(timeout, timeoutStrategy) + .Wrap(MonkeyPolicy.InjectLatency(latencyProvider, injectionRate, enabled)); + + watch.Start(); + policy.Invoking(x => { x.Execute((ctx, ct) => { executed = true; }, context, cts.Token); }) + .ShouldThrow(); + watch.Stop(); + } + + executed.Should().BeFalse(); + watch.Elapsed.Should().BeCloseTo(timeout, ((int)TimeSpan.FromSeconds(3).TotalMilliseconds)); + } + + #endregion } } diff --git a/src/Polly.Contrib.Simmy.Specs/Latency/InjectLatencyTResultAsyncSpecs .cs b/src/Polly.Contrib.Simmy.Specs/Latency/InjectLatencyTResultAsyncSpecs .cs index 1cff874..b4801e3 100644 --- a/src/Polly.Contrib.Simmy.Specs/Latency/InjectLatencyTResultAsyncSpecs .cs +++ b/src/Polly.Contrib.Simmy.Specs/Latency/InjectLatencyTResultAsyncSpecs .cs @@ -1,10 +1,12 @@ using System; +using System.Diagnostics; using System.Threading; using System.Threading.Tasks; using FluentAssertions; using Polly.Utilities; using Polly.Contrib.Simmy.Specs.Helpers; using Polly.Contrib.Simmy.Utilities; +using Polly.Timeout; using Xunit; namespace Polly.Contrib.Simmy.Specs.Latency @@ -99,7 +101,7 @@ public async Task InjectLatency_With_Context_With_Enabled_Lambda_Should_Introduc var context = new Context(); context["Enabled"] = true; - Func> enabled = async (ctx) => + Func> enabled = async (ctx, ct) => { return await Task.FromResult((bool)ctx["Enabled"]); }; @@ -122,7 +124,7 @@ public async Task InjectLatency_With_Context_With_Enabled_Lambda_Should_Not_Intr var context = new Context(); context["Enabled"] = false; - Func> enabled = async (ctx) => + Func> enabled = async (ctx, ct) => { return await Task.FromResult((bool)ctx["Enabled"]); }; @@ -145,7 +147,7 @@ public async Task InjectLatency_With_Context_With_Enabled_Lambda_Should_Not_Intr var context = new Context(); context["Enabled"] = true; - Func> enabled = async (ctx) => + Func> enabled = async (ctx, ct) => { return await Task.FromResult((bool)ctx["Enabled"]); }; @@ -169,16 +171,16 @@ public async Task InjectLatency_With_Context_With_InjectionRate_Lambda_Should_In context["Enabled"] = true; context["InjectionRate"] = 0.6; - Func> enabled = async (ctx) => + Func> enabled = async (ctx, ct) => { return await Task.FromResult((bool)ctx["Enabled"]); }; - Func> injectionRate = async (ctx) => + Func> injectionRate = async (ctx, ct) => { if (ctx["InjectionRate"] != null) { - return await Task.FromResult((double) ctx["InjectionRate"]); + return await Task.FromResult((double)ctx["InjectionRate"]); } return await Task.FromResult(0); @@ -203,16 +205,16 @@ public async Task InjectLatency_With_Context_With_InjectionRate_Lambda_Should_No context["Enabled"] = true; context["InjectionRate"] = 0.3; - Func> enabled = async (ctx) => + Func> enabled = async (ctx, ct) => { return await Task.FromResult((bool)ctx["Enabled"]); }; - Func> injectionRate = async (ctx) => + Func> injectionRate = async (ctx, ct) => { if (ctx["InjectionRate"] != null) { - return await Task.FromResult((double) ctx["InjectionRate"]); + return await Task.FromResult((double)ctx["InjectionRate"]); } return await Task.FromResult(0); @@ -248,16 +250,16 @@ public async Task InjectLatency_With_Context_With_Latency_Lambda_Should_Introduc return await Task.FromResult(TimeSpan.FromMilliseconds(0)); }; - Func> enabled = async (ctx) => + Func> enabled = async (ctx, ct) => { return await Task.FromResult((bool)ctx["Enabled"]); }; - Func> injectionRate = async (ctx) => + Func> injectionRate = async (ctx, ct) => { if (ctx["InjectionRate"] != null) { - return await Task.FromResult((double) ctx["InjectionRate"]); + return await Task.FromResult((double)ctx["InjectionRate"]); } return await Task.FromResult(0); @@ -293,16 +295,16 @@ public async Task InjectLatency_With_Context_With_Latency_Lambda_Should_Not_Intr return await Task.FromResult(TimeSpan.FromMilliseconds(0)); }; - Func> enabled = async (ctx) => + Func> enabled = async (ctx, ct) => { return await Task.FromResult((bool)ctx["Enabled"]); }; - Func> injectionRate = async (ctx) => + Func> injectionRate = async (ctx, ct) => { if (ctx["InjectionRate"] != null) { - return await Task.FromResult((double) ctx["InjectionRate"]); + return await Task.FromResult((double)ctx["InjectionRate"]); } return await Task.FromResult(0); @@ -338,16 +340,16 @@ public async Task InjectLatency_With_Context_With_Latency_Lambda_Should_Not_Intr return await Task.FromResult(TimeSpan.FromMilliseconds(0)); }; - Func> enabled = async (ctx) => + Func> enabled = async (ctx, ct) => { return await Task.FromResult((bool)ctx["Enabled"]); }; - Func> injectionRate = async (ctx) => + Func> injectionRate = async (ctx, ct) => { if (ctx["InjectionRate"] != null) { - return await Task.FromResult((double) ctx["InjectionRate"]); + return await Task.FromResult((double)ctx["InjectionRate"]); } return await Task.FromResult(0); @@ -365,5 +367,274 @@ public async Task InjectLatency_With_Context_With_Latency_Lambda_Should_Not_Intr } #endregion + + #region Cancellable scenarios + + [Fact] + public void InjectLatency_With_Context_Should_not_execute_user_delegate_if_user_cancelationtoken_cancelled_before_to_start_execution() + { + var delay = TimeSpan.FromMilliseconds(500); + var context = new Context(); + context["ShouldInjectLatency"] = true; + context["Enabled"] = true; + context["InjectionRate"] = 0.6; + + Func> latencyProvider = async (ctx, ct) => + { + if ((bool)ctx["ShouldInjectLatency"]) + { + return await Task.FromResult(delay); + } + + return await Task.FromResult(TimeSpan.FromMilliseconds(0)); + }; + + Func> enabled = async (ctx, ct) => + { + return await Task.FromResult((bool)ctx["Enabled"]); + }; + + Func> injectionRate = async (ctx, ct) => + { + if (ctx["InjectionRate"] != null) + { + return await Task.FromResult((double)ctx["InjectionRate"]); + } + + return await Task.FromResult(0); + }; + + Boolean executed = false; + var policy = MonkeyPolicy.InjectLatencyAsync(latencyProvider, injectionRate, enabled); + Func> actionAsync = (_, ct) => { executed = true; return Task.FromResult(ResultPrimitive.Good); }; + + using (CancellationTokenSource cts = new CancellationTokenSource()) + { + cts.Cancel(); + + policy.Awaiting(async x => await x.ExecuteAsync(actionAsync, context, cts.Token)) + .ShouldThrow(); + } + + executed.Should().BeFalse(); + _totalTimeSlept.Should().Be(0); + } + + [Fact] + public void InjectLatency_With_Context_Should_not_execute_user_delegate_if_user_cancelationtoken_cancelled_on_enabled_config_delegate() + { + var delay = TimeSpan.FromMilliseconds(500); + var context = new Context(); + context["ShouldInjectLatency"] = true; + context["Enabled"] = true; + context["InjectionRate"] = 0.6; + + Func> latencyProvider = async (ctx, ct) => + { + if ((bool)ctx["ShouldInjectLatency"]) + { + return await Task.FromResult(delay); + } + + return await Task.FromResult(TimeSpan.FromMilliseconds(0)); + }; + + Func> injectionRate = async (ctx, ct) => + { + if (ctx["InjectionRate"] != null) + { + return await Task.FromResult((double)ctx["InjectionRate"]); + } + + return await Task.FromResult(0); + }; + + Boolean executed = false; + Func> actionAsync = (_, ct) => { executed = true; return Task.FromResult(ResultPrimitive.Good); }; + + using (CancellationTokenSource cts = new CancellationTokenSource()) + { + Func> enabled = async (ctx, ct) => + { + cts.Cancel(); + return await Task.FromResult((bool)ctx["Enabled"]); + }; + + var policy = MonkeyPolicy.InjectLatencyAsync(latencyProvider, injectionRate, enabled); + + policy.Awaiting(async x => await x.ExecuteAsync(actionAsync, context, cts.Token)) + .ShouldThrow(); + } + + executed.Should().BeFalse(); + _totalTimeSlept.Should().Be(0); + } + + [Fact] + public void InjectLatency_With_Context_Should_not_execute_user_delegate_if_user_cancelationtoken_cancelled_on_injectionrate_config_delegate() + { + var delay = TimeSpan.FromMilliseconds(500); + var context = new Context(); + context["ShouldInjectLatency"] = true; + context["Enabled"] = true; + context["InjectionRate"] = 0.6; + + Func> latencyProvider = async (ctx, ct) => + { + if ((bool)ctx["ShouldInjectLatency"]) + { + return await Task.FromResult(delay); + } + + return await Task.FromResult(TimeSpan.FromMilliseconds(0)); + }; + + Boolean executed = false; + Func> actionAsync = (_, ct) => { executed = true; return Task.FromResult(ResultPrimitive.Good); }; + + using (CancellationTokenSource cts = new CancellationTokenSource()) + { + Func> enabled = async (ctx, ct) => + { + return await Task.FromResult((bool)ctx["Enabled"]); + }; + + Func> injectionRate = async (ctx, ct) => + { + cts.Cancel(); + + if (ctx["InjectionRate"] != null) + { + return await Task.FromResult((double)ctx["InjectionRate"]); + } + + return await Task.FromResult(0); + }; + + var policy = MonkeyPolicy.InjectLatencyAsync(latencyProvider, injectionRate, enabled); + + policy.Awaiting(async x => await x.ExecuteAsync(actionAsync, context, cts.Token)) + .ShouldThrow(); + } + + executed.Should().BeFalse(); + _totalTimeSlept.Should().Be(0); + } + + [Fact] + public void InjectLatency_With_Context_Should_not_execute_user_delegate_if_user_cancelationtoken_cancelled_on_latency_config_delegate() + { + var delay = TimeSpan.FromMilliseconds(500); + var context = new Context(); + context["ShouldInjectLatency"] = true; + context["Enabled"] = true; + context["InjectionRate"] = 0.6; + + Boolean executed = false; + Func> actionAsync = (_, ct) => { executed = true; return Task.FromResult(ResultPrimitive.Good); }; + + using (CancellationTokenSource cts = new CancellationTokenSource()) + { + Func> enabled = async (ctx, ct) => + { + return await Task.FromResult((bool)ctx["Enabled"]); + }; + + Func> injectionRate = async (ctx, ct) => + { + if (ctx["InjectionRate"] != null) + { + return await Task.FromResult((double)ctx["InjectionRate"]); + } + + return await Task.FromResult(0); + }; + + Func> latencyProvider = async (ctx, ct) => + { + cts.Cancel(); + + if ((bool)ctx["ShouldInjectLatency"]) + { + return await Task.FromResult(delay); + } + + return await Task.FromResult(TimeSpan.FromMilliseconds(0)); + }; + + var policy = MonkeyPolicy.InjectLatencyAsync(latencyProvider, injectionRate, enabled); + + policy.Awaiting(async x => await x.ExecuteAsync(actionAsync, context, cts.Token)) + .ShouldThrow(); + } + + executed.Should().BeFalse(); + _totalTimeSlept.Should().Be(0); + } + + [Theory] + [InlineData(TimeoutStrategy.Optimistic)] + [InlineData(TimeoutStrategy.Pessimistic)] + public void InjectLatency_With_Context_Should_not_inject_the_whole_latency_if_user_cancelationtoken_is_signaled_from_timeout(TimeoutStrategy timeoutStrategy) + { + SystemClock.Reset(); + var timeout = TimeSpan.FromSeconds(5); + var delay = TimeSpan.FromSeconds(10); + var context = new Context(); + context["ShouldInjectLatency"] = true; + context["Enabled"] = true; + context["InjectionRate"] = 0.6; + + Boolean executed = false; + Stopwatch watch = new Stopwatch(); + + using (CancellationTokenSource cts = new CancellationTokenSource()) + { + Func> enabled = async (ctx, ct) => + { + return await Task.FromResult((bool)ctx["Enabled"]); + }; + + Func> injectionRate = async (ctx, ct) => + { + if (ctx["InjectionRate"] != null) + { + return await Task.FromResult((double)ctx["InjectionRate"]); + } + + return await Task.FromResult(0); + }; + + Func> latencyProvider = async (ctx, ct) => + { + if ((bool)ctx["ShouldInjectLatency"]) + { + return await Task.FromResult(delay); + } + + return await Task.FromResult(TimeSpan.FromMilliseconds(0)); + }; + + Func> actionAsync = (_, ct) => + { + executed = true; + return Task.FromResult(ResultPrimitive.Good); + }; + + var policy = Policy.TimeoutAsync(timeout, timeoutStrategy) + .WrapAsync(MonkeyPolicy.InjectLatencyAsync(latencyProvider, injectionRate, enabled)); + + watch.Start(); + policy.Awaiting(async x => { await x.ExecuteAsync(actionAsync, context, cts.Token); }) + .ShouldThrow(); + + watch.Stop(); + } + + executed.Should().BeFalse(); + watch.Elapsed.Should().BeCloseTo(timeout, ((int)TimeSpan.FromSeconds(3).TotalMilliseconds)); + } + + #endregion } } diff --git a/src/Polly.Contrib.Simmy.Specs/Latency/InjectLatencyTResultSpecs.cs b/src/Polly.Contrib.Simmy.Specs/Latency/InjectLatencyTResultSpecs.cs index 07ab6a9..315f7af 100644 --- a/src/Polly.Contrib.Simmy.Specs/Latency/InjectLatencyTResultSpecs.cs +++ b/src/Polly.Contrib.Simmy.Specs/Latency/InjectLatencyTResultSpecs.cs @@ -1,9 +1,12 @@ using System; +using System.Diagnostics; +using System.Threading; using FluentAssertions; using Polly.Utilities; using Polly.Contrib.Simmy.Specs.Helpers; using Polly.Contrib.Simmy.Utilities; using Xunit; +using Polly.Timeout; namespace Polly.Contrib.Simmy.Specs.Latency { @@ -97,7 +100,7 @@ public void InjectLatency_With_Context_With_Enabled_Lambda_Should_Introduce_Dela var context = new Context(); context["Enabled"] = true; - Func enabled = (ctx) => + Func enabled = (ctx, ct) => { return ((bool)ctx["Enabled"]); }; @@ -120,7 +123,7 @@ public void InjectLatency_With_Context_With_Enabled_Lambda_Should_Not_Introduce_ var context = new Context(); context["Enabled"] = false; - Func enabled = (ctx) => + Func enabled = (ctx, ct) => { return ((bool)ctx["Enabled"]); }; @@ -143,7 +146,7 @@ public void InjectLatency_With_Context_With_Enabled_Lambda_Should_Not_Introduce_ var context = new Context(); context["Enabled"] = true; - Func enabled = (ctx) => + Func enabled = (ctx, ct) => { return ((bool)ctx["Enabled"]); }; @@ -167,12 +170,12 @@ public void InjectLatency_With_Context_With_InjectionRate_Lambda_Should_Introduc context["Enabled"] = true; context["InjectionRate"] = 0.6; - Func enabled = (ctx) => + Func enabled = (ctx, ct) => { return ((bool)ctx["Enabled"]); }; - Func injectionRate = (ctx) => + Func injectionRate = (ctx, ct) => { if (ctx["InjectionRate"] != null) { @@ -201,12 +204,12 @@ public void InjectLatency_With_Context_With_InjectionRate_Lambda_Should_Not_Intr context["Enabled"] = true; context["InjectionRate"] = 0.3; - Func enabled = (ctx) => + Func enabled = (ctx, ct) => { return ((bool)ctx["Enabled"]); }; - Func injectionRate = (ctx) => + Func injectionRate = (ctx, ct) => { if (ctx["InjectionRate"] != null) { @@ -236,7 +239,7 @@ public void InjectLatency_With_Context_With_Latency_Lambda_Should_Introduce_Dela context["Enabled"] = true; context["InjectionRate"] = 0.6; - Func latencyProvider = (ctx) => + Func latencyProvider = (ctx, ct) => { if ((bool)ctx["ShouldInjectLatency"]) { @@ -246,12 +249,12 @@ public void InjectLatency_With_Context_With_Latency_Lambda_Should_Introduce_Dela return TimeSpan.FromMilliseconds(0); }; - Func enabled = (ctx) => + Func enabled = (ctx, ct) => { return ((bool)ctx["Enabled"]); }; - Func injectionRate = (ctx) => + Func injectionRate = (ctx, ct) => { if (ctx["InjectionRate"] != null) { @@ -281,7 +284,7 @@ public void InjectLatency_With_Context_With_Latency_Lambda_Should_Not_Introduce_ context["Enabled"] = true; context["InjectionRate"] = 0.6; - Func latencyProvider = (ctx) => + Func latencyProvider = (ctx, ct) => { if ((bool)ctx["ShouldInjectLatency"]) { @@ -291,12 +294,12 @@ public void InjectLatency_With_Context_With_Latency_Lambda_Should_Not_Introduce_ return TimeSpan.FromMilliseconds(0); }; - Func enabled = (ctx) => + Func enabled = (ctx, ct) => { return ((bool)ctx["Enabled"]); }; - Func injectionRate = (ctx) => + Func injectionRate = (ctx, ct) => { if (ctx["InjectionRate"] != null) { @@ -326,7 +329,7 @@ public void InjectLatency_With_Context_With_Latency_Lambda_Should_Not_Introduce_ context["Enabled"] = true; context["InjectionRate"] = 0.3; - Func latencyProvider = (ctx) => + Func latencyProvider = (ctx, ct) => { if ((bool)ctx["ShouldInjectLatency"]) { @@ -336,12 +339,12 @@ public void InjectLatency_With_Context_With_Latency_Lambda_Should_Not_Introduce_ return TimeSpan.FromMilliseconds(0); }; - Func enabled = (ctx) => + Func enabled = (ctx, ct) => { return ((bool)ctx["Enabled"]); }; - Func injectionRate = (ctx) => + Func injectionRate = (ctx, ct) => { if (ctx["InjectionRate"] != null) { @@ -362,5 +365,267 @@ public void InjectLatency_With_Context_With_Latency_Lambda_Should_Not_Introduce_ } #endregion + + #region Cancellable scenarios + + [Fact] + public void InjectLatency_With_Context_Should_not_execute_user_delegate_if_user_cancelationtoken_cancelled_before_to_start_execution() + { + var delay = TimeSpan.FromMilliseconds(500); + var context = new Context(); + context["ShouldInjectLatency"] = true; + context["Enabled"] = true; + context["InjectionRate"] = 0.6; + + Func latencyProvider = (ctx, ct) => + { + if ((bool)ctx["ShouldInjectLatency"]) + { + return delay; + } + + return TimeSpan.FromMilliseconds(0); + }; + + Func enabled = (ctx, ct) => + { + return (bool)ctx["Enabled"]; + }; + + Func injectionRate = (ctx, ct) => + { + if (ctx["InjectionRate"] != null) + { + return (double)ctx["InjectionRate"]; + } + + return 0; + }; + + Boolean executed = false; + Func action = (ctx, ct) => { executed = true; return ResultPrimitive.Good; }; + var policy = MonkeyPolicy.InjectLatency(latencyProvider, injectionRate, enabled); + + using (CancellationTokenSource cts = new CancellationTokenSource()) + { + cts.Cancel(); + + policy.Invoking(x => x.Execute(action, context, cts.Token)) + .ShouldThrow(); + } + + executed.Should().BeFalse(); + _totalTimeSlept.Should().Be(0); + } + + [Fact] + public void InjectLatency_With_Context_Should_not_execute_user_delegate_if_user_cancelationtoken_cancelled_on_enabled_config_delegate() + { + var delay = TimeSpan.FromMilliseconds(500); + var context = new Context(); + context["ShouldInjectLatency"] = true; + context["Enabled"] = true; + context["InjectionRate"] = 0.6; + + Func latencyProvider = (ctx, ct) => + { + if ((bool)ctx["ShouldInjectLatency"]) + { + return delay; + } + + return TimeSpan.FromMilliseconds(0); + }; + + Func injectionRate = (ctx, ct) => + { + if (ctx["InjectionRate"] != null) + { + return (double)ctx["InjectionRate"]; + } + + return 0; + }; + + Boolean executed = false; + Func action = (ctx, ct) => { executed = true; return ResultPrimitive.Good; }; + + using (CancellationTokenSource cts = new CancellationTokenSource()) + { + Func enabled = (ctx, ct) => + { + cts.Cancel(); + return (bool)ctx["Enabled"]; + }; + + var policy = MonkeyPolicy.InjectLatency(latencyProvider, injectionRate, enabled); + policy.Invoking(x => x.Execute(action, context, cts.Token)) + .ShouldThrow(); + } + + executed.Should().BeFalse(); + _totalTimeSlept.Should().Be(0); + } + + [Fact] + public void InjectLatency_With_Context_Should_not_execute_user_delegate_if_user_cancelationtoken_cancelled_on_injectionrate_config_delegate() + { + var delay = TimeSpan.FromMilliseconds(500); + var context = new Context(); + context["ShouldInjectLatency"] = true; + context["Enabled"] = true; + context["InjectionRate"] = 0.6; + + Func latencyProvider = (ctx, ct) => + { + if ((bool)ctx["ShouldInjectLatency"]) + { + return delay; + } + + return TimeSpan.FromMilliseconds(0); + }; + + Boolean executed = false; + Func action = (ctx, ct) => { executed = true; return ResultPrimitive.Good; }; + + using (CancellationTokenSource cts = new CancellationTokenSource()) + { + Func enabled = (ctx, ct) => + { + return (bool)ctx["Enabled"]; + }; + + Func injectionRate = (ctx, ct) => + { + cts.Cancel(); + + if (ctx["InjectionRate"] != null) + { + return (double)ctx["InjectionRate"]; + } + + return 0; + }; + + var policy = MonkeyPolicy.InjectLatency(latencyProvider, injectionRate, enabled); + + policy.Invoking(x => x.Execute(action, context, cts.Token)) + .ShouldThrow(); + } + + executed.Should().BeFalse(); + _totalTimeSlept.Should().Be(0); + } + + [Fact] + public void InjectLatency_With_Context_Should_not_execute_user_delegate_if_user_cancelationtoken_cancelled_on_latency_config_delegate() + { + var delay = TimeSpan.FromMilliseconds(500); + var context = new Context(); + context["ShouldInjectLatency"] = true; + context["Enabled"] = true; + context["InjectionRate"] = 0.6; + + Boolean executed = false; + using (CancellationTokenSource cts = new CancellationTokenSource()) + { + Func enabled = (ctx, ct) => + { + return (bool)ctx["Enabled"]; + }; + + Func injectionRate = (ctx, ct) => + { + if (ctx["InjectionRate"] != null) + { + return (double)ctx["InjectionRate"]; + } + + return 0; + }; + + Func latencyProvider = (ctx, ct) => + { + cts.Cancel(); + + if ((bool)ctx["ShouldInjectLatency"]) + { + return delay; + } + + return TimeSpan.FromMilliseconds(0); + }; + + var policy = MonkeyPolicy.InjectLatency(latencyProvider, injectionRate, enabled); + Func action = (ctx, ct) => { executed = true; return ResultPrimitive.Good; }; + + policy.Invoking(x => x.Execute(action, context, cts.Token)) + .ShouldThrow(); + } + + executed.Should().BeFalse(); + _totalTimeSlept.Should().Be(0); + } + + [Theory] + [InlineData(TimeoutStrategy.Optimistic)] + [InlineData(TimeoutStrategy.Pessimistic)] + public void InjectLatency_With_Context_Should_not_inject_the_whole_latency_if_user_cancelationtoken_is_signaled_from_timeout(TimeoutStrategy timeoutStrategy) + { + SystemClock.Reset(); + var timeout = TimeSpan.FromSeconds(5); + var delay = TimeSpan.FromSeconds(10); + var context = new Context(); + context["ShouldInjectLatency"] = true; + context["Enabled"] = true; + context["InjectionRate"] = 0.6; + + Boolean executed = false; + Stopwatch watch = new Stopwatch(); + + using (CancellationTokenSource cts = new CancellationTokenSource()) + { + Func enabled = (ctx, ct) => + { + return (bool)ctx["Enabled"]; + }; + + Func injectionRate = (ctx, ct) => + { + if (ctx["InjectionRate"] != null) + { + return (double)ctx["InjectionRate"]; + } + + return 0; + }; + + Func latencyProvider = (ctx, ct) => + { + if ((bool)ctx["ShouldInjectLatency"]) + { + return delay; + } + + return TimeSpan.FromMilliseconds(0); + }; + + Func action = (ctx, ct) => { executed = true; return ResultPrimitive.Good; }; + + var policy = Policy.Timeout(timeout, timeoutStrategy) + .Wrap(MonkeyPolicy.InjectLatency(latencyProvider, injectionRate, enabled)); + + watch.Start(); + policy.Invoking(x => { x.Execute(action, context, cts.Token); }) + .ShouldThrow(); + watch.Stop(); + } + + executed.Should().BeFalse(); + watch.Elapsed.Should().BeCloseTo(timeout, ((int)TimeSpan.FromSeconds(3).TotalMilliseconds)); + } + + #endregion } } diff --git a/src/Polly.Contrib.Simmy.nuspec b/src/Polly.Contrib.Simmy.nuspec index 43b3b1c..ce6acb2 100644 --- a/src/Polly.Contrib.Simmy.nuspec +++ b/src/Polly.Contrib.Simmy.nuspec @@ -13,6 +13,10 @@ Resilience Chaos-engineering Fault-injection Copyright © 2019, App vNext + 0.2.0 + --------------------- + - Makes InjectLatency policies cancellable (both sync and async) + - Add support for cancellation on async configuration-providing delegates 0.1.0 --------------------- - Initial launch diff --git a/src/Polly.Contrib.Simmy/AsyncMonkeyEngine.cs b/src/Polly.Contrib.Simmy/AsyncMonkeyEngine.cs index fe2f191..db90c82 100644 --- a/src/Polly.Contrib.Simmy/AsyncMonkeyEngine.cs +++ b/src/Polly.Contrib.Simmy/AsyncMonkeyEngine.cs @@ -7,14 +7,29 @@ namespace Polly.Contrib.Simmy { internal static class AsyncMonkeyEngine { - private static async Task ShouldInjectAsync(Context context, Func> injectionRate, Func> enabled, bool continueOnCapturedContext) + private static async Task ShouldInjectAsync( + Context context, + CancellationToken cancellationToken, + Func> injectionRate, + Func> enabled, + bool continueOnCapturedContext) { - if (!await enabled(context).ConfigureAwait(continueOnCapturedContext)) + // to prevent execute config delegates if token is signaled before to start. + cancellationToken.ThrowIfCancellationRequested(); + + if (!await enabled(context, cancellationToken).ConfigureAwait(continueOnCapturedContext)) { return false; } - double injectionThreshold = await injectionRate(context).ConfigureAwait(continueOnCapturedContext); + // to prevent execute injectionRate config delegate if token is signaled on enable configuration delegate. + cancellationToken.ThrowIfCancellationRequested(); + + double injectionThreshold = await injectionRate(context, cancellationToken).ConfigureAwait(continueOnCapturedContext); + + // to prevent execute further config delegates if token is signaled on injectionRate configuration delegate. + cancellationToken.ThrowIfCancellationRequested(); + if (injectionThreshold < 0) { throw new ArgumentOutOfRangeException(nameof(injectionThreshold), "Injection rate/threshold in Monkey policies should always be a double between [0, 1]; never a negative number."); @@ -24,7 +39,7 @@ private static async Task ShouldInjectAsync(Context context, Func InjectBehaviourImplementationAsync( @@ -32,15 +47,17 @@ internal static async Task InjectBehaviourImplementationAsync( Context context, CancellationToken cancellationToken, Func injectedBehaviour, - Func> injectionRate, - Func> enabled, + Func> injectionRate, + Func> enabled, bool continueOnCapturedContext) { - if (await ShouldInjectAsync(context, injectionRate, enabled, continueOnCapturedContext).ConfigureAwait(continueOnCapturedContext)) + if (await ShouldInjectAsync(context, cancellationToken, injectionRate, enabled, continueOnCapturedContext).ConfigureAwait(continueOnCapturedContext)) { await injectedBehaviour(context, cancellationToken).ConfigureAwait(continueOnCapturedContext); } + // to prevent execute the user's action if token is signaled on injectedBehaviour delegate. + cancellationToken.ThrowIfCancellationRequested(); return await action(context, cancellationToken).ConfigureAwait(continueOnCapturedContext); } @@ -49,13 +66,17 @@ internal static async Task InjectExceptionImplementationAsync( Context context, CancellationToken cancellationToken, Func> injectedException, - Func> injectionRate, - Func> enabled, + Func> injectionRate, + Func> enabled, bool continueOnCapturedContext) { - if (await ShouldInjectAsync(context, injectionRate, enabled, continueOnCapturedContext).ConfigureAwait(continueOnCapturedContext)) + if (await ShouldInjectAsync(context, cancellationToken, injectionRate, enabled, continueOnCapturedContext).ConfigureAwait(continueOnCapturedContext)) { Exception exception = await injectedException(context, cancellationToken).ConfigureAwait(continueOnCapturedContext); + + // to prevent throws the exception if token is signaled on injectedException configuration delegate. + cancellationToken.ThrowIfCancellationRequested(); + if (exception != null) { throw exception; @@ -70,15 +91,17 @@ internal static async Task InjectResultImplementationAsync( Context context, CancellationToken cancellationToken, Func> injectedResult, - Func> injectionRate, - Func> enabled, + Func> injectionRate, + Func> enabled, bool continueOnCapturedContext) { - if (await ShouldInjectAsync(context, injectionRate, enabled, continueOnCapturedContext).ConfigureAwait(continueOnCapturedContext)) + if (await ShouldInjectAsync(context, cancellationToken, injectionRate, enabled, continueOnCapturedContext).ConfigureAwait(continueOnCapturedContext)) { return await injectedResult(context, cancellationToken).ConfigureAwait(continueOnCapturedContext); } + // to prevent inject the result if token is signaled on injectedResult delegate. + cancellationToken.ThrowIfCancellationRequested(); return await action(context, cancellationToken).ConfigureAwait(continueOnCapturedContext); } } diff --git a/src/Polly.Contrib.Simmy/AsyncMonkeyPolicy.cs b/src/Polly.Contrib.Simmy/AsyncMonkeyPolicy.cs index 185f4b2..8d5afda 100644 --- a/src/Polly.Contrib.Simmy/AsyncMonkeyPolicy.cs +++ b/src/Polly.Contrib.Simmy/AsyncMonkeyPolicy.cs @@ -1,4 +1,5 @@ using System; +using System.Threading; using System.Threading.Tasks; namespace Polly.Contrib.Simmy @@ -8,10 +9,10 @@ namespace Polly.Contrib.Simmy /// public abstract class AsyncMonkeyPolicy : AsyncPolicy, IMonkeyPolicy { - internal Func> InjectionRate { get; } - internal Func> Enabled { get; } + internal Func> InjectionRate { get; } + internal Func> Enabled { get; } - internal AsyncMonkeyPolicy(Func> injectionRate, Func> enabled) + internal AsyncMonkeyPolicy(Func> injectionRate, Func> enabled) { InjectionRate = injectionRate ?? throw new ArgumentNullException(nameof(injectionRate)); Enabled = enabled ?? throw new ArgumentNullException(nameof(enabled)); @@ -24,10 +25,10 @@ internal AsyncMonkeyPolicy(Func> injectionRate, FuncThe type of return values this policy will handle. public abstract class AsyncMonkeyPolicy : AsyncPolicy, IMonkeyPolicy { - internal Func> InjectionRate { get; } - internal Func> Enabled { get; } + internal Func> InjectionRate { get; } + internal Func> Enabled { get; } - internal AsyncMonkeyPolicy(Func> injectionRate, Func> enabled) + internal AsyncMonkeyPolicy(Func> injectionRate, Func> enabled) { InjectionRate = injectionRate ?? throw new ArgumentNullException(nameof(injectionRate)); Enabled = enabled ?? throw new ArgumentNullException(nameof(enabled)); diff --git a/src/Polly.Contrib.Simmy/Behavior/AsyncInjectBehaviourPolicy.cs b/src/Polly.Contrib.Simmy/Behavior/AsyncInjectBehaviourPolicy.cs index 539f26e..fd2607b 100644 --- a/src/Polly.Contrib.Simmy/Behavior/AsyncInjectBehaviourPolicy.cs +++ b/src/Polly.Contrib.Simmy/Behavior/AsyncInjectBehaviourPolicy.cs @@ -11,7 +11,7 @@ public class AsyncInjectBehaviourPolicy : AsyncMonkeyPolicy { private readonly Func _behaviour; - internal AsyncInjectBehaviourPolicy(Func behaviour, Func> injectionRate, Func> enabled) + internal AsyncInjectBehaviourPolicy(Func behaviour, Func> injectionRate, Func> enabled) : base(injectionRate, enabled) { _behaviour = behaviour ?? throw new ArgumentNullException(nameof(behaviour)); @@ -40,7 +40,7 @@ public class AsyncInjectBehaviourPolicy : AsyncMonkeyPolicy { private readonly Func _behaviour; - internal AsyncInjectBehaviourPolicy(Func behaviour, Func> injectionRate, Func> enabled) + internal AsyncInjectBehaviourPolicy(Func behaviour, Func> injectionRate, Func> enabled) : base(injectionRate, enabled) { _behaviour = behaviour ?? throw new ArgumentNullException(nameof(behaviour)); diff --git a/src/Polly.Contrib.Simmy/Behavior/AsyncInjectBehaviourSyntax.cs b/src/Polly.Contrib.Simmy/Behavior/AsyncInjectBehaviourSyntax.cs index 725ced7..0a72ae7 100644 --- a/src/Polly.Contrib.Simmy/Behavior/AsyncInjectBehaviourSyntax.cs +++ b/src/Polly.Contrib.Simmy/Behavior/AsyncInjectBehaviourSyntax.cs @@ -27,8 +27,8 @@ public static AsyncInjectBehaviourPolicy InjectBehaviourAsync( if (enabled == null) throw new ArgumentNullException(nameof(enabled)); Task BehaviourLambda(Context _, CancellationToken __) => behaviour(); - Task InjectionRateLambda(Context _) => Task.FromResult(injectionRate); - Task EnabledLambda(Context _) => enabled(); + Task InjectionRateLambda(Context _, CancellationToken __) => Task.FromResult(injectionRate); + Task EnabledLambda(Context _, CancellationToken __) => enabled(); return InjectBehaviourAsync(BehaviourLambda, InjectionRateLambda, EnabledLambda); } @@ -49,8 +49,8 @@ public static AsyncInjectBehaviourPolicy InjectBehaviourAsync( if (behaviour == null) throw new ArgumentNullException(nameof(behaviour)); if (enabled == null) throw new ArgumentNullException(nameof(enabled)); - Task InjectionRateLambda(Context _) => Task.FromResult(injectionRate); - Task EnabledLambda(Context _) => enabled(); + Task InjectionRateLambda(Context _, CancellationToken __) => Task.FromResult(injectionRate); + Task EnabledLambda(Context _, CancellationToken __) => enabled(); return InjectBehaviourAsync(behaviour, InjectionRateLambda, EnabledLambda); } @@ -66,12 +66,12 @@ public static AsyncInjectBehaviourPolicy InjectBehaviourAsync( public static AsyncInjectBehaviourPolicy InjectBehaviourAsync( Func behaviour, Double injectionRate, - Func> enabled) + Func> enabled) { if (behaviour == null) throw new ArgumentNullException(nameof(behaviour)); if (enabled == null) throw new ArgumentNullException(nameof(enabled)); - Task InjectionRateLambda(Context _) => Task.FromResult(injectionRate); + Task InjectionRateLambda(Context _, CancellationToken __) => Task.FromResult(injectionRate); return InjectBehaviourAsync(behaviour, InjectionRateLambda, enabled); } @@ -86,8 +86,8 @@ public static AsyncInjectBehaviourPolicy InjectBehaviourAsync( /// The policy instance. public static AsyncInjectBehaviourPolicy InjectBehaviourAsync( Func behaviour, - Func> injectionRate, - Func> enabled) + Func> injectionRate, + Func> enabled) { if (behaviour == null) throw new ArgumentNullException(nameof(behaviour)); if (injectionRate == null) throw new ArgumentNullException(nameof(injectionRate)); diff --git a/src/Polly.Contrib.Simmy/Behavior/AsyncInjectBehaviourTResultSyntax.cs b/src/Polly.Contrib.Simmy/Behavior/AsyncInjectBehaviourTResultSyntax.cs index 26a393c..4abc929 100644 --- a/src/Polly.Contrib.Simmy/Behavior/AsyncInjectBehaviourTResultSyntax.cs +++ b/src/Polly.Contrib.Simmy/Behavior/AsyncInjectBehaviourTResultSyntax.cs @@ -27,8 +27,8 @@ public static AsyncInjectBehaviourPolicy InjectBehaviourAsync( if (enabled == null) throw new ArgumentNullException(nameof(enabled)); Task BehaviourLambda(Context _, CancellationToken __) => behaviour(); - Task InjectionRateLambda(Context _) => Task.FromResult(injectionRate); - Task EnabledLambda(Context _) => enabled(); + Task InjectionRateLambda(Context _, CancellationToken __) => Task.FromResult(injectionRate); + Task EnabledLambda(Context _, CancellationToken __) => enabled(); return InjectBehaviourAsync(BehaviourLambda, InjectionRateLambda, EnabledLambda); } @@ -49,8 +49,8 @@ public static AsyncInjectBehaviourPolicy InjectBehaviourAsync( if (behaviour == null) throw new ArgumentNullException(nameof(behaviour)); if (enabled == null) throw new ArgumentNullException(nameof(enabled)); - Task InjectionRateLambda(Context _) => Task.FromResult(injectionRate); - Task EnabledLambda(Context _) => enabled(); + Task InjectionRateLambda(Context _, CancellationToken __) => Task.FromResult(injectionRate); + Task EnabledLambda(Context _, CancellationToken __) => enabled(); return InjectBehaviourAsync(behaviour, InjectionRateLambda, EnabledLambda); } @@ -66,12 +66,12 @@ public static AsyncInjectBehaviourPolicy InjectBehaviourAsync( public static AsyncInjectBehaviourPolicy InjectBehaviourAsync( Func behaviour, Double injectionRate, - Func> enabled) + Func> enabled) { if (behaviour == null) throw new ArgumentNullException(nameof(behaviour)); if (enabled == null) throw new ArgumentNullException(nameof(enabled)); - Task InjectionRateLambda(Context _) => Task.FromResult(injectionRate); + Task InjectionRateLambda(Context _, CancellationToken __) => Task.FromResult(injectionRate); return InjectBehaviourAsync(behaviour, InjectionRateLambda, enabled); } @@ -86,8 +86,8 @@ public static AsyncInjectBehaviourPolicy InjectBehaviourAsync( /// The policy instance. public static AsyncInjectBehaviourPolicy InjectBehaviourAsync( Func behaviour, - Func> injectionRate, - Func> enabled) + Func> injectionRate, + Func> enabled) { if (behaviour == null) throw new ArgumentNullException(nameof(behaviour)); if (injectionRate == null) throw new ArgumentNullException(nameof(injectionRate)); diff --git a/src/Polly.Contrib.Simmy/Behavior/InjectBehaviourPolicy.cs b/src/Polly.Contrib.Simmy/Behavior/InjectBehaviourPolicy.cs index ae1578d..362bd7d 100644 --- a/src/Polly.Contrib.Simmy/Behavior/InjectBehaviourPolicy.cs +++ b/src/Polly.Contrib.Simmy/Behavior/InjectBehaviourPolicy.cs @@ -8,9 +8,9 @@ namespace Polly.Contrib.Simmy.Behavior /// public class InjectBehaviourPolicy : Simmy.MonkeyPolicy { - private readonly Action _behaviour; + private readonly Action _behaviour; - internal InjectBehaviourPolicy(Action behaviour, Func injectionRate, Func enabled) + internal InjectBehaviourPolicy(Action behaviour, Func injectionRate, Func enabled) : base(injectionRate, enabled) { _behaviour = behaviour ?? throw new ArgumentNullException(nameof(behaviour)); @@ -23,7 +23,7 @@ protected override TResult Implementation(Func _behaviour(ctx), + (ctx, ct) => _behaviour(ctx, ct), InjectionRate, Enabled); } @@ -34,8 +34,8 @@ protected override TResult Implementation(FuncThe type of return values this policy will handle. public class InjectBehaviourPolicy : MonkeyPolicy { - private readonly Action _behaviour; - internal InjectBehaviourPolicy(Action behaviour, Func injectionRate, Func enabled) + private readonly Action _behaviour; + internal InjectBehaviourPolicy(Action behaviour, Func injectionRate, Func enabled) : base(injectionRate, enabled) { _behaviour = behaviour ?? throw new ArgumentNullException(nameof(behaviour)); @@ -48,7 +48,7 @@ protected override TResult Implementation(Func _behaviour(ctx), + (ctx, ct) => _behaviour(ctx, ct), InjectionRate, Enabled); } diff --git a/src/Polly.Contrib.Simmy/Behavior/InjectBehaviourSyntax.cs b/src/Polly.Contrib.Simmy/Behavior/InjectBehaviourSyntax.cs index 440e981..78d2946 100644 --- a/src/Polly.Contrib.Simmy/Behavior/InjectBehaviourSyntax.cs +++ b/src/Polly.Contrib.Simmy/Behavior/InjectBehaviourSyntax.cs @@ -1,4 +1,5 @@ using System; +using System.Threading; using Polly.Contrib.Simmy.Behavior; namespace Polly.Contrib.Simmy @@ -24,9 +25,9 @@ public static InjectBehaviourPolicy InjectBehaviour( if (behaviour == null) throw new ArgumentNullException(nameof(behaviour)); if (enabled == null) throw new ArgumentNullException(nameof(enabled)); - void BehaviourLambda(Context _) => behaviour(); - double InjectionRateLambda(Context _) => injectionRate; - bool EnabledLambda(Context _) => enabled(); + void BehaviourLambda(Context _, CancellationToken __) => behaviour(); + double InjectionRateLambda(Context _, CancellationToken __) => injectionRate; + bool EnabledLambda(Context _, CancellationToken __) => enabled(); return InjectBehaviour(BehaviourLambda, InjectionRateLambda, EnabledLambda); } @@ -40,15 +41,15 @@ public static InjectBehaviourPolicy InjectBehaviour( /// Lambda to check if this policy is enabled in context free mode /// The policy instance. public static InjectBehaviourPolicy InjectBehaviour( - Action behaviour, + Action behaviour, Double injectionRate, Func enabled) { if (behaviour == null) throw new ArgumentNullException(nameof(behaviour)); if (enabled == null) throw new ArgumentNullException(nameof(enabled)); - double InjectionRateLambda(Context _) => injectionRate; - bool EnabledLambda(Context _) => enabled(); + double InjectionRateLambda(Context _, CancellationToken __) => injectionRate; + bool EnabledLambda(Context _, CancellationToken __) => enabled(); return InjectBehaviour(behaviour, InjectionRateLambda, EnabledLambda); } @@ -62,15 +63,15 @@ public static InjectBehaviourPolicy InjectBehaviour( /// Lambda to check if this policy is enabled in current context /// The policy instance. public static InjectBehaviourPolicy InjectBehaviour( - Action behaviour, + Action behaviour, Double injectionRate, - Func enabled) + Func enabled) { if (behaviour == null) throw new ArgumentNullException(nameof(behaviour)); if (enabled == null) throw new ArgumentNullException(nameof(enabled)); - double InjectionRateLambda(Context _) => injectionRate; - return InjectBehaviour(behaviour, (Func) InjectionRateLambda, enabled); + double InjectionRateLambda(Context _, CancellationToken __) => injectionRate; + return InjectBehaviour(behaviour, (Func)InjectionRateLambda, enabled); } /// @@ -82,9 +83,9 @@ public static InjectBehaviourPolicy InjectBehaviour( /// Lambda to check if this policy is enabled in current context /// The policy instance. public static InjectBehaviourPolicy InjectBehaviour( - Action behaviour, - Func injectionRate, - Func enabled) + Action behaviour, + Func injectionRate, + Func enabled) { if (behaviour == null) throw new ArgumentNullException(nameof(behaviour)); if (injectionRate == null) throw new ArgumentNullException(nameof(injectionRate)); diff --git a/src/Polly.Contrib.Simmy/Behavior/InjectBehaviourTResultSyntax.cs b/src/Polly.Contrib.Simmy/Behavior/InjectBehaviourTResultSyntax.cs index beaadd4..b272eba 100644 --- a/src/Polly.Contrib.Simmy/Behavior/InjectBehaviourTResultSyntax.cs +++ b/src/Polly.Contrib.Simmy/Behavior/InjectBehaviourTResultSyntax.cs @@ -1,4 +1,5 @@ using System; +using System.Threading; using Polly.Contrib.Simmy.Behavior; namespace Polly.Contrib.Simmy @@ -24,9 +25,9 @@ public static InjectBehaviourPolicy InjectBehaviour( if (behaviour == null) throw new ArgumentNullException(nameof(behaviour)); if (enabled == null) throw new ArgumentNullException(nameof(enabled)); - void BehaviourLambda(Context _) => behaviour(); - double InjectionRateLambda(Context _) => injectionRate; - bool EnabledLambda(Context _) => enabled(); + void BehaviourLambda(Context _, CancellationToken __) => behaviour(); + double InjectionRateLambda(Context _, CancellationToken __) => injectionRate; + bool EnabledLambda(Context _, CancellationToken __) => enabled(); return InjectBehaviour(BehaviourLambda, InjectionRateLambda, EnabledLambda); } @@ -40,15 +41,15 @@ public static InjectBehaviourPolicy InjectBehaviour( /// Lambda to check if this policy is enabled in context free mode /// The policy instance. public static InjectBehaviourPolicy InjectBehaviour( - Action behaviour, + Action behaviour, Double injectionRate, Func enabled) { if (behaviour == null) throw new ArgumentNullException(nameof(behaviour)); if (enabled == null) throw new ArgumentNullException(nameof(enabled)); - double InjectionRateLambda(Context _) => injectionRate; - bool EnabledLambda(Context _) => enabled(); + double InjectionRateLambda(Context _, CancellationToken __) => injectionRate; + bool EnabledLambda(Context _, CancellationToken __) => enabled(); return InjectBehaviour(behaviour, InjectionRateLambda, EnabledLambda); } @@ -62,14 +63,14 @@ public static InjectBehaviourPolicy InjectBehaviour( /// Lambda to check if this policy is enabled in current context /// The policy instance. public static InjectBehaviourPolicy InjectBehaviour( - Action behaviour, + Action behaviour, Double injectionRate, - Func enabled) + Func enabled) { if (behaviour == null) throw new ArgumentNullException(nameof(behaviour)); if (enabled == null) throw new ArgumentNullException(nameof(enabled)); - double InjectionRateLambda(Context _) => injectionRate; + double InjectionRateLambda(Context _, CancellationToken __) => injectionRate; return InjectBehaviour(behaviour, InjectionRateLambda, enabled); } @@ -82,9 +83,9 @@ public static InjectBehaviourPolicy InjectBehaviour( /// Lambda to check if this policy is enabled in current context /// The policy instance. public static InjectBehaviourPolicy InjectBehaviour( - Action behaviour, - Func injectionRate, - Func enabled) + Action behaviour, + Func injectionRate, + Func enabled) { if (behaviour == null) throw new ArgumentNullException(nameof(behaviour)); if (injectionRate == null) throw new ArgumentNullException(nameof(injectionRate)); diff --git a/src/Polly.Contrib.Simmy/Fault/AsyncInjectFaultSyntax.cs b/src/Polly.Contrib.Simmy/Fault/AsyncInjectFaultSyntax.cs index 27e45f9..d4e044e 100644 --- a/src/Polly.Contrib.Simmy/Fault/AsyncInjectFaultSyntax.cs +++ b/src/Polly.Contrib.Simmy/Fault/AsyncInjectFaultSyntax.cs @@ -23,8 +23,8 @@ public static AsyncInjectOutcomePolicy InjectFaultAsync( if (enabled == null) throw new ArgumentNullException(nameof(enabled)); Task FaultLambda(Context _, CancellationToken __) => Task.FromResult(fault); - Task InjectionRateLambda(Context _) => Task.FromResult(injectionRate); - Task EnabledLambda(Context _) => Task.FromResult(enabled()); + Task InjectionRateLambda(Context _, CancellationToken __) => Task.FromResult(injectionRate); + Task EnabledLambda(Context _, CancellationToken __) => Task.FromResult(enabled()); return new AsyncInjectOutcomePolicy(FaultLambda, InjectionRateLambda, EnabledLambda); } @@ -40,12 +40,12 @@ public static AsyncInjectOutcomePolicy InjectFaultAsync( public static AsyncInjectOutcomePolicy InjectFaultAsync( Exception fault, Double injectionRate, - Func> enabled) + Func> enabled) { if (enabled == null) throw new ArgumentNullException(nameof(enabled)); Task FaultLambda(Context _, CancellationToken __) => Task.FromResult(fault); - Task InjectionRateLambda(Context _) => Task.FromResult(injectionRate); + Task InjectionRateLambda(Context _, CancellationToken __) => Task.FromResult(injectionRate); return new AsyncInjectOutcomePolicy(FaultLambda, InjectionRateLambda, enabled); } @@ -60,8 +60,8 @@ public static AsyncInjectOutcomePolicy InjectFaultAsync( /// The policy instance. public static AsyncInjectOutcomePolicy InjectFaultAsync( Func> faultProvider, - Func> injectionRate, - Func> enabled) + Func> injectionRate, + Func> enabled) { if (faultProvider == null) throw new ArgumentNullException(nameof(faultProvider)); if (injectionRate == null) throw new ArgumentNullException(nameof(injectionRate)); @@ -90,8 +90,8 @@ public static AsyncInjectOutcomePolicy InjectFaultAsync( if (enabled == null) throw new ArgumentNullException(nameof(enabled)); Task FaultLambda(Context _, CancellationToken __) => Task.FromResult(fault); - Task InjectionRateLambda(Context _) => Task.FromResult(injectionRate); - Task EnabledLambda(Context _) => Task.FromResult(enabled()); + Task InjectionRateLambda(Context _, CancellationToken __) => Task.FromResult(injectionRate); + Task EnabledLambda(Context _, CancellationToken __) => Task.FromResult(enabled()); return new AsyncInjectOutcomePolicy((Func>)FaultLambda, InjectionRateLambda, EnabledLambda); } @@ -107,12 +107,12 @@ public static AsyncInjectOutcomePolicy InjectFaultAsync( public static AsyncInjectOutcomePolicy InjectFaultAsync( Exception fault, Double injectionRate, - Func> enabled) + Func> enabled) { if (enabled == null) throw new ArgumentNullException(nameof(enabled)); Task FaultLambda(Context _, CancellationToken __) => Task.FromResult(fault); - Task InjectionRateLambda(Context _) => Task.FromResult(injectionRate); + Task InjectionRateLambda(Context _, CancellationToken __) => Task.FromResult(injectionRate); return new AsyncInjectOutcomePolicy((Func>)FaultLambda, InjectionRateLambda, enabled); } @@ -127,8 +127,8 @@ public static AsyncInjectOutcomePolicy InjectFaultAsync( /// The policy instance. public static AsyncInjectOutcomePolicy InjectFaultAsync( Func> faultProvider, - Func> injectionRate, - Func> enabled) + Func> injectionRate, + Func> enabled) { if (faultProvider == null) throw new ArgumentNullException(nameof(faultProvider)); if (injectionRate == null) throw new ArgumentNullException(nameof(injectionRate)); @@ -156,8 +156,8 @@ public static AsyncInjectOutcomePolicy InjectFaultAsync( if (enabled == null) throw new ArgumentNullException(nameof(enabled)); Task FaultLambda(Context _, CancellationToken __) => Task.FromResult(fault); - Task InjectionRateLambda(Context _) => Task.FromResult(injectionRate); - Task EnabledLambda(Context _) + Task InjectionRateLambda(Context _, CancellationToken __) => Task.FromResult(injectionRate); + Task EnabledLambda(Context _, CancellationToken __) { return Task.FromResult(enabled()); } @@ -176,12 +176,12 @@ Task EnabledLambda(Context _) public static AsyncInjectOutcomePolicy InjectFaultAsync( TResult fault, Double injectionRate, - Func> enabled) + Func> enabled) { if (enabled == null) throw new ArgumentNullException(nameof(enabled)); Task FaultLambda(Context _, CancellationToken __) => Task.FromResult(fault); - Task InjectionRateLambda(Context _) => Task.FromResult(injectionRate); + Task InjectionRateLambda(Context _, CancellationToken __) => Task.FromResult(injectionRate); return new AsyncInjectOutcomePolicy((Func>)FaultLambda, InjectionRateLambda, enabled); } @@ -196,8 +196,8 @@ public static AsyncInjectOutcomePolicy InjectFaultAsync( /// The policy instance. public static AsyncInjectOutcomePolicy InjectFaultAsync( Func> fault, - Func> injectionRate, - Func> enabled) + Func> injectionRate, + Func> enabled) { if (fault == null) throw new ArgumentNullException(nameof(fault)); if (injectionRate == null) throw new ArgumentNullException(nameof(injectionRate)); diff --git a/src/Polly.Contrib.Simmy/Fault/AsyncInjectOutcomePolicy.cs b/src/Polly.Contrib.Simmy/Fault/AsyncInjectOutcomePolicy.cs index 2174f37..8952f8e 100644 --- a/src/Polly.Contrib.Simmy/Fault/AsyncInjectOutcomePolicy.cs +++ b/src/Polly.Contrib.Simmy/Fault/AsyncInjectOutcomePolicy.cs @@ -12,7 +12,7 @@ public class AsyncInjectOutcomePolicy : AsyncMonkeyPolicy { private readonly Func> _faultProvider; - internal AsyncInjectOutcomePolicy(Func> faultProvider, Func> injectionRate, Func> enabled) + internal AsyncInjectOutcomePolicy(Func> faultProvider, Func> injectionRate, Func> enabled) : base(injectionRate, enabled) { _faultProvider = faultProvider ?? throw new ArgumentNullException(nameof(faultProvider)); @@ -41,13 +41,13 @@ public class AsyncInjectOutcomePolicy : AsyncMonkeyPolicy private readonly Func> _faultProvider; private readonly Func> _resultProvider; - internal AsyncInjectOutcomePolicy(Func> faultProvider, Func> injectionRate, Func> enabled) + internal AsyncInjectOutcomePolicy(Func> faultProvider, Func> injectionRate, Func> enabled) : base(injectionRate, enabled) { _faultProvider = faultProvider ?? throw new ArgumentNullException(nameof(faultProvider)); } - internal AsyncInjectOutcomePolicy(Func> resultProvider, Func> injectionRate, Func> enabled) + internal AsyncInjectOutcomePolicy(Func> resultProvider, Func> injectionRate, Func> enabled) : base(injectionRate, enabled) { _resultProvider = resultProvider ?? throw new ArgumentNullException(nameof(resultProvider)); diff --git a/src/Polly.Contrib.Simmy/Fault/InjectFaultSyntax.cs b/src/Polly.Contrib.Simmy/Fault/InjectFaultSyntax.cs index 82c89bc..8000f15 100644 --- a/src/Polly.Contrib.Simmy/Fault/InjectFaultSyntax.cs +++ b/src/Polly.Contrib.Simmy/Fault/InjectFaultSyntax.cs @@ -1,4 +1,5 @@ using System; +using System.Threading; using Polly.Contrib.Simmy.Fault; namespace Polly.Contrib.Simmy @@ -20,9 +21,9 @@ public static InjectOutcomePolicy InjectFault( { if (enabled == null) throw new ArgumentNullException(nameof(enabled)); - Exception FaultLambda(Context _) => fault; - double InjectionRateLambda(Context _) => injectionRate; - bool EnabledLambda(Context _) => enabled(); + Exception FaultLambda(Context _, CancellationToken __) => fault; + double InjectionRateLambda(Context _, CancellationToken __) => injectionRate; + bool EnabledLambda(Context _, CancellationToken __) => enabled(); return new InjectOutcomePolicy(FaultLambda, InjectionRateLambda, EnabledLambda); } @@ -38,12 +39,12 @@ public static InjectOutcomePolicy InjectFault( public static InjectOutcomePolicy InjectFault( Exception fault, Double injectionRate, - Func enabled) + Func enabled) { if (enabled == null) throw new ArgumentNullException(nameof(enabled)); - Exception FaultLambda(Context _) => fault; - double InjectionRateLambda(Context _) => injectionRate; + Exception FaultLambda(Context _, CancellationToken __) => fault; + double InjectionRateLambda(Context _, CancellationToken __) => injectionRate; return new InjectOutcomePolicy(FaultLambda, InjectionRateLambda, enabled); } @@ -57,9 +58,9 @@ public static InjectOutcomePolicy InjectFault( /// Lambda to check if this policy is enabled in current context /// The policy instance. public static InjectOutcomePolicy InjectFault( - Func faultProvider, - Func injectionRate, - Func enabled) + Func faultProvider, + Func injectionRate, + Func enabled) { if (faultProvider == null) throw new ArgumentNullException(nameof(faultProvider)); if (injectionRate == null) throw new ArgumentNullException(nameof(injectionRate)); @@ -87,11 +88,11 @@ public static InjectOutcomePolicy InjectFault( { if (enabled == null) throw new ArgumentNullException(nameof(enabled)); - Exception FaultLambda(Context _) => fault; - double InjectionRateLambda(Context _) => injectionRate; - bool EnabledLambda(Context _) => enabled(); + Exception FaultLambda(Context _, CancellationToken __) => fault; + double InjectionRateLambda(Context _, CancellationToken __) => injectionRate; + bool EnabledLambda(Context _, CancellationToken __) => enabled(); - return new InjectOutcomePolicy((Func)FaultLambda, InjectionRateLambda, EnabledLambda); + return new InjectOutcomePolicy((Func)FaultLambda, InjectionRateLambda, EnabledLambda); } /// @@ -105,14 +106,14 @@ public static InjectOutcomePolicy InjectFault( public static InjectOutcomePolicy InjectFault( Exception fault, Double injectionRate, - Func enabled) + Func enabled) { if (enabled == null) throw new ArgumentNullException(nameof(enabled)); - Exception FaultLambda(Context _) => fault; - double InjectionRateLambda(Context _) => injectionRate; + Exception FaultLambda(Context _, CancellationToken __) => fault; + double InjectionRateLambda(Context _, CancellationToken __) => injectionRate; - return new InjectOutcomePolicy((Func)FaultLambda, InjectionRateLambda, enabled); + return new InjectOutcomePolicy((Func)FaultLambda, InjectionRateLambda, enabled); } /// @@ -124,9 +125,9 @@ public static InjectOutcomePolicy InjectFault( /// Lambda to check if this policy is enabled in current context /// The policy instance. public static InjectOutcomePolicy InjectFault( - Func faultProvider, - Func injectionRate, - Func enabled) + Func faultProvider, + Func injectionRate, + Func enabled) { if (faultProvider == null) throw new ArgumentNullException(nameof(faultProvider)); if (injectionRate == null) throw new ArgumentNullException(nameof(injectionRate)); @@ -154,11 +155,11 @@ public static InjectOutcomePolicy InjectFault( { if (enabled == null) throw new ArgumentNullException(nameof(enabled)); - TResult FaultLambda(Context _) => fault; - double InjectionRateLambda(Context _) => injectionRate; - bool EnabledLambda(Context _) => enabled(); + TResult FaultLambda(Context _, CancellationToken __) => fault; + double InjectionRateLambda(Context _, CancellationToken __) => injectionRate; + bool EnabledLambda(Context _, CancellationToken __) => enabled(); - return new InjectOutcomePolicy((Func)FaultLambda, InjectionRateLambda, EnabledLambda); + return new InjectOutcomePolicy((Func)FaultLambda, InjectionRateLambda, EnabledLambda); } /// @@ -172,14 +173,14 @@ public static InjectOutcomePolicy InjectFault( public static InjectOutcomePolicy InjectFault( TResult fault, Double injectionRate, - Func enabled) + Func enabled) { if (enabled == null) throw new ArgumentNullException(nameof(enabled)); - TResult FaultLambda(Context _) => fault; - double InjectionRateLambda(Context _) => injectionRate; + TResult FaultLambda(Context _, CancellationToken __) => fault; + double InjectionRateLambda(Context _, CancellationToken __) => injectionRate; - return new InjectOutcomePolicy((Func)FaultLambda, InjectionRateLambda, enabled); + return new InjectOutcomePolicy((Func)FaultLambda, InjectionRateLambda, enabled); } /// @@ -191,9 +192,9 @@ public static InjectOutcomePolicy InjectFault( /// Lambda to check if this policy is enabled in current context /// The policy instance. public static InjectOutcomePolicy InjectFault( - Func faultProvider, - Func injectionRate, - Func enabled) + Func faultProvider, + Func injectionRate, + Func enabled) { if (faultProvider == null) throw new ArgumentNullException(nameof(faultProvider)); if (injectionRate == null) throw new ArgumentNullException(nameof(injectionRate)); diff --git a/src/Polly.Contrib.Simmy/Fault/InjectOutcomePolicy.cs b/src/Polly.Contrib.Simmy/Fault/InjectOutcomePolicy.cs index 51bae42..c048795 100644 --- a/src/Polly.Contrib.Simmy/Fault/InjectOutcomePolicy.cs +++ b/src/Polly.Contrib.Simmy/Fault/InjectOutcomePolicy.cs @@ -9,9 +9,9 @@ namespace Polly.Contrib.Simmy.Fault /// public class InjectOutcomePolicy : Simmy.MonkeyPolicy { - private readonly Func _faultProvider; + private readonly Func _faultProvider; - internal InjectOutcomePolicy(Func faultProvider, Func injectionRate, Func enabled) + internal InjectOutcomePolicy(Func faultProvider, Func injectionRate, Func enabled) : base(injectionRate, enabled) { _faultProvider = faultProvider ?? throw new ArgumentNullException(nameof(faultProvider)); @@ -35,16 +35,16 @@ protected override TResult Implementation(Func public class InjectOutcomePolicy : MonkeyPolicy { - private readonly Func _faultProvider; - private readonly Func _resultProvider; + private readonly Func _faultProvider; + private readonly Func _resultProvider; - internal InjectOutcomePolicy(Func faultProvider, Func injectionRate, Func enabled) + internal InjectOutcomePolicy(Func faultProvider, Func injectionRate, Func enabled) : base(injectionRate, enabled) { _faultProvider = faultProvider ?? throw new ArgumentNullException(nameof(faultProvider)); } - internal InjectOutcomePolicy(Func resultProvider, Func injectionRate, Func enabled) + internal InjectOutcomePolicy(Func resultProvider, Func injectionRate, Func enabled) : base(injectionRate, enabled) { _resultProvider = resultProvider ?? throw new ArgumentNullException(nameof(resultProvider)); diff --git a/src/Polly.Contrib.Simmy/Latency/AsyncInjectLatencyPolicy.cs b/src/Polly.Contrib.Simmy/Latency/AsyncInjectLatencyPolicy.cs index d01b8d1..eeba3d1 100644 --- a/src/Polly.Contrib.Simmy/Latency/AsyncInjectLatencyPolicy.cs +++ b/src/Polly.Contrib.Simmy/Latency/AsyncInjectLatencyPolicy.cs @@ -14,8 +14,8 @@ public class AsyncInjectLatencyPolicy : AsyncMonkeyPolicy internal AsyncInjectLatencyPolicy( Func> latencyProvider, - Func> injectionRate, - Func> enabled) + Func> injectionRate, + Func> enabled) : base(injectionRate, enabled) { _latencyProvider = latencyProvider ?? throw new ArgumentNullException(nameof(latencyProvider)); @@ -32,10 +32,17 @@ protected override Task ImplementationAsync( action, context, cancellationToken, - async (ctx, ct) => await SystemClock.SleepAsync( - await _latencyProvider(ctx, cancellationToken).ConfigureAwait(continueOnCapturedContext), - InjectLatencyPolicy.DefaultCancellationForInjectedLatency) - .ConfigureAwait(continueOnCapturedContext), + async (ctx, ct) => + { + var latency = await _latencyProvider(ctx, cancellationToken).ConfigureAwait(continueOnCapturedContext); + + // to prevent inject latency if token was signaled on latency configuration delegate. + cancellationToken.ThrowIfCancellationRequested(); + await SystemClock.SleepAsync( + latency, + cancellationToken) + .ConfigureAwait(continueOnCapturedContext); + }, InjectionRate, Enabled, continueOnCapturedContext); @@ -51,8 +58,8 @@ public class AsyncInjectLatencyPolicy : AsyncMonkeyPolicy internal AsyncInjectLatencyPolicy( Func> latencyProvider, - Func> injectionRate, - Func> enabled) + Func> injectionRate, + Func> enabled) : base(injectionRate, enabled) { _latencyProvider = latencyProvider ?? throw new ArgumentNullException(nameof(latencyProvider)); @@ -69,10 +76,17 @@ protected override Task ImplementationAsync( action, context, cancellationToken, - async (ctx, ct) => await SystemClock.SleepAsync( - await _latencyProvider(ctx, cancellationToken).ConfigureAwait(continueOnCapturedContext), - InjectLatencyPolicy.DefaultCancellationForInjectedLatency) - .ConfigureAwait(continueOnCapturedContext), + async (ctx, ct) => + { + var latency = await _latencyProvider(ctx, cancellationToken).ConfigureAwait(continueOnCapturedContext); + + // to prevent inject latency if token was signaled on latency configuration delegate. + cancellationToken.ThrowIfCancellationRequested(); + await SystemClock.SleepAsync( + latency, + cancellationToken) + .ConfigureAwait(continueOnCapturedContext); + }, InjectionRate, Enabled, continueOnCapturedContext); diff --git a/src/Polly.Contrib.Simmy/Latency/AsyncInjectLatencySyntax.cs b/src/Polly.Contrib.Simmy/Latency/AsyncInjectLatencySyntax.cs index f00eaf2..fc7600f 100644 --- a/src/Polly.Contrib.Simmy/Latency/AsyncInjectLatencySyntax.cs +++ b/src/Polly.Contrib.Simmy/Latency/AsyncInjectLatencySyntax.cs @@ -23,8 +23,8 @@ public static AsyncInjectLatencyPolicy InjectLatencyAsync( if (enabled == null) throw new ArgumentNullException(nameof(enabled)); Task LatencyProvider(Context _, CancellationToken __) => Task.FromResult(latency); - Task InjectionRateLambda(Context _) => Task.FromResult(injectionRate); - Task EnabledLambda(Context _) => Task.FromResult(enabled()); + Task InjectionRateLambda(Context _, CancellationToken __) => Task.FromResult(injectionRate); + Task EnabledLambda(Context _, CancellationToken __) => Task.FromResult(enabled()); return new AsyncInjectLatencyPolicy(LatencyProvider, InjectionRateLambda, EnabledLambda); } @@ -40,12 +40,12 @@ public static AsyncInjectLatencyPolicy InjectLatencyAsync( public static AsyncInjectLatencyPolicy InjectLatencyAsync( TimeSpan latency, Double injectionRate, - Func> enabled) + Func> enabled) { if (enabled == null) throw new ArgumentNullException(nameof(enabled)); Task LatencyProvider(Context _, CancellationToken __) => Task.FromResult(latency); - Task InjectionRateLambda(Context _) => Task.FromResult(injectionRate); + Task InjectionRateLambda(Context _, CancellationToken __) => Task.FromResult(injectionRate); return new AsyncInjectLatencyPolicy(LatencyProvider, InjectionRateLambda, enabled); } @@ -60,8 +60,8 @@ public static AsyncInjectLatencyPolicy InjectLatencyAsync( /// The policy instance. public static AsyncInjectLatencyPolicy InjectLatencyAsync( TimeSpan latency, - Func> injectionRate, - Func> enabled) + Func> injectionRate, + Func> enabled) { if (injectionRate == null) throw new ArgumentNullException(nameof(injectionRate)); if (enabled == null) throw new ArgumentNullException(nameof(enabled)); @@ -80,8 +80,8 @@ public static AsyncInjectLatencyPolicy InjectLatencyAsync( /// The policy instance. public static AsyncInjectLatencyPolicy InjectLatencyAsync( Func> latency, - Func> injectionRate, - Func> enabled) + Func> injectionRate, + Func> enabled) { if (latency == null) throw new ArgumentNullException(nameof(latency)); if (injectionRate == null) throw new ArgumentNullException(nameof(injectionRate)); @@ -109,8 +109,8 @@ public static AsyncInjectLatencyPolicy InjectLatencyAsync( if (enabled == null) throw new ArgumentNullException(nameof(enabled)); Task LatencyProvider(Context _, CancellationToken __) => Task.FromResult(latency); - Task InjectionRateLambda(Context _) => Task.FromResult(injectionRate); - Task EnabledLambda(Context _) => Task.FromResult(enabled()); + Task InjectionRateLambda(Context _, CancellationToken __) => Task.FromResult(injectionRate); + Task EnabledLambda(Context _, CancellationToken __) => Task.FromResult(enabled()); return new AsyncInjectLatencyPolicy(LatencyProvider, InjectionRateLambda, EnabledLambda); } @@ -126,12 +126,12 @@ public static AsyncInjectLatencyPolicy InjectLatencyAsync( public static AsyncInjectLatencyPolicy InjectLatencyAsync( TimeSpan latency, Double injectionRate, - Func> enabled) + Func> enabled) { if (enabled == null) throw new ArgumentNullException(nameof(enabled)); Task LatencyProvider(Context _, CancellationToken __) => Task.FromResult(latency); - Task InjectionRateLambda(Context _) => Task.FromResult(injectionRate); + Task InjectionRateLambda(Context _, CancellationToken __) => Task.FromResult(injectionRate); return new AsyncInjectLatencyPolicy(LatencyProvider, InjectionRateLambda, enabled); } @@ -146,8 +146,8 @@ public static AsyncInjectLatencyPolicy InjectLatencyAsync( /// The policy instance. public static AsyncInjectLatencyPolicy InjectLatencyAsync( TimeSpan latency, - Func> injectionRate, - Func> enabled) + Func> injectionRate, + Func> enabled) { if (injectionRate == null) throw new ArgumentNullException(nameof(injectionRate)); if (enabled == null) throw new ArgumentNullException(nameof(enabled)); @@ -166,8 +166,8 @@ public static AsyncInjectLatencyPolicy InjectLatencyAsync( /// The policy instance. public static AsyncInjectLatencyPolicy InjectLatencyAsync( Func> latency, - Func> injectionRate, - Func> enabled) + Func> injectionRate, + Func> enabled) { if (latency == null) throw new ArgumentNullException(nameof(latency)); if (injectionRate == null) throw new ArgumentNullException(nameof(injectionRate)); diff --git a/src/Polly.Contrib.Simmy/Latency/InjectLatencyPolicy.cs b/src/Polly.Contrib.Simmy/Latency/InjectLatencyPolicy.cs index 67e4d0e..5620d75 100644 --- a/src/Polly.Contrib.Simmy/Latency/InjectLatencyPolicy.cs +++ b/src/Polly.Contrib.Simmy/Latency/InjectLatencyPolicy.cs @@ -9,14 +9,12 @@ namespace Polly.Contrib.Simmy.Latency /// public class InjectLatencyPolicy : MonkeyPolicy { - internal static readonly CancellationToken DefaultCancellationForInjectedLatency = CancellationToken.None; // It is intended that injected latency is not susceptible to cancellation. (TO CONFIRM) - - private readonly Func _latencyProvider; + private readonly Func _latencyProvider; internal InjectLatencyPolicy( - Func latencyProvider, - Func injectionRate, - Func enabled) + Func latencyProvider, + Func injectionRate, + Func enabled) : base(injectionRate, enabled) { _latencyProvider = latencyProvider ?? throw new ArgumentNullException(nameof(latencyProvider)); @@ -29,7 +27,14 @@ protected override TResult Implementation(Func SystemClock.Sleep(_latencyProvider(ctx), DefaultCancellationForInjectedLatency), + (ctx, ct) => + { + var latency = _latencyProvider(ctx, ct); + + // to prevent inject latency if token was signaled on latency configuration delegate. + cancellationToken.ThrowIfCancellationRequested(); + SystemClock.Sleep(latency, cancellationToken); + }, InjectionRate, Enabled); } @@ -41,12 +46,12 @@ protected override TResult Implementation(FuncThe type of return values this policy will handle. public class InjectLatencyPolicy : MonkeyPolicy { - private readonly Func _latencyProvider; + private readonly Func _latencyProvider; internal InjectLatencyPolicy( - Func latencyProvider, - Func injectionRate, - Func enabled) + Func latencyProvider, + Func injectionRate, + Func enabled) : base(injectionRate, enabled) { _latencyProvider = latencyProvider ?? throw new ArgumentNullException(nameof(latencyProvider)); @@ -59,10 +64,16 @@ protected override TResult Implementation(Func SystemClock.Sleep(_latencyProvider(ctx), InjectLatencyPolicy.DefaultCancellationForInjectedLatency), + (ctx, ct) => + { + var latency = _latencyProvider(ctx, ct); + + // to prevent inject latency if token was signaled on latency configuration delegate. + cancellationToken.ThrowIfCancellationRequested(); + SystemClock.Sleep(latency, cancellationToken); + }, InjectionRate, Enabled); } - } } diff --git a/src/Polly.Contrib.Simmy/Latency/InjectLatencySyntax.cs b/src/Polly.Contrib.Simmy/Latency/InjectLatencySyntax.cs index bafd6c1..1d574bb 100644 --- a/src/Polly.Contrib.Simmy/Latency/InjectLatencySyntax.cs +++ b/src/Polly.Contrib.Simmy/Latency/InjectLatencySyntax.cs @@ -1,4 +1,5 @@ using System; +using System.Threading; using Polly.Contrib.Simmy.Latency; namespace Polly.Contrib.Simmy @@ -20,7 +21,7 @@ public static InjectLatencyPolicy InjectLatency( { if (enabled == null) throw new ArgumentNullException(nameof(enabled)); - return new InjectLatencyPolicy(_ => latency, _ => injectionRate, _ => enabled()); + return new InjectLatencyPolicy((_, __) => latency, (_, __) => injectionRate, (_, __) => enabled()); } /// @@ -34,11 +35,11 @@ public static InjectLatencyPolicy InjectLatency( public static InjectLatencyPolicy InjectLatency( TimeSpan latency, Double injectionRate, - Func enabled) + Func enabled) { if (enabled == null) throw new ArgumentNullException(nameof(enabled)); - return new InjectLatencyPolicy(_ => latency, _ => injectionRate, enabled); + return new InjectLatencyPolicy((_, __) => latency, (_, __) => injectionRate, enabled); } /// @@ -51,13 +52,13 @@ public static InjectLatencyPolicy InjectLatency( /// The policy instance. public static InjectLatencyPolicy InjectLatency( TimeSpan latency, - Func injectionRate, - Func enabled) + Func injectionRate, + Func enabled) { if (injectionRate == null) throw new ArgumentNullException(nameof(injectionRate)); if (enabled == null) throw new ArgumentNullException(nameof(enabled)); - return new InjectLatencyPolicy(_ => latency, injectionRate, enabled); + return new InjectLatencyPolicy((_, __) => latency, injectionRate, enabled); } /// @@ -69,14 +70,14 @@ public static InjectLatencyPolicy InjectLatency( /// Lambda to check if this policy is enabled in current context /// The policy instance. public static InjectLatencyPolicy InjectLatency( - Func latency, - Func injectionRate, - Func enabled) + Func latency, + Func injectionRate, + Func enabled) { if (latency == null) throw new ArgumentNullException(nameof(latency)); if (injectionRate == null) throw new ArgumentNullException(nameof(injectionRate)); if (enabled == null) throw new ArgumentNullException(nameof(enabled)); - + return new InjectLatencyPolicy(latency, injectionRate, enabled); } } @@ -98,7 +99,7 @@ public static InjectLatencyPolicy InjectLatency( { if (enabled == null) throw new ArgumentNullException(nameof(enabled)); - return new InjectLatencyPolicy(_ => latency, _ => injectionRate, _ => enabled()); + return new InjectLatencyPolicy((_, __) => latency, (_, __) => injectionRate, (_, __) => enabled()); } /// @@ -112,11 +113,11 @@ public static InjectLatencyPolicy InjectLatency( public static InjectLatencyPolicy InjectLatency( TimeSpan latency, Double injectionRate, - Func enabled) + Func enabled) { if (enabled == null) throw new ArgumentNullException(nameof(enabled)); - return new InjectLatencyPolicy(_ => latency, _ => injectionRate, enabled); + return new InjectLatencyPolicy((_, __) => latency, (_, __) => injectionRate, enabled); } /// @@ -129,13 +130,13 @@ public static InjectLatencyPolicy InjectLatency( /// The policy instance. public static InjectLatencyPolicy InjectLatency( TimeSpan latency, - Func injectionRate, - Func enabled) + Func injectionRate, + Func enabled) { if (injectionRate == null) throw new ArgumentNullException(nameof(injectionRate)); if (enabled == null) throw new ArgumentNullException(nameof(enabled)); - return new InjectLatencyPolicy(_ => latency, injectionRate, enabled); + return new InjectLatencyPolicy((_, __) => latency, injectionRate, enabled); } /// @@ -147,9 +148,9 @@ public static InjectLatencyPolicy InjectLatency( /// Lambda to check if this policy is enabled in current context /// The policy instance. public static InjectLatencyPolicy InjectLatency( - Func latency, - Func injectionRate, - Func enabled) + Func latency, + Func injectionRate, + Func enabled) { if (latency == null) throw new ArgumentNullException(nameof(latency)); if (injectionRate == null) throw new ArgumentNullException(nameof(injectionRate)); diff --git a/src/Polly.Contrib.Simmy/MonkeyEngine.cs b/src/Polly.Contrib.Simmy/MonkeyEngine.cs index d9d164c..d631897 100644 --- a/src/Polly.Contrib.Simmy/MonkeyEngine.cs +++ b/src/Polly.Contrib.Simmy/MonkeyEngine.cs @@ -6,18 +6,29 @@ namespace Polly.Contrib.Simmy { internal static class MonkeyEngine { - private static bool ShouldInject(Context context, Func injectionRate, Func enabled) + private static bool ShouldInject(Context context, CancellationToken cancellationToken, Func injectionRate, Func enabled) { - if (!enabled(context)) + // to prevent execute config delegates if token is signaled before to start. + cancellationToken.ThrowIfCancellationRequested(); + + if (!enabled(context, cancellationToken)) { return false; } - double injectionThreshold = injectionRate(context); + // to prevent execute injectionRate config delegate if token is signaled on enable configuration delegate. + cancellationToken.ThrowIfCancellationRequested(); + + double injectionThreshold = injectionRate(context, cancellationToken); + + // to prevent execute further config delegates if token is signaled on injectionRate configuration delegate. + cancellationToken.ThrowIfCancellationRequested(); + if (injectionThreshold < 0) { throw new ArgumentOutOfRangeException(nameof(injectionThreshold), "Injection rate/threshold in Monkey policies should always be a double between [0, 1]; never a negative number."); } + if (injectionThreshold > 1) { throw new ArgumentOutOfRangeException(nameof(injectionThreshold), "Injection rate/threshold in Monkey policies should always be a double between [0, 1]; never a number greater than 1."); @@ -31,14 +42,16 @@ internal static TResult InjectBehaviourImplementation( Context context, CancellationToken cancellationToken, Action injectedBehaviour, - Func injectionRate, - Func enabled) + Func injectionRate, + Func enabled) { - if (ShouldInject(context, injectionRate, enabled)) + if (ShouldInject(context, cancellationToken, injectionRate, enabled)) { injectedBehaviour(context, cancellationToken); } + // to prevent execute the user's action if token is signaled on injectedBehaviour delegate. + cancellationToken.ThrowIfCancellationRequested(); return action(context, cancellationToken); } @@ -46,18 +59,22 @@ internal static TResult InjectExceptionImplementation( Func action, Context context, CancellationToken cancellationToken, - Func injectedException, - Func injectionRate, - Func enabled) + Func injectedException, + Func injectionRate, + Func enabled) { - if (ShouldInject(context, injectionRate, enabled)) + if (ShouldInject(context, cancellationToken, injectionRate, enabled)) { - Exception exception = injectedException(context); + Exception exception = injectedException(context, cancellationToken); + + // to prevent throws the exception if token is signaled on injectedException configuration delegate. + cancellationToken.ThrowIfCancellationRequested(); + if (exception != null) { - throw exception; + throw exception; } - + } return action(context, cancellationToken); @@ -67,15 +84,17 @@ internal static TResult InjectResultImplementation( Func action, Context context, CancellationToken cancellationToken, - Func injectedResult, - Func injectionRate, - Func enabled) + Func injectedResult, + Func injectionRate, + Func enabled) { - if (ShouldInject(context, injectionRate, enabled)) + if (ShouldInject(context, cancellationToken, injectionRate, enabled)) { - return injectedResult(context); + return injectedResult(context, cancellationToken); } + // to prevent inject the result if token is signaled on injectedResult delegate. + cancellationToken.ThrowIfCancellationRequested(); return action(context, cancellationToken); } } diff --git a/src/Polly.Contrib.Simmy/MonkeyPolicy.cs b/src/Polly.Contrib.Simmy/MonkeyPolicy.cs index 770ea4c..b81bce3 100644 --- a/src/Polly.Contrib.Simmy/MonkeyPolicy.cs +++ b/src/Polly.Contrib.Simmy/MonkeyPolicy.cs @@ -1,4 +1,5 @@ using System; +using System.Threading; namespace Polly.Contrib.Simmy { @@ -7,11 +8,11 @@ namespace Polly.Contrib.Simmy /// public abstract partial class MonkeyPolicy : Policy, IMonkeyPolicy { - internal Func InjectionRate { get; } + internal Func InjectionRate { get; } - internal Func Enabled { get; } + internal Func Enabled { get; } - internal MonkeyPolicy(Func injectionRate, Func enabled) + internal MonkeyPolicy(Func injectionRate, Func enabled) { InjectionRate = injectionRate ?? throw new ArgumentNullException(nameof(injectionRate)); Enabled = enabled ?? throw new ArgumentNullException(nameof(enabled)); @@ -24,11 +25,11 @@ internal MonkeyPolicy(Func injectionRate, Func e /// The type of return values this policy will handle. public abstract class MonkeyPolicy : Policy, IMonkeyPolicy { - internal Func InjectionRate { get; } + internal Func InjectionRate { get; } - internal Func Enabled { get; } + internal Func Enabled { get; } - internal MonkeyPolicy(Func injectionRate, Func enabled) + internal MonkeyPolicy(Func injectionRate, Func enabled) { InjectionRate = injectionRate ?? throw new ArgumentNullException(nameof(injectionRate)); Enabled = enabled ?? throw new ArgumentNullException(nameof(enabled));