Skip to content

Commit 982babb

Browse files
authored
Improve UPDATES query for Platform (aspnet#2132)
* Improve updates * Update parameters * Update random number * Fix build * Fix arrays support
1 parent 87c93b1 commit 982babb

5 files changed

Lines changed: 159 additions & 67 deletions

File tree

src/BenchmarksApps/TechEmpower/PlatformBenchmarks/Data/Random.cs

Lines changed: 0 additions & 32 deletions
This file was deleted.

src/BenchmarksApps/TechEmpower/PlatformBenchmarks/Data/RawDb.cs

Lines changed: 29 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,14 @@
88
using System.Threading.Tasks;
99
using Microsoft.Extensions.Caching.Memory;
1010
using Npgsql;
11+
using NpgsqlTypes;
1112

1213
// ReSharper disable UseAwaitUsing
1314

1415
namespace PlatformBenchmarks
1516
{
1617
public sealed class RawDb
1718
{
18-
private readonly ConcurrentRandom _random;
1919
private readonly MemoryCache _cache
2020
= new(new MemoryCacheOptions { ExpirationScanFrequency = TimeSpan.FromMinutes(60) });
2121

@@ -25,11 +25,10 @@ private readonly MemoryCache _cache
2525
private readonly string _connectionString;
2626
#endif
2727

28-
public RawDb(ConcurrentRandom random, AppSettings appSettings)
28+
public RawDb(AppSettings appSettings)
2929
{
30-
_random = random;
3130
#if NET8_0_OR_GREATER
32-
_dataSource = new NpgsqlSlimDataSourceBuilder(appSettings.ConnectionString).Build();
31+
_dataSource = new NpgsqlSlimDataSourceBuilder(appSettings.ConnectionString).EnableArrays().Build();
3332
#elif NET7_0
3433
_dataSource = NpgsqlDataSource.Create(appSettings.ConnectionString);
3534
#else
@@ -53,10 +52,10 @@ public Task<CachedWorld[]> LoadCachedQueries(int count)
5352
var result = new CachedWorld[count];
5453
var cacheKeys = _cacheKeys;
5554
var cache = _cache;
56-
var random = _random;
55+
5756
for (var i = 0; i < result.Length; i++)
5857
{
59-
var id = random.Next(1, 10001);
58+
var id = Random.Shared.Next(1, 10001);
6059
var key = cacheKeys[id];
6160
if (cache.TryGetValue(key, out var cached))
6261
{
@@ -88,7 +87,7 @@ static async Task<CachedWorld[]> LoadUncachedQueries(int id, int i, int count, R
8887
{
8988
result[i] = await rawdb._cache.GetOrCreateAsync(key, create);
9089

91-
id = rawdb._random.Next(1, 10001);
90+
id = Random.Shared.Next(1, 10001);
9291
idParameter.TypedValue = id;
9392
key = cacheKeys[id];
9493
}
@@ -136,7 +135,7 @@ public async Task<World[]> LoadMultipleQueriesRows(int count)
136135
batch.BatchCommands.Add(new()
137136
{
138137
CommandText = "SELECT id, randomnumber FROM world WHERE id = $1",
139-
Parameters = { new NpgsqlParameter<int> { TypedValue = _random.Next(1, 10001) } }
138+
Parameters = { new NpgsqlParameter<int> { TypedValue = Random.Shared.Next(1, 10001) } }
140139
});
141140
}
142141

@@ -165,7 +164,7 @@ public async Task<World[]> LoadMultipleQueriesRows(int count)
165164
for (var i = 0; i < results.Length; i++)
166165
{
167166
results[i] = await ReadSingleRow(cmd);
168-
idParameter.TypedValue = _random.Next(1, 10001);
167+
idParameter.TypedValue = Random.Shared.Next(1, 10001);
169168
}
170169

171170
return results;
@@ -176,23 +175,29 @@ public async Task<World[]> LoadMultipleUpdatesRows(int count)
176175
{
177176
var results = new World[count];
178177

178+
var ids = new int[count];
179+
for (var i = 0; i < count; i++)
180+
{
181+
ids[i] = Random.Shared.Next(1, 10001);
182+
}
183+
Array.Sort(ids);
184+
179185
using var connection = CreateConnection();
180186
await connection.OpenAsync();
181187

182-
#if NET7_0_OR_GREATER
183188
using (var batch = new NpgsqlBatch(connection))
184189
{
185190
// Inserts a PG Sync message between each statement in the batch, required for compliance with
186191
// TechEmpower general test requirement 7
187192
// https://github.com/TechEmpower/FrameworkBenchmarks/wiki/Project-Information-Framework-Tests-Overview
188193
batch.EnableErrorBarriers = true;
189-
194+
190195
for (var i = 0; i < count; i++)
191196
{
192197
batch.BatchCommands.Add(new()
193198
{
194199
CommandText = "SELECT id, randomnumber FROM world WHERE id = $1",
195-
Parameters = { new NpgsqlParameter<int> { TypedValue = _random.Next(1, 10001) } }
200+
Parameters = { new NpgsqlParameter<int> { TypedValue = ids[i] } }
196201
});
197202
}
198203

@@ -205,32 +210,22 @@ public async Task<World[]> LoadMultipleUpdatesRows(int count)
205210
await reader.NextResultAsync();
206211
}
207212
}
208-
#else
209-
var (queryCmd, queryParameter) = CreateReadCommand(connection);
210-
using (queryCmd)
211-
{
212-
for (var i = 0; i < results.Length; i++)
213-
{
214-
results[i] = await ReadSingleRow(queryCmd);
215-
queryParameter.TypedValue = _random.Next(1, 10001);
216-
}
217-
}
218-
#endif
219213

220-
using (var updateCmd = new NpgsqlCommand(BatchUpdateString.Query(count), connection))
214+
var numbers = new int[count];
215+
for (var i = 0; i < count; i++)
221216
{
222-
for (var i = 0; i < results.Length; i++)
223-
{
224-
var randomNumber = _random.Next(1, 10001);
217+
var randomNumber = Random.Shared.Next(1, 10001);
218+
results[i].RandomNumber = randomNumber;
219+
numbers[i] = randomNumber;
220+
}
225221

226-
updateCmd.Parameters.Add(new NpgsqlParameter<int> { TypedValue = results[i].Id });
227-
updateCmd.Parameters.Add(new NpgsqlParameter<int> { TypedValue = randomNumber });
222+
var update = "UPDATE world w SET randomnumber = u.new_val FROM (SELECT unnest($1) as id, unnest($2) as new_val) u WHERE w.id = u.id";
228223

229-
results[i].RandomNumber = randomNumber;
230-
}
224+
using var updateCmd = new NpgsqlCommand(update, connection);
225+
updateCmd.Parameters.Add(new NpgsqlParameter<int[]> { TypedValue = ids, NpgsqlDbType = NpgsqlDbType.Array | NpgsqlDbType.Integer });
226+
updateCmd.Parameters.Add(new NpgsqlParameter<int[]> { TypedValue = numbers, NpgsqlDbType = NpgsqlDbType.Array | NpgsqlDbType.Integer });
231227

232-
await updateCmd.ExecuteNonQueryAsync();
233-
}
228+
await updateCmd.ExecuteNonQueryAsync();
234229

235230
return results;
236231
}
@@ -293,7 +288,7 @@ public Task<List<FortuneUtf8>> LoadFortunesRowsNoDb()
293288
private (NpgsqlCommand readCmd, NpgsqlParameter<int> idParameter) CreateReadCommand(NpgsqlConnection connection)
294289
{
295290
var cmd = new NpgsqlCommand("SELECT id, randomnumber FROM world WHERE id = $1", connection);
296-
var parameter = new NpgsqlParameter<int> { TypedValue = _random.Next(1, 10001) };
291+
var parameter = new NpgsqlParameter<int> { TypedValue = Random.Shared.Next(1, 10001) };
297292

298293
cmd.Parameters.Add(parameter);
299294

src/BenchmarksApps/TechEmpower/PlatformBenchmarks/Program.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ public static IWebHost BuildWebHost(string[] args)
7373

7474
if (appSettings.Database == DatabaseServer.PostgreSql)
7575
{
76-
BenchmarkApplication.RawDb = new RawDb(new ConcurrentRandom(), appSettings);
76+
BenchmarkApplication.RawDb = new RawDb(appSettings);
7777
BenchmarkApplication.DapperDb = new DapperDb(appSettings);
7878
BenchmarkApplication.EfDb = new EfDb(appSettings);
7979
}
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
#:sdk Aspire.AppHost.Sdk@13.0.0
2+
3+
var builder = DistributedApplication.CreateBuilder(args);
4+
5+
// Add the postgres database from Dockerfile
6+
var postgres = builder.AddDockerfile("postgres-techempower", "../../../docker/postgres-techempower")
7+
.WithEndpoint(port: 5432, targetPort: 5432, name: "tcp")
8+
.WithEnvironment("POSTGRES_USER", "benchmarkdbuser")
9+
.WithEnvironment("POSTGRES_PASSWORD", "benchmarkdbpass")
10+
.WithEnvironment("POSTGRES_DB", "hello_world");
11+
12+
var postgresEndpoint = postgres.GetEndpoint("tcp");
13+
var connectionString = ReferenceExpression.Create($"Server={postgresEndpoint.Property(EndpointProperty.Host)};Port={postgresEndpoint.Property(EndpointProperty.Port)};Database=hello_world;User Id=benchmarkdbuser;Password=benchmarkdbpass");
14+
15+
// Add all TechEmpower benchmark applications
16+
// Note: BlazorSSR, Minimal, Mvc only support net8.0. MvcFull targets .NET Framework 4.8.
17+
// PlatformBenchmarks and RazorPages support net8.0;net9.0 multi-targeting.
18+
19+
builder.AddProject("blazorssr", "BlazorSSR/BlazorSSR.csproj")
20+
.WaitFor(postgres)
21+
.WithEnvironment("ConnectionString", connectionString)
22+
.WithUrls(context =>
23+
{
24+
var http = context.GetEndpoint("http");
25+
if (http is not null)
26+
{
27+
context.Urls.Add(new() { Url = $"{http.Url}/plaintext", DisplayText = "plaintext" });
28+
context.Urls.Add(new() { Url = $"{http.Url}/json", DisplayText = "json" });
29+
context.Urls.Add(new() { Url = $"{http.Url}/fortunes", DisplayText = "fortunes" });
30+
context.Urls.Add(new() { Url = $"{http.Url}/updates/20", DisplayText = "updates" });
31+
}
32+
});
33+
34+
builder.AddProject("minimal", "Minimal/Minimal.csproj")
35+
.WaitFor(postgres)
36+
.WithEnvironment("ConnectionString", connectionString)
37+
.WithUrls(context =>
38+
{
39+
var http = context.GetEndpoint("http");
40+
if (http is not null)
41+
{
42+
context.Urls.Add(new() { Url = $"{http.Url}/plaintext", DisplayText = "plaintext" });
43+
context.Urls.Add(new() { Url = $"{http.Url}/json", DisplayText = "json" });
44+
context.Urls.Add(new() { Url = $"{http.Url}/fortunes", DisplayText = "fortunes" });
45+
context.Urls.Add(new() { Url = $"{http.Url}/updates/20", DisplayText = "updates" });
46+
}
47+
});
48+
49+
builder.AddProject("mvc", "Mvc/Mvc.csproj")
50+
.WaitFor(postgres)
51+
.WithEnvironment("ConnectionString", connectionString)
52+
.WithUrls(context =>
53+
{
54+
var http = context.GetEndpoint("http");
55+
if (http is not null)
56+
{
57+
context.Urls.Add(new() { Url = $"{http.Url}/plaintext", DisplayText = "plaintext" });
58+
context.Urls.Add(new() { Url = $"{http.Url}/json", DisplayText = "json" });
59+
context.Urls.Add(new() { Url = $"{http.Url}/fortunes", DisplayText = "fortunes" });
60+
context.Urls.Add(new() { Url = $"{http.Url}/updates/20", DisplayText = "updates" });
61+
}
62+
});
63+
64+
builder.AddProject("platformbenchmarks", "PlatformBenchmarks/PlatformBenchmarks.csproj")
65+
.WaitFor(postgres)
66+
.WithEnvironment("ConnectionString", connectionString)
67+
.WithArgs("--framework", "net9.0")
68+
.WithArgs("-p:IsDatabase=true")
69+
.WithEnvironment("DATABASE", "PostgreSql")
70+
.WithUrls(context =>
71+
{
72+
var http = context.GetEndpoint("http");
73+
if (http is not null)
74+
{
75+
context.Urls.Add(new() { Url = $"{http.Url}/plaintext", DisplayText = "plaintext" });
76+
context.Urls.Add(new() { Url = $"{http.Url}/json", DisplayText = "json" });
77+
context.Urls.Add(new() { Url = $"{http.Url}/fortunes", DisplayText = "fortunes" });
78+
context.Urls.Add(new() { Url = $"{http.Url}/updates/20", DisplayText = "updates" });
79+
}
80+
});
81+
82+
builder.AddProject("razorpages", "RazorPages/RazorPages.csproj")
83+
.WaitFor(postgres)
84+
.WithEnvironment("ConnectionString", connectionString)
85+
.WithArgs("--framework", "net9.0")
86+
.WithUrls(context =>
87+
{
88+
var http = context.GetEndpoint("http");
89+
if (http is not null)
90+
{
91+
context.Urls.Add(new() { Url = $"{http.Url}/plaintext", DisplayText = "plaintext" });
92+
context.Urls.Add(new() { Url = $"{http.Url}/json", DisplayText = "json" });
93+
context.Urls.Add(new() { Url = $"{http.Url}/fortunes", DisplayText = "fortunes" });
94+
context.Urls.Add(new() { Url = $"{http.Url}/updates/20", DisplayText = "updates" });
95+
}
96+
});
97+
98+
builder.Build().Run();
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
{
2+
"$schema": "https://json.schemastore.org/launchsettings.json",
3+
"profiles": {
4+
"https": {
5+
"commandName": "Project",
6+
"dotnetRunMessages": true,
7+
"launchBrowser": true,
8+
"applicationUrl": "https://localhost:17005;http://localhost:15223",
9+
"environmentVariables": {
10+
"ASPNETCORE_ENVIRONMENT": "Development",
11+
"DOTNET_ENVIRONMENT": "Development",
12+
"ASPIRE_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:21012",
13+
"ASPIRE_DASHBOARD_MCP_ENDPOINT_URL": "https://localhost:23127",
14+
"ASPIRE_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:22253"
15+
}
16+
},
17+
"http": {
18+
"commandName": "Project",
19+
"dotnetRunMessages": true,
20+
"launchBrowser": true,
21+
"applicationUrl": "http://localhost:15223",
22+
"environmentVariables": {
23+
"ASPNETCORE_ENVIRONMENT": "Development",
24+
"DOTNET_ENVIRONMENT": "Development",
25+
"ASPIRE_DASHBOARD_OTLP_ENDPOINT_URL": "http://localhost:19080",
26+
"ASPIRE_DASHBOARD_MCP_ENDPOINT_URL": "http://localhost:18252",
27+
"ASPIRE_RESOURCE_SERVICE_ENDPOINT_URL": "http://localhost:20285"
28+
}
29+
}
30+
}
31+
}

0 commit comments

Comments
 (0)