8000 Merge pull request #257 from caleblloyd/f_concurrency_test · mysql-net/MySqlConnector@fb60897 · GitHub
[go: up one dir, main page]

Skip to content

Commit fb60897

Browse files
authored
Merge pull request #257 from caleblloyd/f_concurrency_test
Add Concurrency Tests.
2 parents 605c1ff + f74333f commit fb60897

File tree

7 files changed

+242
-14
lines changed

7 files changed

+242
-14
lines changed
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
using System;
2+
3+
namespace MySqlConnector.Performance.Commands
4+
{
5+
public static class CommandRunner
6+
{
7+
public static void Help()
8+
{
9+
Console.Error.WriteLine(@"dotnet run
10+
concurrency [iterations] [concurrency] [operations]
11+
-h, --help show this message
12+
");
13+
}
14+
15+
public static int Run(string[] args)
16+
{
17+
var cmd = args[0];
18+
19+
try
20+
{
21+
switch (cmd)
22+
{
23+
case "concurrency":
24+
if (args.Length != 4)
25+
goto default;
26+
ConcurrencyCommand.Run(int.Parse(args[1]), int.Parse(args[2]), int.Parse(args[3]));
27+
break;
28+
case "-h":
29+
case "--help":
30+
Help();
31+
break;
32+
default:
33+
Help();
34+
return 1;
35+
}
36+
}
37+
catch (Exception e)
38+
{
39+
Console.Error.WriteLine(e.Message);
40+
Console.Error.WriteLine(e.StackTrace);
41+
return 1;
42+
}
43+
return 0;
44+
}
45+
}
46+
}
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
using System;
2+
using System.Collections.Concurrent;
3+
using System.Collections.Generic;
4+
using System.Linq;
5+
using System.Threading;
6+
using System.Threading.Tasks;
7+
using MySqlConnector.Performance.Models;
8+
9+
namespace MySqlConnector.Performance.Commands
10+
{
11+
public static class ConcurrencyCommand
12+
{
13+
public static void Run(int iterations, int concurrency, int ops)
14+
{
15+
16+
var recordNum = 0;
17+
async Task InsertOne(AppDb db)
18+
{
19+
var blog = new BlogPost(db)
20+
{
21+
Title = "Title " + Interlocked.Increment(ref recordNum),
22+
Content = "content"
23+
};
24+
await blog.InsertAsync();
25+
}
26+
27+
var selected = new ConcurrentQueue<string>();
28+
async Task SelectTen(AppDb db)
29+
{
30+
var blogPosts = await (new BlogPostQuery(db)).LatestPostsAsync();
31+
selected.Enqueue(blogPosts.FirstOrDefault().Title);
32+
}
33+
34+
var sleepNum = 0;
35+
async Task SleepMillisecond(AppDb db)
36+
{
37+
using (var cmd = db.Connection.CreateCommand())
38+
{
39+
cmd.CommandText = "SELECT SLEEP(0.001)";
40+
await cmd.ExecuteNonQueryAsync();
41+
}
42+
Interlocked.Increment(ref sleepNum);
43+
}
44+
45+
using (var db = new AppDb())
46+
{
47+
db.Connection.Open();
48+
using (var cmd = db.Connection.CreateCommand())
49+
{
50+
cmd.CommandText = "DELETE FROM `BlogPost`";
51+
cmd.ExecuteNonQuery();
52+
}
53+
}
54+
55+
PerfTest(InsertOne, "Insert One", iterations, concurrency, ops).GetAwaiter().GetResult();
56+
using (var db = new AppDb())
57+
{
58+
db.Connection.Open();
59+
using (var cmd = db.Connection.CreateCommand())
60+
{
61+
cmd.CommandText = "SELECT COUNT(*) FROM `BlogPost`";
62+
Console.WriteLine("Records Inserted: " + cmd.ExecuteScalar());
63+
Console.WriteLine();
64+
}
65+
}
66+
67+
PerfTest(SelectTen, "Select Ten", iterations, concurrency, ops).GetAwaiter().GetResult();
68+
Console.WriteLine("Records Selected: " + selected.Count * 10);
69+
string firstRecord;
70+
if (selected.TryDequeue(out firstRecord))
71+
Console.WriteLine("First Record: " + firstRecord);
72+
Console.WriteLine();
73+
74+
PerfTest(SleepMillisecond, "Sleep 1ms", iterations, concurrency, ops).GetAwaiter().GetResult();
75+
Console.WriteLine("Total Sleep Commands: " + sleepNum);
76+
Console.WriteLine();
77+
}
78+
79+
public static async Task PerfTest(Func<AppDb, Task> test, string testName, int iterations, int concurrency, int ops)
80+
{
81+
var timers = new List<TimeSpan>();
82+
for (var iteration = 0; iteration < iterations; iteration++)
83+
{
84+
var tasks = new List<Task>();
85+
var start = DateTime.UtcNow;
86+
for (var connection = 0; connection < concurrency; connection++)
87+
{
88+
tasks.Add(ConnectionTask(test, ops));
89+
}
90+
await Task.WhenAll(tasks);
91+
timers.Add(DateTime.UtcNow - start);
92+
}
93+
Console.WriteLine("Test " + testName);
94+
Console.WriteLine("Iterations: " + iterations);
95+
Console.WriteLine("Concurrency: " + concurrency);
96+
Console.WriteLine("Operations: " + ops);
97+
Console.WriteLine("Times (Min, Average, Max) "
98+
+ timers.Min() + ", "
99+
+ TimeSpan.FromTicks(timers.Sum(timer => timer.Ticks) / timers.Count) + ", "
100+
+ timers.Max());
101+
Console.WriteLine();
102+
}
103+
104+
private static async Task ConnectionTask(Func<AppDb, Task> cb, int ops)
105+
{
106+
using (var db = new AppDb())
107+
{
108+
await db.Connection.OpenAsync();
109+
for (var op = 0; op < ops; op++)
110+
{
111+
await cb(db);
112+
}
113+
}
114+
}
115+
116+
}
117+
}

tests/MySqlConnector.Performance/Controllers/AsyncController.cs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,13 +118,21 @@ public async Task<IActionResult> BulkInsert(int num)
118118
};
119119
await blogPost.InsertAsync();
120120
}
121+
#if BASELINE
122+
txn.Commit();
123+
#else
124+
await txn.CommitAsync();
125+
#endif
121126
}
122127
catch (Exception)
123128
{
129+
#if BASELINE
130+
txn.Rollback();
131+
#else
124132
await txn.RollbackAsync();
133+
#endif
125134
throw;
126135
}
127-
await txn.CommitAsync();
128136
var timing = $"Async: Inserted {num} records in " + (DateTime.Now - time);
129137
Console.WriteLine(timing);
130138
return new OkObjectResult(timing);

tests/MySqlConnector.Performance/Models/BlogPostQuery.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,11 +58,19 @@ public async Task DeleteAllAsync()
5858
try
5959
{
6060
await DeleteAllCmd().ExecuteNonQueryAsync();
61+
#if BASELINE
62+
txn.Commit();
63+
#else
6164
await txn.CommitAsync();
65+
#endif
6266
}
6367
catch
6468
{
69+
#if BASELINE
70+
txn.Rollback();
71+
#else
6572
await txn.RollbackAsync();
73+
#endif
6674
throw;
6775
}
6876
}
Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,16 @@
1-
<Project Sdk="Microsoft.NET.Sdk.Web">
1+
<Project Sdk="Microsoft.NET.Sdk.Web">
22

33
<PropertyGroup>
44
<TargetFramework>netcoreapp1.1.1</TargetFramework>
55
<PreserveCompilationContext>true</PreserveCompilationContext>
66
<AssemblyName>MySqlConnector.Performance</AssemblyName>
77
<OutputType>Exe</OutputType>
88
<PackageId>MySqlConnector.Performance</PackageId>
9+
<ServerGarbageCollection>true</ServerGarbageCollection>
10+
<ConcurrentGarbageCollection>true</ConcurrentGarbageCollection>
11+
<ThreadPoolMinThreads>64</ThreadPoolMinThreads>
912
</PropertyGroup>
1013

11-
<ItemGroup>
12-
<ProjectReference Include="..\..\src\MySqlConnector\MySqlConnector.csproj" />
13-
</ItemGroup>
14-
1514
<ItemGroup>
1615
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="1.1.2" />
1716
<PackageReference Include="Microsoft.AspNetCore.Server.Kestrel" Version="1.1.1" />
@@ -25,4 +24,15 @@
2524
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="1.1.1" />
2625
</ItemGroup>
2726

28-
</Project>
27+
<ItemGroup Condition=" '$(Configuration)' != 'Baseline' ">
28+
<ProjectReference Include="..\..\src\MySqlConnector\MySqlConnector.csproj" />
29+
</ItemGroup>
30+
31+
<ItemGroup Condition=" '$(Configuration)' == 'Baseline' ">
32+
<PackageReference Include="MySql.Data" Version="7.0.7-m61" />
33+
</ItemGroup>
34+
<PropertyGroup Condition=" '$(Configuration)' == 'Baseline' ">
35+
<DefineConstants>BASELINE</DefineConstants>
36+
</PropertyGroup>
37+
38+
</Project>
Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
using Microsoft.AspNetCore.Hosting;
1+
using System;
2+
using Microsoft.AspNetCore.Hosting;
3+
using MySqlConnector.Performance.Commands;
24

35
namespace MySqlConnector.Performance
46
{
@@ -7,12 +9,19 @@ public class Program
79
public static void Main(string[] args)
810
{
911
AppDb.Initialize();
10-
var host = new WebHostBuilder()
11-
.UseKestrel()
12-
.UseStartup<Startup>()
13-
.Build();
14-
15-
host.Run();
12+
if (args.Length == 0)
13+
{
14+
var host = new WebHostBuilder()
15+
.UseUrls("http://*:5000")
16+
.UseKestrel()
17+
.UseStartup<Startup>()
18+
.Build();
19+
host.Run();
20+
}
21+
else
22+
{
23+
Environment.Exit(CommandRunner.Run(args));
24+
}
1625
}
1726
}
1827
}

tests/MySqlConnector.Performance/README.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,22 @@
11
Performance Tests
22
=================
33

4+
## Concurrency Testing
5+
6+
To run concurrency tests, execute the following command
7+
8+
```
9+
dotnet run concurrency [iterations] [concurrency] [operations]
10+
```
11+
12+
For example, the following command will run 3 iterations using 100 concurrent connections and 10 operations in each concurrency test:
13+
14+
```
15+
dotnet run concurrency 3 100 10
16+
```
17+
18+
## HTTP Load Testing
19+
420
The `MySqlConnector.Performance` project runs a .NET Core MVC API application that is intended to be used to load test asynchronous and synchronous MySqlConnector methods.
521

622
You first must configure your MySql Database. Open the `config.json.example` file, configure the connection string, and save it as `config.json`. Now you can run the application with `dotnet run`.
@@ -31,3 +47,17 @@ The `scripts` directory contains load testing scripts. These scripts require th
3147

3248
# run 50 sync queries per second for 1 minute on windows
3349
./stress.ps1 50 1m sync
50+
51+
## Baseline Tests
52+
53+
To run the Baseline tests against MySql.Data, first restore the project to include MySql.Data:
54+
55+
```
56+
dotnet restore /p:Configuration=Baseline
57+
```
58+
59+
Next, run use `dotnet -c Baseline` when running any dotnet commands. Example:
60+
61+
```
62+
dotnet run -c Baseline concurrency 3 100 10
63+
```

0 commit comments

Comments
 (0)
0