Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enables cancellation on async InjectLatency monkey. #44

Merged
merged 4 commits into from
Jul 28, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion GitVersionConfig.yaml
Original file line number Diff line number Diff line change
@@ -1 +1 @@
next-version: 0.1.0
next-version: 0.2.0
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
* [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.
76 changes: 70 additions & 6 deletions src/Polly.Contrib.Simmy.Specs/Latency/InjectLatencyAsyncSpecs.cs
Original file line number Diff line number Diff line change
@@ -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;

Expand Down Expand Up @@ -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<Task> actionAsync = () => { executed = true; return TaskHelper.EmptyTask; };
await policy.ExecuteAsync(actionAsync);

Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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<Context, CancellationToken, Task<bool>> enabled = async (ctx, ct) =>
{
return await Task.FromResult((bool)ctx["Enabled"]);
};

Func<Context, CancellationToken, Task<double>> injectionRate = async (ctx, ct) =>
{
if (ctx["InjectionRate"] != null)
{
return await Task.FromResult((double)ctx["InjectionRate"]);
}

return await Task.FromResult(0);
};

Func<Context, CancellationToken, Task<TimeSpan>> latencyProvider = async (ctx, ct) =>
{
if ((bool)ctx["ShouldInjectLatency"])
{
return await Task.FromResult(delay);
}

return await Task.FromResult(TimeSpan.FromMilliseconds(0));
};

Func<Context, CancellationToken, Task> 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<TimeoutRejectedException>();
watch.Stop();
}

executed.Should().BeFalse();
watch.Elapsed.Should().BeCloseTo(timeout, ((int)TimeSpan.FromSeconds(3).TotalMilliseconds));
}

#endregion
}
}
58 changes: 58 additions & 0 deletions src/Polly.Contrib.Simmy.Specs/Latency/InjectLatencySpecs.cs
Original file line number Diff line number Diff line change
@@ -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;

Expand Down Expand Up @@ -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<Context, CancellationToken, bool> enabled = (ctx, ct) =>
{
return (bool)ctx["Enabled"];
};

Func<Context, CancellationToken, double> injectionRate = (ctx, ct) =>
{
if (ctx["InjectionRate"] != null)
{
return (double)ctx["InjectionRate"];
}

return 0;
};

Func<Context, CancellationToken, TimeSpan> 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<TimeoutRejectedException>();
watch.Stop();
}

executed.Should().BeFalse();
watch.Elapsed.Should().BeCloseTo(timeout, ((int)TimeSpan.FromSeconds(3).TotalMilliseconds));
}

#endregion
}
}
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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<Context, CancellationToken, Task<bool>> enabled = async (ctx, ct) =>
{
return await Task.FromResult((bool)ctx["Enabled"]);
};

Func<Context, CancellationToken, Task<double>> injectionRate = async (ctx, ct) =>
{
if (ctx["InjectionRate"] != null)
{
return await Task.FromResult((double)ctx["InjectionRate"]);
}

return await Task.FromResult(0);
};

Func<Context, CancellationToken, Task<TimeSpan>> latencyProvider = async (ctx, ct) =>
{
if ((bool)ctx["ShouldInjectLatency"])
{
return await Task.FromResult(delay);
}

return await Task.FromResult(TimeSpan.FromMilliseconds(0));
};

Func<Context, CancellationToken, Task<ResultPrimitive>> actionAsync = (_, ct) =>
{
executed = true;
return Task.FromResult(ResultPrimitive.Good);
};

var policy = Policy.TimeoutAsync(timeout, timeoutStrategy)
.WrapAsync(MonkeyPolicy.InjectLatencyAsync<ResultPrimitive>(latencyProvider, injectionRate, enabled));

watch.Start();
policy.Awaiting(async x => { await x.ExecuteAsync(actionAsync, context, cts.Token); })
.ShouldThrow<TimeoutRejectedException>();

watch.Stop();
}

executed.Should().BeFalse();
watch.Elapsed.Should().BeCloseTo(timeout, ((int)TimeSpan.FromSeconds(3).TotalMilliseconds));
}

#endregion
}
}
Loading