From 4b6992e8e5ded60f10b817069d09ff94bf0e8060 Mon Sep 17 00:00:00 2001 From: Geovanny Alzate Sandoval Date: Wed, 10 Jul 2019 21:14:15 -0500 Subject: [PATCH 01/10] making async configuraion delegates cancellable (initial refactor commit) --- .../Fault/InjectFaultAsyncSpecs.cs | 22 ++++++------ .../Fault/InjectFaultTResultAsyncSpecs.cs | 26 +++++++------- .../Latency/InjectLatencyAsyncSpecs.cs | 26 +++++++------- .../InjectLatencyTResultAsyncSpecs .cs | 26 +++++++------- src/Polly.Contrib.Simmy/AsyncMonkeyEngine.cs | 29 ++++++++------- src/Polly.Contrib.Simmy/AsyncMonkeyPolicy.cs | 13 +++---- .../Behavior/AsyncInjectBehaviourPolicy.cs | 4 +-- .../Behavior/AsyncInjectBehaviourSyntax.cs | 16 ++++----- .../AsyncInjectBehaviourTResultSyntax.cs | 16 ++++----- .../Fault/AsyncInjectFaultSyntax.cs | 36 +++++++++---------- .../Fault/AsyncInjectOutcomePolicy.cs | 6 ++-- .../Latency/AsyncInjectLatencyPolicy.cs | 8 ++--- .../Latency/AsyncInjectLatencySyntax.cs | 32 ++++++++--------- 13 files changed, 133 insertions(+), 127 deletions(-) diff --git a/src/Polly.Contrib.Simmy.Specs/Fault/InjectFaultAsyncSpecs.cs b/src/Polly.Contrib.Simmy.Specs/Fault/InjectFaultAsyncSpecs.cs index a6669d9..12f8da8 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"]); }; diff --git a/src/Polly.Contrib.Simmy.Specs/Fault/InjectFaultTResultAsyncSpecs.cs b/src/Polly.Contrib.Simmy.Specs/Fault/InjectFaultTResultAsyncSpecs.cs index f73d968..cbd90d2 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"]); }; @@ -249,7 +249,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 +273,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 +302,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 +313,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 +342,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 +353,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); }; diff --git a/src/Polly.Contrib.Simmy.Specs/Latency/InjectLatencyAsyncSpecs.cs b/src/Polly.Contrib.Simmy.Specs/Latency/InjectLatencyAsyncSpecs.cs index bdae871..e9f994d 100644 --- a/src/Polly.Contrib.Simmy.Specs/Latency/InjectLatencyAsyncSpecs.cs +++ b/src/Polly.Contrib.Simmy.Specs/Latency/InjectLatencyAsyncSpecs.cs @@ -94,7 +94,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 +116,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 +138,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,12 +161,12 @@ 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) { @@ -194,12 +194,12 @@ 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) { @@ -238,12 +238,12 @@ 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) { @@ -282,12 +282,12 @@ 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) { @@ -326,12 +326,12 @@ 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) { diff --git a/src/Polly.Contrib.Simmy.Specs/Latency/InjectLatencyTResultAsyncSpecs .cs b/src/Polly.Contrib.Simmy.Specs/Latency/InjectLatencyTResultAsyncSpecs .cs index 1cff874..0953735 100644 --- a/src/Polly.Contrib.Simmy.Specs/Latency/InjectLatencyTResultAsyncSpecs .cs +++ b/src/Polly.Contrib.Simmy.Specs/Latency/InjectLatencyTResultAsyncSpecs .cs @@ -99,7 +99,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 +122,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 +145,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,12 +169,12 @@ 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) { @@ -203,12 +203,12 @@ 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) { @@ -248,12 +248,12 @@ 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) { @@ -293,12 +293,12 @@ 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) { @@ -338,12 +338,12 @@ 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) { diff --git a/src/Polly.Contrib.Simmy/AsyncMonkeyEngine.cs b/src/Polly.Contrib.Simmy/AsyncMonkeyEngine.cs index fe2f191..c3d8a9e 100644 --- a/src/Polly.Contrib.Simmy/AsyncMonkeyEngine.cs +++ b/src/Polly.Contrib.Simmy/AsyncMonkeyEngine.cs @@ -7,14 +7,19 @@ 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)) + if (!await enabled(context, cancellationToken).ConfigureAwait(continueOnCapturedContext)) { return false; } - double injectionThreshold = await injectionRate(context).ConfigureAwait(continueOnCapturedContext); + double injectionThreshold = await injectionRate(context, cancellationToken).ConfigureAwait(continueOnCapturedContext); 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."); @@ -32,11 +37,11 @@ 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); } @@ -49,11 +54,11 @@ 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); if (exception != null) @@ -70,11 +75,11 @@ 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); } 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/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/Latency/AsyncInjectLatencyPolicy.cs b/src/Polly.Contrib.Simmy/Latency/AsyncInjectLatencyPolicy.cs index d01b8d1..084bc62 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)); @@ -51,8 +51,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)); 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)); From e56484acaa14fcf7c40b714cbb2e9212d23cfcc5 Mon Sep 17 00:00:00 2001 From: Geovanny Alzate Sandoval Date: Thu, 11 Jul 2019 21:58:51 -0500 Subject: [PATCH 02/10] adds unit tests to cover config delegates cancellable scenarios. refactors the engine to add some validations to avoid execute extra code when cancellation token is signaled. --- .../Fault/InjectFaultAsyncSpecs.cs | 220 +++++++++++++++++ .../Fault/InjectFaultTResultAsyncSpecs.cs | 221 ++++++++++++++++++ .../Latency/InjectLatencyAsyncSpecs.cs | 206 ++++++++++++++++ .../InjectLatencyTResultAsyncSpecs .cs | 206 ++++++++++++++++ src/Polly.Contrib.Simmy/AsyncMonkeyEngine.cs | 18 +- .../Latency/AsyncInjectLatencyPolicy.cs | 28 ++- 6 files changed, 890 insertions(+), 9 deletions(-) diff --git a/src/Polly.Contrib.Simmy.Specs/Fault/InjectFaultAsyncSpecs.cs b/src/Polly.Contrib.Simmy.Specs/Fault/InjectFaultAsyncSpecs.cs index 12f8da8..1b33ef7 100644 --- a/src/Polly.Contrib.Simmy.Specs/Fault/InjectFaultAsyncSpecs.cs +++ b/src/Polly.Contrib.Simmy.Specs/Fault/InjectFaultAsyncSpecs.cs @@ -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/InjectFaultTResultAsyncSpecs.cs b/src/Polly.Contrib.Simmy.Specs/Fault/InjectFaultTResultAsyncSpecs.cs index cbd90d2..604aedc 100644 --- a/src/Polly.Contrib.Simmy.Specs/Fault/InjectFaultTResultAsyncSpecs.cs +++ b/src/Polly.Contrib.Simmy.Specs/Fault/InjectFaultTResultAsyncSpecs.cs @@ -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 @@ -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/Latency/InjectLatencyAsyncSpecs.cs b/src/Polly.Contrib.Simmy.Specs/Latency/InjectLatencyAsyncSpecs.cs index e9f994d..dcf94d6 100644 --- a/src/Polly.Contrib.Simmy.Specs/Latency/InjectLatencyAsyncSpecs.cs +++ b/src/Polly.Contrib.Simmy.Specs/Latency/InjectLatencyAsyncSpecs.cs @@ -352,5 +352,211 @@ 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); + } + + #endregion } } diff --git a/src/Polly.Contrib.Simmy.Specs/Latency/InjectLatencyTResultAsyncSpecs .cs b/src/Polly.Contrib.Simmy.Specs/Latency/InjectLatencyTResultAsyncSpecs .cs index 0953735..68ae06c 100644 --- a/src/Polly.Contrib.Simmy.Specs/Latency/InjectLatencyTResultAsyncSpecs .cs +++ b/src/Polly.Contrib.Simmy.Specs/Latency/InjectLatencyTResultAsyncSpecs .cs @@ -365,5 +365,211 @@ 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); + } + + #endregion } } diff --git a/src/Polly.Contrib.Simmy/AsyncMonkeyEngine.cs b/src/Polly.Contrib.Simmy/AsyncMonkeyEngine.cs index c3d8a9e..b041f9f 100644 --- a/src/Polly.Contrib.Simmy/AsyncMonkeyEngine.cs +++ b/src/Polly.Contrib.Simmy/AsyncMonkeyEngine.cs @@ -14,12 +14,22 @@ private static async Task ShouldInjectAsync( Func> enabled, bool continueOnCapturedContext) { + // to prevent execute config delegates if user cancels the whole execution before to start. + cancellationToken.ThrowIfCancellationRequested(); + if (!await enabled(context, cancellationToken).ConfigureAwait(continueOnCapturedContext)) { return false; } + // to prevent execute injectionRate config delegate if user cancels execution on enable configuration delegate. + cancellationToken.ThrowIfCancellationRequested(); + double injectionThreshold = await injectionRate(context, cancellationToken).ConfigureAwait(continueOnCapturedContext); + + // to prevent execute further config delegates if user cancels execution 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."); @@ -29,7 +39,7 @@ private static async Task ShouldInjectAsync( 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."); } - return ThreadSafeRandom_LockOncePerThread.NextDouble() < injectionThreshold; + return ThreadSafeRandom_LockOncePerThread.NextDouble() < injectionThreshold; } internal static async Task InjectBehaviourImplementationAsync( @@ -46,6 +56,8 @@ internal static async Task InjectBehaviourImplementationAsync( await injectedBehaviour(context, cancellationToken).ConfigureAwait(continueOnCapturedContext); } + // to prevent execute the user's action if user cancels execution on injectedBehaviour configuration delegate. + cancellationToken.ThrowIfCancellationRequested(); return await action(context, cancellationToken).ConfigureAwait(continueOnCapturedContext); } @@ -61,6 +73,10 @@ internal static async Task InjectExceptionImplementationAsync( if (await ShouldInjectAsync(context, cancellationToken, injectionRate, enabled, continueOnCapturedContext).ConfigureAwait(continueOnCapturedContext)) { Exception exception = await injectedException(context, cancellationToken).ConfigureAwait(continueOnCapturedContext); + + // to prevent throws the exception if user cancels execution on injectedException configuration delegate. + cancellationToken.ThrowIfCancellationRequested(); + if (exception != null) { throw exception; diff --git a/src/Polly.Contrib.Simmy/Latency/AsyncInjectLatencyPolicy.cs b/src/Polly.Contrib.Simmy/Latency/AsyncInjectLatencyPolicy.cs index 084bc62..1fc91eb 100644 --- a/src/Polly.Contrib.Simmy/Latency/AsyncInjectLatencyPolicy.cs +++ b/src/Polly.Contrib.Simmy/Latency/AsyncInjectLatencyPolicy.cs @@ -32,10 +32,16 @@ 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); + cancellationToken.ThrowIfCancellationRequested(); + + await SystemClock.SleepAsync( + latency, + InjectLatencyPolicy.DefaultCancellationForInjectedLatency) + .ConfigureAwait(continueOnCapturedContext); + }, InjectionRate, Enabled, continueOnCapturedContext); @@ -69,10 +75,16 @@ 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); + cancellationToken.ThrowIfCancellationRequested(); + + await SystemClock.SleepAsync( + latency, + InjectLatencyPolicy.DefaultCancellationForInjectedLatency) + .ConfigureAwait(continueOnCapturedContext); + }, InjectionRate, Enabled, continueOnCapturedContext); From d930a07d964946ffa300cbaf7dda88081386b965 Mon Sep 17 00:00:00 2001 From: Geovanny Alzate Sandoval Date: Wed, 24 Jul 2019 21:34:27 -0500 Subject: [PATCH 03/10] refactors sync policies to allow make the config delegates cancellable --- .../Fault/InjectFaultSpecs.cs | 231 +++++++++++++++-- .../Fault/InjectFaultTResultSpecs.cs | 244 ++++++++++++++++-- .../Latency/InjectLatencySpecs.cs | 231 +++++++++++++++-- .../Latency/InjectLatencyTResultSpecs.cs | 237 +++++++++++++++-- src/Polly.Contrib.Simmy/AsyncMonkeyEngine.cs | 12 +- .../Behavior/InjectBehaviourPolicy.cs | 12 +- .../Behavior/InjectBehaviourSyntax.cs | 27 +- .../Behavior/InjectBehaviourTResultSyntax.cs | 25 +- .../Fault/InjectFaultSyntax.cs | 63 ++--- .../Fault/InjectOutcomePolicy.cs | 12 +- .../Latency/AsyncInjectLatencyPolicy.cs | 6 +- .../Latency/InjectLatencyPolicy.cs | 34 ++- .../Latency/InjectLatencySyntax.cs | 39 +-- src/Polly.Contrib.Simmy/MonkeyEngine.cs | 55 ++-- src/Polly.Contrib.Simmy/MonkeyPolicy.cs | 13 +- 15 files changed, 1048 insertions(+), 193 deletions(-) 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/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/InjectLatencySpecs.cs b/src/Polly.Contrib.Simmy.Specs/Latency/InjectLatencySpecs.cs index 56a9cb6..adfb576 100644 --- a/src/Polly.Contrib.Simmy.Specs/Latency/InjectLatencySpecs.cs +++ b/src/Polly.Contrib.Simmy.Specs/Latency/InjectLatencySpecs.cs @@ -1,4 +1,5 @@ using System; +using System.Threading; using FluentAssertions; using Polly.Contrib.Simmy.Utilities; using Polly.Utilities; @@ -88,7 +89,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 +110,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 +131,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 +153,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 +185,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 +218,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 +228,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 +261,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 +271,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 +304,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 +314,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 +339,203 @@ 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); + } + + #endregion } } diff --git a/src/Polly.Contrib.Simmy.Specs/Latency/InjectLatencyTResultSpecs.cs b/src/Polly.Contrib.Simmy.Specs/Latency/InjectLatencyTResultSpecs.cs index 07ab6a9..a5b03e7 100644 --- a/src/Polly.Contrib.Simmy.Specs/Latency/InjectLatencyTResultSpecs.cs +++ b/src/Polly.Contrib.Simmy.Specs/Latency/InjectLatencyTResultSpecs.cs @@ -1,4 +1,5 @@ using System; +using System.Threading; using FluentAssertions; using Polly.Utilities; using Polly.Contrib.Simmy.Specs.Helpers; @@ -97,7 +98,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 +121,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 +144,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 +168,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 +202,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 +237,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 +247,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 +282,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 +292,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 +327,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 +337,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 +363,209 @@ 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); + } + + #endregion } } diff --git a/src/Polly.Contrib.Simmy/AsyncMonkeyEngine.cs b/src/Polly.Contrib.Simmy/AsyncMonkeyEngine.cs index b041f9f..db90c82 100644 --- a/src/Polly.Contrib.Simmy/AsyncMonkeyEngine.cs +++ b/src/Polly.Contrib.Simmy/AsyncMonkeyEngine.cs @@ -14,7 +14,7 @@ private static async Task ShouldInjectAsync( Func> enabled, bool continueOnCapturedContext) { - // to prevent execute config delegates if user cancels the whole execution before to start. + // to prevent execute config delegates if token is signaled before to start. cancellationToken.ThrowIfCancellationRequested(); if (!await enabled(context, cancellationToken).ConfigureAwait(continueOnCapturedContext)) @@ -22,12 +22,12 @@ private static async Task ShouldInjectAsync( return false; } - // to prevent execute injectionRate config delegate if user cancels execution on enable configuration delegate. + // 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 user cancels execution on injectionRate configuration delegate. + // to prevent execute further config delegates if token is signaled on injectionRate configuration delegate. cancellationToken.ThrowIfCancellationRequested(); if (injectionThreshold < 0) @@ -56,7 +56,7 @@ internal static async Task InjectBehaviourImplementationAsync( await injectedBehaviour(context, cancellationToken).ConfigureAwait(continueOnCapturedContext); } - // to prevent execute the user's action if user cancels execution on injectedBehaviour configuration delegate. + // to prevent execute the user's action if token is signaled on injectedBehaviour delegate. cancellationToken.ThrowIfCancellationRequested(); return await action(context, cancellationToken).ConfigureAwait(continueOnCapturedContext); } @@ -74,7 +74,7 @@ internal static async Task InjectExceptionImplementationAsync( { Exception exception = await injectedException(context, cancellationToken).ConfigureAwait(continueOnCapturedContext); - // to prevent throws the exception if user cancels execution on injectedException configuration delegate. + // to prevent throws the exception if token is signaled on injectedException configuration delegate. cancellationToken.ThrowIfCancellationRequested(); if (exception != null) @@ -100,6 +100,8 @@ internal static async Task InjectResultImplementationAsync( 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/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/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 1fc91eb..4453b4e 100644 --- a/src/Polly.Contrib.Simmy/Latency/AsyncInjectLatencyPolicy.cs +++ b/src/Polly.Contrib.Simmy/Latency/AsyncInjectLatencyPolicy.cs @@ -35,8 +35,9 @@ protected override Task ImplementationAsync( async (ctx, ct) => { var latency = await _latencyProvider(ctx, cancellationToken).ConfigureAwait(continueOnCapturedContext); - cancellationToken.ThrowIfCancellationRequested(); + // to prevent inject latency if token was signaled on latency configuration delegate. + cancellationToken.ThrowIfCancellationRequested(); await SystemClock.SleepAsync( latency, InjectLatencyPolicy.DefaultCancellationForInjectedLatency) @@ -78,8 +79,9 @@ protected override Task ImplementationAsync( async (ctx, ct) => { var latency = await _latencyProvider(ctx, cancellationToken).ConfigureAwait(continueOnCapturedContext); - cancellationToken.ThrowIfCancellationRequested(); + // to prevent inject latency if token was signaled on latency configuration delegate. + cancellationToken.ThrowIfCancellationRequested(); await SystemClock.SleepAsync( latency, InjectLatencyPolicy.DefaultCancellationForInjectedLatency) diff --git a/src/Polly.Contrib.Simmy/Latency/InjectLatencyPolicy.cs b/src/Polly.Contrib.Simmy/Latency/InjectLatencyPolicy.cs index 67e4d0e..b20725c 100644 --- a/src/Polly.Contrib.Simmy/Latency/InjectLatencyPolicy.cs +++ b/src/Polly.Contrib.Simmy/Latency/InjectLatencyPolicy.cs @@ -11,12 +11,12 @@ 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 +29,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, DefaultCancellationForInjectedLatency); + }, InjectionRate, Enabled); } @@ -41,12 +48,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,7 +66,14 @@ 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, InjectLatencyPolicy.DefaultCancellationForInjectedLatency); + }, 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)); From fb450686eee5b9c667c67588312b77844e410d56 Mon Sep 17 00:00:00 2001 From: Geovanny Alzate Sandoval Date: Fri, 26 Jul 2019 20:32:12 -0500 Subject: [PATCH 04/10] enables cancellation on async InjectLatency monkey. --- .../Latency/InjectLatencyAsyncSpecs.cs | 76 +++++++++++++++++-- .../InjectLatencyTResultAsyncSpecs .cs | 75 ++++++++++++++++-- .../Latency/AsyncInjectLatencyPolicy.cs | 4 +- 3 files changed, 142 insertions(+), 13 deletions(-) diff --git a/src/Polly.Contrib.Simmy.Specs/Latency/InjectLatencyAsyncSpecs.cs b/src/Polly.Contrib.Simmy.Specs/Latency/InjectLatencyAsyncSpecs.cs index dcf94d6..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); @@ -170,7 +172,7 @@ public async Task InjectLatency_With_Context_With_InjectionRate_Lambda_Should_In { if (ctx["InjectionRate"] != null) { - return await Task.FromResult((double) ctx["InjectionRate"]); + return await Task.FromResult((double)ctx["InjectionRate"]); } return await Task.FromResult(0); @@ -203,7 +205,7 @@ public async Task InjectLatency_With_Context_With_InjectionRate_Lambda_Should_No { if (ctx["InjectionRate"] != null) { - return await Task.FromResult((double) ctx["InjectionRate"]); + return await Task.FromResult((double)ctx["InjectionRate"]); } return await Task.FromResult(0); @@ -247,7 +249,7 @@ public async Task InjectLatency_With_Context_With_Latency_Lambda_Should_Introduc { if (ctx["InjectionRate"] != null) { - return await Task.FromResult((double) ctx["InjectionRate"]); + return await Task.FromResult((double)ctx["InjectionRate"]); } return await Task.FromResult(0); @@ -291,7 +293,7 @@ public async Task InjectLatency_With_Context_With_Latency_Lambda_Should_Not_Intr { if (ctx["InjectionRate"] != null) { - return await Task.FromResult((double) ctx["InjectionRate"]); + return await Task.FromResult((double)ctx["InjectionRate"]); } return await Task.FromResult(0); @@ -335,7 +337,7 @@ public async Task InjectLatency_With_Context_With_Latency_Lambda_Should_Not_Intr { if (ctx["InjectionRate"] != null) { - return await Task.FromResult((double) ctx["InjectionRate"]); + return await Task.FromResult((double)ctx["InjectionRate"]); } return await Task.FromResult(0); @@ -557,6 +559,68 @@ public void InjectLatency_With_Context_Should_not_execute_user_delegate_if_user_ _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/InjectLatencyTResultAsyncSpecs .cs b/src/Polly.Contrib.Simmy.Specs/Latency/InjectLatencyTResultAsyncSpecs .cs index 68ae06c..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 @@ -178,7 +180,7 @@ public async Task InjectLatency_With_Context_With_InjectionRate_Lambda_Should_In { if (ctx["InjectionRate"] != null) { - return await Task.FromResult((double) ctx["InjectionRate"]); + return await Task.FromResult((double)ctx["InjectionRate"]); } return await Task.FromResult(0); @@ -212,7 +214,7 @@ public async Task InjectLatency_With_Context_With_InjectionRate_Lambda_Should_No { if (ctx["InjectionRate"] != null) { - return await Task.FromResult((double) ctx["InjectionRate"]); + return await Task.FromResult((double)ctx["InjectionRate"]); } return await Task.FromResult(0); @@ -257,7 +259,7 @@ public async Task InjectLatency_With_Context_With_Latency_Lambda_Should_Introduc { if (ctx["InjectionRate"] != null) { - return await Task.FromResult((double) ctx["InjectionRate"]); + return await Task.FromResult((double)ctx["InjectionRate"]); } return await Task.FromResult(0); @@ -302,7 +304,7 @@ public async Task InjectLatency_With_Context_With_Latency_Lambda_Should_Not_Intr { if (ctx["InjectionRate"] != null) { - return await Task.FromResult((double) ctx["InjectionRate"]); + return await Task.FromResult((double)ctx["InjectionRate"]); } return await Task.FromResult(0); @@ -347,7 +349,7 @@ public async Task InjectLatency_With_Context_With_Latency_Lambda_Should_Not_Intr { if (ctx["InjectionRate"] != null) { - return await Task.FromResult((double) ctx["InjectionRate"]); + return await Task.FromResult((double)ctx["InjectionRate"]); } return await Task.FromResult(0); @@ -570,6 +572,69 @@ public void InjectLatency_With_Context_Should_not_execute_user_delegate_if_user_ _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/Latency/AsyncInjectLatencyPolicy.cs b/src/Polly.Contrib.Simmy/Latency/AsyncInjectLatencyPolicy.cs index 4453b4e..eeba3d1 100644 --- a/src/Polly.Contrib.Simmy/Latency/AsyncInjectLatencyPolicy.cs +++ b/src/Polly.Contrib.Simmy/Latency/AsyncInjectLatencyPolicy.cs @@ -40,7 +40,7 @@ protected override Task ImplementationAsync( cancellationToken.ThrowIfCancellationRequested(); await SystemClock.SleepAsync( latency, - InjectLatencyPolicy.DefaultCancellationForInjectedLatency) + cancellationToken) .ConfigureAwait(continueOnCapturedContext); }, InjectionRate, @@ -84,7 +84,7 @@ protected override Task ImplementationAsync( cancellationToken.ThrowIfCancellationRequested(); await SystemClock.SleepAsync( latency, - InjectLatencyPolicy.DefaultCancellationForInjectedLatency) + cancellationToken) .ConfigureAwait(continueOnCapturedContext); }, InjectionRate, From ab2533c4518d734e4f1369b31d1f8566f2881b3d Mon Sep 17 00:00:00 2001 From: Geovanny Alzate Sandoval Date: Sat, 27 Jul 2019 19:04:14 -0500 Subject: [PATCH 05/10] honors the token on injectlatency sync overloads --- .../Latency/InjectLatencySpecs.cs | 58 ++++++++++++++++++ .../Latency/InjectLatencyTResultSpecs.cs | 60 +++++++++++++++++++ .../Latency/InjectLatencyPolicy.cs | 7 +-- 3 files changed, 120 insertions(+), 5 deletions(-) diff --git a/src/Polly.Contrib.Simmy.Specs/Latency/InjectLatencySpecs.cs b/src/Polly.Contrib.Simmy.Specs/Latency/InjectLatencySpecs.cs index adfb576..f981ef1 100644 --- a/src/Polly.Contrib.Simmy.Specs/Latency/InjectLatencySpecs.cs +++ b/src/Polly.Contrib.Simmy.Specs/Latency/InjectLatencySpecs.cs @@ -1,7 +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; @@ -536,6 +538,62 @@ public void InjectLatency_With_Context_Should_not_execute_user_delegate_if_user_ _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/InjectLatencyTResultSpecs.cs b/src/Polly.Contrib.Simmy.Specs/Latency/InjectLatencyTResultSpecs.cs index a5b03e7..315f7af 100644 --- a/src/Polly.Contrib.Simmy.Specs/Latency/InjectLatencyTResultSpecs.cs +++ b/src/Polly.Contrib.Simmy.Specs/Latency/InjectLatencyTResultSpecs.cs @@ -1,10 +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 { @@ -566,6 +568,64 @@ public void InjectLatency_With_Context_Should_not_execute_user_delegate_if_user_ _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/Latency/InjectLatencyPolicy.cs b/src/Polly.Contrib.Simmy/Latency/InjectLatencyPolicy.cs index b20725c..5620d75 100644 --- a/src/Polly.Contrib.Simmy/Latency/InjectLatencyPolicy.cs +++ b/src/Polly.Contrib.Simmy/Latency/InjectLatencyPolicy.cs @@ -9,8 +9,6 @@ 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; internal InjectLatencyPolicy( @@ -35,7 +33,7 @@ protected override TResult Implementation(Func Date: Sat, 27 Jul 2019 19:12:14 -0500 Subject: [PATCH 06/10] updates version to 0.2.0 --- GitVersionConfig.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 From 10cf2faac7d8d9546b86b81182bf6a75895b3cda Mon Sep 17 00:00:00 2001 From: Geovanny Alzate Sandoval Date: Sat, 27 Jul 2019 19:25:46 -0500 Subject: [PATCH 07/10] no message --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 28429db..a486397 100644 --- a/README.md +++ b/README.md @@ -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. From 3fd9089f9b3158608d261d0a1754cb58f8bbf997 Mon Sep 17 00:00:00 2001 From: Geovanny Alzate Sandoval Date: Mon, 29 Jul 2019 19:16:48 -0500 Subject: [PATCH 08/10] updates release notes. --- CHANGELOG.md | 4 ++++ src/Polly.Contrib.Simmy.nuspec | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index dc55530..e92f2a4 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 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 From d7a7e8a2a95ad8ae9577c842c779293001a211c3 Mon Sep 17 00:00:00 2001 From: Geovanny Alzate Sandoval Date: Tue, 30 Jul 2019 11:53:05 -0500 Subject: [PATCH 09/10] updates syntax examples. --- README.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index a486397..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 ); ``` From 30c82acbae0dd59545a369954c8764535357ee22 Mon Sep 17 00:00:00 2001 From: Geovanny Alzate Sandoval Date: Tue, 30 Jul 2019 17:20:07 -0500 Subject: [PATCH 10/10] Update CHANGELOG.md --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e92f2a4..43fa5c2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ ## 0.2.0 - Makes InjectLatency policies cancellable (both sync and async) -- Add support for cancellation on async configuration-providing delegates +- Add support for cancellation on async configuration-providing delegates ## 0.1.0 -- Initial launch \ No newline at end of file +- Initial launch