diff --git a/src/BenchmarksApps/Kestrel/PlatformBenchmarks/Data/RawDb.cs b/src/BenchmarksApps/Kestrel/PlatformBenchmarks/Data/RawDb.cs index 52c47cb4d..c524f75c0 100644 --- a/src/BenchmarksApps/Kestrel/PlatformBenchmarks/Data/RawDb.cs +++ b/src/BenchmarksApps/Kestrel/PlatformBenchmarks/Data/RawDb.cs @@ -2,17 +2,23 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Runtime.CompilerServices; using System.Threading.Tasks; + using Microsoft.Extensions.Caching.Memory; + using Npgsql; namespace PlatformBenchmarks { public class RawDb { + private static readonly ConcurrentQueue _sqlReadCommands = new ConcurrentQueue(); + private static readonly ConcurrentQueue _sqlFortuneCommands = new ConcurrentQueue(); + private static readonly object[] _cacheKeys = Enumerable.Range(0, 10001).Select((i) => new CacheKey(i)).ToArray(); private readonly ConcurrentRandom _random; private readonly string _connectionString; private readonly MemoryCache _cache = new MemoryCache( @@ -33,9 +39,10 @@ public async Task LoadSingleQueryRow() { await db.OpenAsync(); - var (cmd, _) = CreateReadCommand(db); - using (cmd) + using (var cmd = CreateReadCommand()) { + cmd.Connection = db; + cmd.Parameter.TypedValue = _random.Next(0, 10000) + 1; return await ReadSingleRow(cmd); } } @@ -49,13 +56,14 @@ public async Task LoadMultipleQueriesRows(int count) { await db.OpenAsync(); - var (cmd, idParameter) = CreateReadCommand(db); - using (cmd) + using (var cmd = CreateReadCommand()) { + cmd.Connection = db; + var param = cmd.Parameter; for (int i = 0; i < result.Length; i++) { + param.TypedValue = _random.Next(0, 10000) + 1; result[i] = await ReadSingleRow(cmd); - idParameter.TypedValue = _random.Next(1, 10001); } } } @@ -93,10 +101,10 @@ static async Task LoadUncachedQueries(int id, int i, int count, RawDb r { await db.OpenAsync(); - var (cmd, idParameter) = rawdb.CreateReadCommand(db); - using (cmd) + using (var cmd = CreateReadCommand()) { - Func> create = async (entry) => + cmd.Connection = db; + Func> create = async (entry) => { return await rawdb.ReadSingleRow(cmd); }; @@ -104,7 +112,8 @@ static async Task LoadUncachedQueries(int id, int i, int count, RawDb r var cacheKeys = _cacheKeys; var key = cacheKeys[id]; - idParameter.TypedValue = id; + var param = cmd.Parameter; + param.TypedValue = id; for (; i < result.Length; i++) { @@ -112,7 +121,7 @@ static async Task LoadUncachedQueries(int id, int i, int count, RawDb r result[i] = data; id = rawdb._random.Next(1, 10001); - idParameter.TypedValue = id; + param.TypedValue = id; key = cacheKeys[id]; } } @@ -128,14 +137,14 @@ public async Task PopulateCache() { await db.OpenAsync(); - var (cmd, idParameter) = CreateReadCommand(db); - using (cmd) + using (var cmd = CreateReadCommand()) { + cmd.Connection = db; var cacheKeys = _cacheKeys; var cache = _cache; for (var i = 1; i < 10001; i++) { - idParameter.TypedValue = i; + cmd.Parameter.TypedValue = i; cache.Set(cacheKeys[i], await ReadSingleRow(cmd)); } } @@ -152,13 +161,14 @@ public async Task LoadMultipleUpdatesRows(int count) { await db.OpenAsync(); - var (queryCmd, queryParameter) = CreateReadCommand(db); - using (queryCmd) + using (var cmd = CreateReadCommand()) { + cmd.Connection = db; + var queryParameter = cmd.Parameter; for (int i = 0; i < results.Length; i++) { - results[i] = await ReadSingleRow(queryCmd); - queryParameter.TypedValue = _random.Next(1, 10001); + queryParameter.TypedValue = _random.Next(0, 10000) + 1; + results[i] = await ReadSingleRow(cmd); } } @@ -192,34 +202,46 @@ public async Task> LoadFortunesRows() { await db.OpenAsync(); - using (var cmd = new NpgsqlCommand("SELECT id, message FROM fortune", db)) + using var cmd = CreateFortuneCommand(); + cmd.Connection = db; + using (var rdr = await cmd.ExecuteReaderAsync()) { while (await rdr.ReadAsync()) { result.Add(new Fortune ( - id:rdr.GetInt32(0), + id: rdr.GetInt32(0), message: rdr.GetString(1) )); } } } - result.Add(new Fortune(id: 0, message: "Additional fortune added at request time." )); + result.Add(new Fortune(id: 0, message: "Additional fortune added at request time.")); result.Sort(); return result; } - private (NpgsqlCommand readCmd, NpgsqlParameter idParameter) CreateReadCommand(NpgsqlConnection connection) + private static SqlReadCommand CreateReadCommand() { - var cmd = new NpgsqlCommand("SELECT id, randomnumber FROM world WHERE id = @Id", connection); - var parameter = new NpgsqlParameter(parameterName: "@Id", value: _random.Next(1, 10001)); + if (!_sqlReadCommands.TryDequeue(out var cmd)) + { + cmd = new SqlReadCommand(); + } - cmd.Parameters.Add(parameter); + return cmd; + } - return (cmd, parameter); + private static SqlFortuneCommand CreateFortuneCommand() + { + if (!_sqlFortuneCommands.TryDequeue(out var cmd)) + { + cmd = new SqlFortuneCommand(); + } + + return cmd; } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -237,7 +259,55 @@ private async Task ReadSingleRow(NpgsqlCommand cmd) } } - private static readonly object[] _cacheKeys = Enumerable.Range(0, 10001).Select((i) => new CacheKey(i)).ToArray(); + internal class SqlFortuneCommand : IDisposable + { + private readonly NpgsqlCommand _cmd; + + public NpgsqlConnection Connection + { + set { _cmd.Connection = value; } + } + + public SqlFortuneCommand() + { + _cmd = new NpgsqlCommand("SELECT id, message FROM fortune"); + } + + public Task ExecuteReaderAsync() => _cmd.ExecuteReaderAsync(); + + public void Dispose() + { + _cmd.Connection = null; + _sqlFortuneCommands.Enqueue(this); + } + } + + internal class SqlReadCommand : IDisposable + { + private readonly NpgsqlCommand _cmd; + private readonly NpgsqlParameter _parameter; + + public NpgsqlParameter Parameter => _parameter; + public NpgsqlConnection Connection + { + set { _cmd.Connection = value; } + } + + public SqlReadCommand() + { + _cmd = new NpgsqlCommand("SELECT id, randomnumber FROM world WHERE id = @Id"); + _parameter = new NpgsqlParameter(parameterName: "@Id", value: 0); + _cmd.Parameters.Add(_parameter); + } + + public static implicit operator NpgsqlCommand(SqlReadCommand c) => c._cmd; + + public void Dispose() + { + _cmd.Connection = null; + _sqlReadCommands.Enqueue(this); + } + } public sealed class CacheKey : IEquatable { @@ -249,7 +319,7 @@ public CacheKey(int value) public bool Equals(CacheKey key) => key._value == _value; - public override bool Equals(object obj) + public override bool Equals(object obj) => ReferenceEquals(obj, this); public override int GetHashCode()