Skip to content

Commit 588a8c6

Browse files
authored
Stress Framework (Azure#16656)
1 parent cf73109 commit 588a8c6

25 files changed

+1140
-11
lines changed
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<OutputType>Exe</OutputType>
5+
</PropertyGroup>
6+
7+
<ItemGroup>
8+
<PackageReference Include="System.Threading.Channels" />
9+
</ItemGroup>
10+
11+
<ItemGroup>
12+
<ProjectReference Include="..\Azure.Test.Stress\Azure.Test.Stress.csproj" />
13+
</ItemGroup>
14+
15+
</Project>
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
using Azure.Test.Stress;
5+
using CommandLine;
6+
using System;
7+
using System.Threading;
8+
using System.Threading.Tasks;
9+
10+
namespace Azure.Sample.Stress
11+
{
12+
public class DelayTest : StressTest<DelayTest.DelayOptions, DelayTest.DelayMetrics>
13+
{
14+
public DelayTest(DelayOptions options, DelayMetrics metrics) : base(options, metrics)
15+
{
16+
}
17+
18+
public override async Task RunAsync(CancellationToken cancellationToken)
19+
{
20+
while (!cancellationToken.IsCancellationRequested)
21+
{
22+
await Task.Delay(TimeSpan.FromMilliseconds(Options.InitialDelayMs), cancellationToken);
23+
24+
// Increment metrics
25+
Interlocked.Increment(ref Metrics.TotalOperations);
26+
}
27+
}
28+
29+
public class DelayOptions : StressOptions
30+
{
31+
[Option("initialDelayMs", Default = 1000, HelpText = "Initial delay (in milliseconds)")]
32+
public int InitialDelayMs { get; set; }
33+
}
34+
35+
public class DelayMetrics : StressMetrics
36+
{
37+
public long TotalOperations;
38+
}
39+
}
40+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
using Azure.Test.Stress;
5+
using System.Threading;
6+
using System.Threading.Tasks;
7+
8+
namespace Azure.Sample.Stress
9+
{
10+
public class NoOpTest : StressTest<StressOptions, StressMetrics>
11+
{
12+
public NoOpTest(StressOptions options, StressMetrics metrics) : base(options, metrics)
13+
{
14+
}
15+
16+
public override async Task RunAsync(CancellationToken cancellationToken)
17+
{
18+
await Task.Delay(Timeout.InfiniteTimeSpan, cancellationToken);
19+
}
20+
}
21+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
using Azure.Test.Stress;
5+
using System.Threading.Tasks;
6+
7+
namespace Azure.Sample.Stress
8+
{
9+
public class Program
10+
{
11+
public static async Task Main(string[] args)
12+
{
13+
await StressProgram.Main(typeof(Program).Assembly, args);
14+
}
15+
}
16+
}
Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
using Azure.Test.Stress;
5+
using CommandLine;
6+
using System;
7+
using System.Threading;
8+
using System.Threading.Channels;
9+
using System.Threading.Tasks;
10+
11+
namespace Azure.Sample.Stress
12+
{
13+
public class SendReceiveTest : StressTest<SendReceiveTest.SendReceiveOptions, SendReceiveTest.SendReceiveMetrics>
14+
{
15+
private Channel<int> _channel = Channel.CreateUnbounded<int>();
16+
17+
public SendReceiveTest(SendReceiveOptions options, SendReceiveMetrics metrics) : base(options, metrics)
18+
{
19+
}
20+
21+
public override async Task RunAsync(CancellationToken cancellationToken)
22+
{
23+
var senderTask = Sender(cancellationToken);
24+
25+
var receiverCts = new CancellationTokenSource();
26+
27+
var receiverTasks = new Task[Options.Receivers];
28+
for (var i = 0; i < Options.Receivers; i++)
29+
{
30+
receiverTasks[i] = Receiver(receiverCts.Token);
31+
}
32+
33+
try
34+
{
35+
await senderTask;
36+
}
37+
catch (Exception e) when (ContainsOperationCanceledException(e))
38+
{
39+
}
40+
41+
// Block until all messages have been received
42+
await DelayUntil(() => Metrics.Unprocessed == 0);
43+
44+
receiverCts.Cancel();
45+
46+
await Task.WhenAll(receiverTasks);
47+
}
48+
49+
private async Task Sender(CancellationToken cancellationToken)
50+
{
51+
var index = 0;
52+
while (!cancellationToken.IsCancellationRequested)
53+
{
54+
try
55+
{
56+
await Send(index, cancellationToken);
57+
Interlocked.Increment(ref Metrics.Sends);
58+
index++;
59+
}
60+
catch (Exception e) when (!ContainsOperationCanceledException(e))
61+
{
62+
Metrics.Exceptions.Enqueue(e);
63+
}
64+
}
65+
}
66+
67+
private async Task Receiver(CancellationToken cancellationToken)
68+
{
69+
while (!cancellationToken.IsCancellationRequested)
70+
{
71+
try
72+
{
73+
await Receive(cancellationToken);
74+
Interlocked.Increment(ref Metrics.Receives);
75+
}
76+
catch (Exception e) when (!ContainsOperationCanceledException(e))
77+
{
78+
Metrics.Exceptions.Enqueue(e);
79+
}
80+
}
81+
}
82+
83+
// Simulates method in SDK
84+
private async Task Send(int index, CancellationToken cancellationToken)
85+
{
86+
await Task.Delay(TimeSpan.FromMilliseconds(Random.Next(0, Options.MaxSendDelayMs)), cancellationToken);
87+
var d = Random.NextDouble();
88+
if (d < Options.SendExceptionRate)
89+
{
90+
throw new SendException(d.ToString());
91+
}
92+
else
93+
{
94+
await _channel.Writer.WriteAsync(index, cancellationToken);
95+
}
96+
}
97+
98+
// Simulates method in SDK
99+
private async Task Receive(CancellationToken cancellationToken)
100+
{
101+
await Task.Delay(TimeSpan.FromMilliseconds(Random.Next(0, Options.MaxReceiveDelayMs)), cancellationToken);
102+
var d = Random.NextDouble();
103+
if (d < Options.ReceiveExceptionRate)
104+
{
105+
throw new ReceiveException(d.ToString());
106+
}
107+
else
108+
{
109+
await _channel.Reader.ReadAsync(cancellationToken);
110+
}
111+
}
112+
113+
public class SendReceiveOptions : StressOptions
114+
{
115+
[Option("maxSendDelayMs", Default = 50, HelpText = "Max send delay (in milliseconds)")]
116+
public int MaxSendDelayMs { get; set; }
117+
118+
[Option("maxReceiveDelayMs", Default = 200, HelpText = "Max send delay (in milliseconds)")]
119+
public int MaxReceiveDelayMs { get; set; }
120+
121+
[Option("receivers", Default = 3, HelpText = "Number of receivers")]
122+
public int Receivers { get; set; }
123+
124+
[Option("sendExceptionRate", Default = .01, HelpText = "Rate of send exceptions")]
125+
public double SendExceptionRate { get; set; }
126+
127+
[Option("receiveExceptionRate", Default = .02, HelpText = "Rate of receive exceptions")]
128+
public double ReceiveExceptionRate { get; set; }
129+
}
130+
131+
public class SendReceiveMetrics : StressMetrics
132+
{
133+
public long Sends;
134+
public long Receives;
135+
public long Unprocessed => (Interlocked.Read(ref Sends) - Interlocked.Read(ref Receives));
136+
}
137+
138+
public class SendException : Exception
139+
{
140+
public SendException()
141+
{
142+
}
143+
144+
public SendException(string message) : base(message)
145+
{
146+
}
147+
}
148+
149+
public class ReceiveException : Exception
150+
{
151+
public ReceiveException()
152+
{
153+
}
154+
155+
public ReceiveException(string message) : base(message)
156+
{
157+
}
158+
}
159+
}
160+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
2+
Microsoft Visual Studio Solution File, Format Version 12.00
3+
# Visual Studio Version 16
4+
VisualStudioVersion = 16.0.29325.168
5+
MinimumVisualStudioVersion = 10.0.40219.1
6+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Azure.Test.Stress", "Azure.Test.Stress\Azure.Test.Stress.csproj", "{A0CCD9EF-D7C7-409B-9D28-19E3C4BD1948}"
7+
EndProject
8+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Azure.Sample.Stress", "Azure.Sample.Stress\Azure.Sample.Stress.csproj", "{29C9D6CF-DE90-42FD-B58F-3C254334D2C7}"
9+
EndProject
10+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Azure.Template", "..\..\sdk\template\Azure.Template\src\Azure.Template.csproj", "{6E88344A-4557-424C-9467-9A2AFAD84134}"
11+
EndProject
12+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Azure.Template.Stress", "..\..\sdk\template\Azure.Template\stress\Azure.Template.Stress.csproj", "{5B4187EE-9432-4FAD-9266-57D02A6A05F2}"
13+
EndProject
14+
Global
15+
GlobalSection(SolutionConfigurationPlatforms) = preSolution
16+
Debug|Any CPU = Debug|Any CPU
17+
Release|Any CPU = Release|Any CPU
18+
EndGlobalSection
19+
GlobalSection(ProjectConfigurationPlatforms) = postSolution
20+
{A0CCD9EF-D7C7-409B-9D28-19E3C4BD1948}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
21+
{A0CCD9EF-D7C7-409B-9D28-19E3C4BD1948}.Debug|Any CPU.Build.0 = Debug|Any CPU
22+
{A0CCD9EF-D7C7-409B-9D28-19E3C4BD1948}.Release|Any CPU.ActiveCfg = Release|Any CPU
23+
{A0CCD9EF-D7C7-409B-9D28-19E3C4BD1948}.Release|Any CPU.Build.0 = Release|Any CPU
24+
{29C9D6CF-DE90-42FD-B58F-3C254334D2C7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
25+
{29C9D6CF-DE90-42FD-B58F-3C254334D2C7}.Debug|Any CPU.Build.0 = Debug|Any CPU
26+
{29C9D6CF-DE90-42FD-B58F-3C254334D2C7}.Release|Any CPU.ActiveCfg = Release|Any CPU
27+
{29C9D6CF-DE90-42FD-B58F-3C254334D2C7}.Release|Any CPU.Build.0 = Release|Any CPU
28+
{6E88344A-4557-424C-9467-9A2AFAD84134}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
29+
{6E88344A-4557-424C-9467-9A2AFAD84134}.Debug|Any CPU.Build.0 = Debug|Any CPU
30+
{6E88344A-4557-424C-9467-9A2AFAD84134}.Release|Any CPU.ActiveCfg = Release|Any CPU
31+
{6E88344A-4557-424C-9467-9A2AFAD84134}.Release|Any CPU.Build.0 = Release|Any CPU
32+
{5B4187EE-9432-4FAD-9266-57D02A6A05F2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
33+
{5B4187EE-9432-4FAD-9266-57D02A6A05F2}.Debug|Any CPU.Build.0 = Debug|Any CPU
34+
{5B4187EE-9432-4FAD-9266-57D02A6A05F2}.Release|Any CPU.ActiveCfg = Release|Any CPU
35+
{5B4187EE-9432-4FAD-9266-57D02A6A05F2}.Release|Any CPU.Build.0 = Release|Any CPU
36+
EndGlobalSection
37+
GlobalSection(SolutionProperties) = preSolution
38+
HideSolutionNode = FALSE
39+
EndGlobalSection
40+
GlobalSection(ExtensibilityGlobals) = postSolution
41+
SolutionGuid = {CD67C1F7-9BEF-4878-A675-898D5D3B48E3}
42+
EndGlobalSection
43+
EndGlobal
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<ItemGroup>
4+
<PackageReference Include="Azure.Core" />
5+
<PackageReference Include="CommandLineParser" />
6+
<PackageReference Include="System.Reflection.Emit" />
7+
</ItemGroup>
8+
9+
</Project>
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
using System;
5+
using System.Threading;
6+
using System.Threading.Tasks;
7+
8+
namespace Azure.Test.Stress
9+
{
10+
internal interface IStressTest : IDisposable, IAsyncDisposable
11+
{
12+
Task SetupAsync();
13+
Task RunAsync(CancellationToken cancellationToken);
14+
Task CleanupAsync();
15+
}
16+
}

0 commit comments

Comments
 (0)