|
@@ -10,110 +10,44 @@ using System.Threading.Tasks;
|
|
|
|
|
|
using Microsoft.Extensions.Caching.Memory;
|
|
using Microsoft.Extensions.Caching.Memory;
|
|
using Npgsql;
|
|
using Npgsql;
|
|
|
|
+using System.Runtime.CompilerServices;
|
|
|
|
|
|
|
|
|
|
namespace PlatformBenchmarks
|
|
namespace PlatformBenchmarks
|
|
{
|
|
{
|
|
- public class RawDb
|
|
|
|
|
|
+ public sealed class RawDb
|
|
{
|
|
{
|
|
-
|
|
|
|
private readonly ConcurrentRandom _random;
|
|
private readonly ConcurrentRandom _random;
|
|
|
|
+ private readonly MemoryCache _cache
|
|
|
|
+ = new(new MemoryCacheOptions { ExpirationScanFrequency = TimeSpan.FromMinutes(60) });
|
|
|
|
|
|
- private readonly DbProviderFactory _dbProviderFactory;
|
|
|
|
-
|
|
|
|
- private readonly static MemoryCache _cache = new MemoryCache(
|
|
|
|
- new MemoryCacheOptions()
|
|
|
|
- {
|
|
|
|
- ExpirationScanFrequency = TimeSpan.FromMinutes(60)
|
|
|
|
- });
|
|
|
|
-
|
|
|
|
- private static readonly object[] _cacheKeys = Enumerable.Range(0, 10001).Select((i) => new CacheKey(i)).ToArray();
|
|
|
|
|
|
+ private static DbProviderFactory _dbProviderFactory => Npgsql.NpgsqlFactory.Instance;
|
|
|
|
+ private readonly string _connectionString;
|
|
|
|
|
|
- public static string _connectionString = null;
|
|
|
|
-
|
|
|
|
- public RawDb(ConcurrentRandom random, DbProviderFactory dbProviderFactory)
|
|
|
|
|
|
+ public RawDb(ConcurrentRandom random, string connectionString)
|
|
{
|
|
{
|
|
_random = random;
|
|
_random = random;
|
|
- _dbProviderFactory = dbProviderFactory;
|
|
|
|
- OnCreateCommand();
|
|
|
|
- }
|
|
|
|
- private void OnCreateCommand()
|
|
|
|
- {
|
|
|
|
- SingleCommand = new Npgsql.NpgsqlCommand();
|
|
|
|
- SingleCommand.CommandText = "SELECT id, randomnumber FROM world WHERE id = @Id";
|
|
|
|
- mID = new Npgsql.NpgsqlParameter<int>("@Id", _random.Next(1, 10001));
|
|
|
|
- SingleCommand.Parameters.Add(mID);
|
|
|
|
- FortuneCommand = new Npgsql.NpgsqlCommand();
|
|
|
|
- FortuneCommand.CommandText = "SELECT id, message FROM fortune";
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- private DbCommand SingleCommand;
|
|
|
|
-
|
|
|
|
- private DbCommand FortuneCommand;
|
|
|
|
-
|
|
|
|
- private Npgsql.NpgsqlParameter<int> mID;
|
|
|
|
-
|
|
|
|
- private static int ListDefaultSize = 8;
|
|
|
|
-
|
|
|
|
- private World[] mWorldBuffer = null;
|
|
|
|
-
|
|
|
|
- private World[] GetWorldBuffer()
|
|
|
|
- {
|
|
|
|
- if (mWorldBuffer == null)
|
|
|
|
- mWorldBuffer = new World[512];
|
|
|
|
- return mWorldBuffer;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- private Fortune[] mFortunesBuffer = null;
|
|
|
|
-
|
|
|
|
- private Fortune[] GetFortuneBuffer()
|
|
|
|
- {
|
|
|
|
- if (mFortunesBuffer == null)
|
|
|
|
- mFortunesBuffer = new Fortune[512];
|
|
|
|
- return mFortunesBuffer;
|
|
|
|
|
|
+ _connectionString = connectionString;
|
|
}
|
|
}
|
|
|
|
|
|
public async Task<World> LoadSingleQueryRow()
|
|
public async Task<World> LoadSingleQueryRow()
|
|
{
|
|
{
|
|
- using (var db = _dbProviderFactory.CreateConnection())
|
|
|
|
- {
|
|
|
|
- db.ConnectionString = _connectionString;
|
|
|
|
- await db.OpenAsync();
|
|
|
|
- SingleCommand.Connection = db;
|
|
|
|
- mID.TypedValue = _random.Next(1, 10001);
|
|
|
|
- return await ReadSingleRow(db, SingleCommand);
|
|
|
|
-
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- async Task<World> ReadSingleRow(DbConnection connection, DbCommand cmd)
|
|
|
|
- {
|
|
|
|
- using (var rdr = await cmd.ExecuteReaderAsync(CommandBehavior.SingleRow))
|
|
|
|
-
|
|
|
|
|
|
+ using (var connection = (NpgsqlConnection)_dbProviderFactory.CreateConnection())
|
|
{
|
|
{
|
|
- await rdr.ReadAsync();
|
|
|
|
- return new World
|
|
|
|
|
|
+ connection.ConnectionString = _connectionString;
|
|
|
|
+ await connection.OpenAsync();
|
|
|
|
+ var (cmd, _) = CreateReadCommand(connection);
|
|
|
|
+ using (var command = cmd)
|
|
{
|
|
{
|
|
- Id = rdr.GetInt32(0),
|
|
|
|
- RandomNumber = rdr.GetInt32(1)
|
|
|
|
- };
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
|
|
|
|
- public async Task<ArraySegment<World>> LoadMultipleQueriesRows(int count)
|
|
|
|
- {
|
|
|
|
- using (var db = _dbProviderFactory.CreateConnection())
|
|
|
|
- {
|
|
|
|
- db.ConnectionString = _connectionString;
|
|
|
|
- await db.OpenAsync();
|
|
|
|
- return await LoadMultipleRows(count, db);
|
|
|
|
|
|
+ return await ReadSingleRow(cmd);
|
|
|
|
+ }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
-
|
|
|
|
- public Task<World[]> LoadCachedQueries(int count)
|
|
|
|
|
|
+ public Task<CachedWorld[]> LoadCachedQueries(int count)
|
|
{
|
|
{
|
|
- var result = new World[count];
|
|
|
|
|
|
+ var result = new CachedWorld[count];
|
|
var cacheKeys = _cacheKeys;
|
|
var cacheKeys = _cacheKeys;
|
|
var cache = _cache;
|
|
var cache = _cache;
|
|
var random = _random;
|
|
var random = _random;
|
|
@@ -121,42 +55,40 @@ namespace PlatformBenchmarks
|
|
{
|
|
{
|
|
var id = random.Next(1, 10001);
|
|
var id = random.Next(1, 10001);
|
|
var key = cacheKeys[id];
|
|
var key = cacheKeys[id];
|
|
- var data = cache.Get<CachedWorld>(key);
|
|
|
|
-
|
|
|
|
- if (data != null)
|
|
|
|
|
|
+ if (cache.TryGetValue(key, out var cached))
|
|
{
|
|
{
|
|
- result[i] = data;
|
|
|
|
|
|
+ result[i] = (CachedWorld)cached;
|
|
}
|
|
}
|
|
else
|
|
else
|
|
{
|
|
{
|
|
- return LoadUncachedQueries(id, i, count, this, result);
|
|
|
|
|
|
+ return LoadUncachedQueries(_connectionString, id, i, count, this, result);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
return Task.FromResult(result);
|
|
return Task.FromResult(result);
|
|
|
|
|
|
- static async Task<World[]> LoadUncachedQueries(int id, int i, int count, RawDb rawdb, World[] result)
|
|
|
|
|
|
+ static async Task<CachedWorld[]> LoadUncachedQueries(string conn, int id, int i, int count, RawDb rawdb, CachedWorld[] result)
|
|
{
|
|
{
|
|
- using (var db = new NpgsqlConnection(_connectionString))
|
|
|
|
|
|
+ using (var connection = (NpgsqlConnection)_dbProviderFactory.CreateConnection())
|
|
{
|
|
{
|
|
- await db.OpenAsync();
|
|
|
|
- Func<ICacheEntry, Task<CachedWorld>> create = async (entry) =>
|
|
|
|
- {
|
|
|
|
- return await rawdb.ReadSingleRow(db, rawdb.SingleCommand);
|
|
|
|
- };
|
|
|
|
|
|
+ connection.ConnectionString = conn;
|
|
|
|
+ await connection.OpenAsync();
|
|
|
|
+ var (cmd, idParameter) = rawdb.CreateReadCommand(connection);
|
|
|
|
+ using var command = cmd;
|
|
|
|
+ async Task<CachedWorld> create(ICacheEntry _) => await ReadSingleRow(cmd);
|
|
|
|
+
|
|
|
|
|
|
var cacheKeys = _cacheKeys;
|
|
var cacheKeys = _cacheKeys;
|
|
var key = cacheKeys[id];
|
|
var key = cacheKeys[id];
|
|
|
|
|
|
- rawdb.SingleCommand.Connection = db;
|
|
|
|
- rawdb.mID.TypedValue = id;
|
|
|
|
|
|
+ idParameter.TypedValue = id;
|
|
|
|
+
|
|
for (; i < result.Length; i++)
|
|
for (; i < result.Length; i++)
|
|
{
|
|
{
|
|
- var data = await _cache.GetOrCreateAsync<CachedWorld>(key, create);
|
|
|
|
- result[i] = data;
|
|
|
|
|
|
+ result[i] = await rawdb._cache.GetOrCreateAsync(key, create);
|
|
|
|
+
|
|
id = rawdb._random.Next(1, 10001);
|
|
id = rawdb._random.Next(1, 10001);
|
|
- rawdb.SingleCommand.Connection = db;
|
|
|
|
- rawdb.mID.TypedValue = id;
|
|
|
|
|
|
+ idParameter.TypedValue = id;
|
|
key = cacheKeys[id];
|
|
key = cacheKeys[id];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
@@ -164,215 +96,212 @@ namespace PlatformBenchmarks
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
-
|
|
|
|
- private async Task<ArraySegment<World>> LoadMultipleRows(int count, DbConnection db)
|
|
|
|
|
|
+ public async Task PopulateCache()
|
|
{
|
|
{
|
|
- SingleCommand.Connection = db;
|
|
|
|
- SingleCommand.Parameters[0].Value = _random.Next(1, 10001);
|
|
|
|
- var result = GetWorldBuffer();
|
|
|
|
- for (int i = 0; i < count; i++)
|
|
|
|
|
|
+ using (var connection = (NpgsqlConnection)_dbProviderFactory.CreateConnection())
|
|
{
|
|
{
|
|
- result[i] = await ReadSingleRow(db, SingleCommand);
|
|
|
|
- SingleCommand.Parameters[0].Value = _random.Next(1, 10001);
|
|
|
|
|
|
+ connection.ConnectionString = _connectionString;
|
|
|
|
+ await connection.OpenAsync();
|
|
|
|
+ var (cmd, idParameter) = CreateReadCommand(connection);
|
|
|
|
+ using var command = cmd;
|
|
|
|
+
|
|
|
|
+ var cacheKeys = _cacheKeys;
|
|
|
|
+ var cache = _cache;
|
|
|
|
+ for (var i = 1; i < 10001; i++)
|
|
|
|
+ {
|
|
|
|
+ idParameter.TypedValue = i;
|
|
|
|
+ cache.Set<CachedWorld>(cacheKeys[i], await ReadSingleRow(cmd));
|
|
|
|
+ }
|
|
}
|
|
}
|
|
- return new ArraySegment<World>(result, 0, count);
|
|
|
|
|
|
|
|
|
|
+ Console.WriteLine("Caching Populated");
|
|
}
|
|
}
|
|
|
|
|
|
- public async Task<ArraySegment<Fortune>> LoadFortunesRows()
|
|
|
|
|
|
+ public async Task<World[]> LoadMultipleQueriesRows(int count)
|
|
{
|
|
{
|
|
- int count = 0;
|
|
|
|
- var result = GetFortuneBuffer();
|
|
|
|
- using (var db = new NpgsqlConnection(_connectionString))
|
|
|
|
|
|
+ var results = new World[count];
|
|
|
|
+
|
|
|
|
+ using (var connection = (NpgsqlConnection)_dbProviderFactory.CreateConnection())
|
|
{
|
|
{
|
|
- await db.OpenAsync();
|
|
|
|
- FortuneCommand.Connection = db;
|
|
|
|
- using (var rdr = await FortuneCommand.ExecuteReaderAsync())
|
|
|
|
|
|
+ connection.ConnectionString = _connectionString;
|
|
|
|
+ await connection.OpenAsync();
|
|
|
|
+
|
|
|
|
+ using var batch = new NpgsqlBatch(connection)
|
|
|
|
+ {
|
|
|
|
+ // Inserts a PG Sync message between each statement in the batch, required for compliance with
|
|
|
|
+ // TechEmpower general test requirement 7
|
|
|
|
+ // https://github.com/TechEmpower/FrameworkBenchmarks/wiki/Project-Information-Framework-Tests-Overview
|
|
|
|
+ EnableErrorBarriers = true
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ for (var i = 0; i < count; i++)
|
|
{
|
|
{
|
|
- while (await rdr.ReadAsync())
|
|
|
|
|
|
+ batch.BatchCommands.Add(new()
|
|
{
|
|
{
|
|
- result[count] = (new Fortune
|
|
|
|
- {
|
|
|
|
- Id = rdr.GetInt32(0),
|
|
|
|
- Message = rdr.GetString(1)
|
|
|
|
- });
|
|
|
|
- count++;
|
|
|
|
- }
|
|
|
|
|
|
+ CommandText = "SELECT id, randomnumber FROM world WHERE id = $1",
|
|
|
|
+ Parameters = { new NpgsqlParameter<int> { TypedValue = _random.Next(1, 10001) } }
|
|
|
|
+ });
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ using var reader = await batch.ExecuteReaderAsync();
|
|
|
|
+
|
|
|
|
+ for (var i = 0; i < count; i++)
|
|
|
|
+ {
|
|
|
|
+ await reader.ReadAsync();
|
|
|
|
+ results[i] = new World { Id = reader.GetInt32(0), RandomNumber = reader.GetInt32(1) };
|
|
|
|
+ await reader.NextResultAsync();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
- result[count] = (new Fortune { Message = "Additional fortune added at request time." });
|
|
|
|
- count++;
|
|
|
|
- Array.Sort<Fortune>(result, 0, count);
|
|
|
|
- return new ArraySegment<Fortune>(result, 0, count);
|
|
|
|
|
|
+ return results;
|
|
}
|
|
}
|
|
|
|
+
|
|
public async Task<World[]> LoadMultipleUpdatesRows(int count)
|
|
public async Task<World[]> LoadMultipleUpdatesRows(int count)
|
|
{
|
|
{
|
|
- using (var db = new NpgsqlConnection(_connectionString))
|
|
|
|
|
|
+ var results = new World[count];
|
|
|
|
+
|
|
|
|
+ using (var connection = (NpgsqlConnection)_dbProviderFactory.CreateConnection())
|
|
{
|
|
{
|
|
- await db.OpenAsync();
|
|
|
|
- var updateCmd = UpdateCommandsCached.PopCommand(count);
|
|
|
|
- try
|
|
|
|
|
|
+ connection.ConnectionString = _connectionString;
|
|
|
|
+ await connection.OpenAsync();
|
|
|
|
+ var (queryCmd, queryParameter) = CreateReadCommand(connection);
|
|
|
|
+ using (queryCmd)
|
|
{
|
|
{
|
|
- var command = updateCmd.Command;
|
|
|
|
- command.Connection = db;
|
|
|
|
- SingleCommand.Connection = db;
|
|
|
|
- mID.TypedValue = _random.Next(1, int.MaxValue) % 10000 + 1;
|
|
|
|
- var results = new World[count];
|
|
|
|
- for (int i = 0; i < count; i++)
|
|
|
|
|
|
+ for (var i = 0; i < results.Length; i++)
|
|
{
|
|
{
|
|
- results[i] = await ReadSingleRow(db, SingleCommand);
|
|
|
|
- mID.TypedValue = _random.Next(1, int.MaxValue) % 10000 + 1;
|
|
|
|
|
|
+ results[i] = await ReadSingleRow(queryCmd);
|
|
|
|
+ queryParameter.TypedValue = _random.Next(1, 10001);
|
|
}
|
|
}
|
|
|
|
+ }
|
|
|
|
|
|
- for (int i = 0; i < count; i++)
|
|
|
|
|
|
+ using (var updateCmd = new NpgsqlCommand(BatchUpdateString.Query(count), connection))
|
|
|
|
+ {
|
|
|
|
+ for (var i = 0; i < results.Length; i++)
|
|
{
|
|
{
|
|
- var randomNumber = _random.Next(1, int.MaxValue) % 10000 + 1;
|
|
|
|
- updateCmd.Parameters[i * 2].TypedValue = results[i].Id;
|
|
|
|
- updateCmd.Parameters[i * 2 + 1].TypedValue = randomNumber;
|
|
|
|
- //updateCmd.Parameters[i * 2].Value = results[i].Id;
|
|
|
|
- //updateCmd.Parameters[i * 2 + 1].Value = randomNumber;
|
|
|
|
|
|
+ var randomNumber = _random.Next(1, 10001);
|
|
|
|
+
|
|
|
|
+ updateCmd.Parameters.Add(new NpgsqlParameter<int> { TypedValue = results[i].Id });
|
|
|
|
+ updateCmd.Parameters.Add(new NpgsqlParameter<int> { TypedValue = randomNumber });
|
|
|
|
+
|
|
results[i].RandomNumber = randomNumber;
|
|
results[i].RandomNumber = randomNumber;
|
|
}
|
|
}
|
|
|
|
|
|
- await command.ExecuteNonQueryAsync();
|
|
|
|
- return results;
|
|
|
|
- }
|
|
|
|
- catch (Exception e_)
|
|
|
|
- {
|
|
|
|
- throw e_;
|
|
|
|
- }
|
|
|
|
- finally
|
|
|
|
- {
|
|
|
|
- UpdateCommandsCached.PushCommand(count, updateCmd);
|
|
|
|
|
|
+ await updateCmd.ExecuteNonQueryAsync();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
- }
|
|
|
|
- }
|
|
|
|
|
|
|
|
|
|
+ return results;
|
|
|
|
+ }
|
|
|
|
|
|
- public sealed class CacheKey : IEquatable<CacheKey>
|
|
|
|
- {
|
|
|
|
- private readonly int _value;
|
|
|
|
|
|
+ public async Task<List<Fortune>> LoadFortunesRows()
|
|
|
|
+ {
|
|
|
|
+ // Benchmark requirements explicitly prohibit pre-initializing the list size
|
|
|
|
+ var result = new List<Fortune>();
|
|
|
|
|
|
- public CacheKey(int value)
|
|
|
|
- => _value = value;
|
|
|
|
|
|
+ using (var connection = (NpgsqlConnection)_dbProviderFactory.CreateConnection())
|
|
|
|
+ {
|
|
|
|
+ connection.ConnectionString = _connectionString;
|
|
|
|
+ await connection.OpenAsync();
|
|
|
|
|
|
- public bool Equals(CacheKey key)
|
|
|
|
- => key._value == _value;
|
|
|
|
|
|
+ using (var cmd = new NpgsqlCommand("SELECT id, message FROM fortune", connection))
|
|
|
|
+ {
|
|
|
|
|
|
- public override bool Equals(object obj)
|
|
|
|
- => ReferenceEquals(obj, this);
|
|
|
|
|
|
+ using (var rdr = await cmd.ExecuteReaderAsync())
|
|
|
|
+ {
|
|
|
|
|
|
- public override int GetHashCode()
|
|
|
|
- => _value;
|
|
|
|
|
|
+ while (await rdr.ReadAsync())
|
|
|
|
+ {
|
|
|
|
+ result.Add(new Fortune
|
|
|
|
+ {
|
|
|
|
+ Id = rdr.GetInt32(0),
|
|
|
|
+ Message = rdr.GetString(1)
|
|
|
|
+ });
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ result.Add(new Fortune { Message = "Additional fortune added at request time." });
|
|
|
|
+ result.Sort();
|
|
|
|
|
|
- public override string ToString()
|
|
|
|
- => _value.ToString();
|
|
|
|
- }
|
|
|
|
|
|
+ return result;
|
|
|
|
+ }
|
|
|
|
|
|
- internal class UpdateCommandsCached
|
|
|
|
- {
|
|
|
|
- private static System.Collections.Concurrent.ConcurrentStack<CommandCacheItem>[] mCacheTable
|
|
|
|
- = new System.Collections.Concurrent.ConcurrentStack<CommandCacheItem>[1024];
|
|
|
|
|
|
|
|
- public static string[] IDParamereNames = new string[1024];
|
|
|
|
|
|
+ private (NpgsqlCommand readCmd, NpgsqlParameter<int> idParameter) CreateReadCommand(NpgsqlConnection connection)
|
|
|
|
+ {
|
|
|
|
+ var cmd = new NpgsqlCommand("SELECT id, randomnumber FROM world WHERE id = $1", connection);
|
|
|
|
+ var parameter = new NpgsqlParameter<int> { TypedValue = _random.Next(1, 10001) };
|
|
|
|
|
|
- public static string[] RandomParamereNames = new string[1024];
|
|
|
|
|
|
+ cmd.Parameters.Add(parameter);
|
|
|
|
|
|
- static UpdateCommandsCached()
|
|
|
|
- {
|
|
|
|
- for (int i = 0; i < 1024; i++)
|
|
|
|
- {
|
|
|
|
- IDParamereNames[i] = $"@Id_{i}";
|
|
|
|
- RandomParamereNames[i] = $"@Random_{i}";
|
|
|
|
- mCacheTable[i] = new System.Collections.Concurrent.ConcurrentStack<CommandCacheItem>();
|
|
|
|
- }
|
|
|
|
|
|
+ return (cmd, parameter);
|
|
}
|
|
}
|
|
|
|
|
|
- private static CommandCacheItem CreatCommand(int count)
|
|
|
|
|
|
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
|
|
+ private static async Task<World> ReadSingleRow(NpgsqlCommand cmd)
|
|
{
|
|
{
|
|
- CommandCacheItem item = new CommandCacheItem();
|
|
|
|
- NpgsqlCommand cmd = new Npgsql.NpgsqlCommand();
|
|
|
|
- cmd.CommandText = BatchUpdateString.Query(count);
|
|
|
|
- for (int i = 0; i < count; i++)
|
|
|
|
- {
|
|
|
|
- var id = new NpgsqlParameter<int>();
|
|
|
|
- id.ParameterName = IDParamereNames[i];
|
|
|
|
- cmd.Parameters.Add(id);
|
|
|
|
- item.Parameters.Add(id);
|
|
|
|
-
|
|
|
|
- var random = new NpgsqlParameter<int>();
|
|
|
|
- random.ParameterName = RandomParamereNames[i];
|
|
|
|
- cmd.Parameters.Add(random);
|
|
|
|
- item.Parameters.Add(random);
|
|
|
|
- }
|
|
|
|
- item.Command = cmd;
|
|
|
|
- return item;
|
|
|
|
|
|
+ using var rdr = await cmd.ExecuteReaderAsync(System.Data.CommandBehavior.SingleRow);
|
|
|
|
+ await rdr.ReadAsync();
|
|
|
|
|
|
|
|
+ return new World
|
|
|
|
+ {
|
|
|
|
+ Id = rdr.GetInt32(0),
|
|
|
|
+ RandomNumber = rdr.GetInt32(1)
|
|
|
|
+ };
|
|
}
|
|
}
|
|
|
|
|
|
- public static void PushCommand(int count, CommandCacheItem cmd)
|
|
|
|
- {
|
|
|
|
- mCacheTable[count].Push(cmd);
|
|
|
|
- }
|
|
|
|
|
|
+ private static readonly object[] _cacheKeys = Enumerable.Range(0, 10001).Select((i) => new CacheKey(i)).ToArray();
|
|
|
|
|
|
- public static CommandCacheItem PopCommand(int count)
|
|
|
|
|
|
+ public sealed class CacheKey : IEquatable<CacheKey>
|
|
{
|
|
{
|
|
- if (mCacheTable[count].TryPop(out CommandCacheItem cmd))
|
|
|
|
- return cmd;
|
|
|
|
- return CreatCommand(count);
|
|
|
|
- }
|
|
|
|
|
|
+ private readonly int _value;
|
|
|
|
|
|
- private static bool mInited = false;
|
|
|
|
|
|
+ public CacheKey(int value)
|
|
|
|
+ => _value = value;
|
|
|
|
|
|
- public static void Init()
|
|
|
|
- {
|
|
|
|
- if (mInited)
|
|
|
|
- return;
|
|
|
|
- lock (typeof(UpdateCommandsCached))
|
|
|
|
- {
|
|
|
|
- if (mInited)
|
|
|
|
- return;
|
|
|
|
- for (int i = 1; i <= 500; i++)
|
|
|
|
- {
|
|
|
|
- for (int k = 0; k < 10; k++)
|
|
|
|
- {
|
|
|
|
- var cmd = CreatCommand(i);
|
|
|
|
- mCacheTable[i].Push(cmd);
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- mInited = true;
|
|
|
|
- return;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
|
|
+ public bool Equals(CacheKey key)
|
|
|
|
+ => key._value == _value;
|
|
|
|
|
|
- class CommandCacheItem
|
|
|
|
- {
|
|
|
|
- public Npgsql.NpgsqlCommand Command { get; set; }
|
|
|
|
|
|
+ public override bool Equals(object obj)
|
|
|
|
+ => ReferenceEquals(obj, this);
|
|
|
|
|
|
- public List<Npgsql.NpgsqlParameter<int>> Parameters { get; private set; } = new List<Npgsql.NpgsqlParameter<int>>(1024);
|
|
|
|
|
|
+ public override int GetHashCode()
|
|
|
|
+ => _value;
|
|
|
|
+
|
|
|
|
+ public override string ToString()
|
|
|
|
+ => _value.ToString();
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
- internal class BatchUpdateString
|
|
|
|
|
|
+ internal sealed class BatchUpdateString
|
|
{
|
|
{
|
|
private const int MaxBatch = 500;
|
|
private const int MaxBatch = 500;
|
|
|
|
|
|
|
|
+ internal static readonly string[] ParamNames = Enumerable.Range(0, MaxBatch * 2).Select(i => $"@p{i}").ToArray();
|
|
|
|
+
|
|
private static string[] _queries = new string[MaxBatch + 1];
|
|
private static string[] _queries = new string[MaxBatch + 1];
|
|
|
|
|
|
public static string Query(int batchSize)
|
|
public static string Query(int batchSize)
|
|
|
|
+ => _queries[batchSize] is null
|
|
|
|
+ ? CreateBatch(batchSize)
|
|
|
|
+ : _queries[batchSize];
|
|
|
|
+
|
|
|
|
+ private static string CreateBatch(int batchSize)
|
|
{
|
|
{
|
|
- if (_queries[batchSize] != null)
|
|
|
|
|
|
+ var sb = StringBuilderCache.Acquire();
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ sb.Append("UPDATE world SET randomNumber = temp.randomNumber FROM (VALUES ");
|
|
|
|
+ var c = 1;
|
|
|
|
+ for (var i = 0; i < batchSize; i++)
|
|
{
|
|
{
|
|
- return _queries[batchSize];
|
|
|
|
|
|
+ if (i > 0)
|
|
|
|
+ sb.Append(", ");
|
|
|
|
+ sb.Append($"(${c++}, ${c++})");
|
|
}
|
|
}
|
|
|
|
+ sb.Append(" ORDER BY 1) AS temp(id, randomNumber) WHERE temp.id = world.id");
|
|
|
|
+
|
|
|
|
|
|
- var lastIndex = batchSize - 1;
|
|
|
|
- var sb = StringBuilderCache.Acquire();
|
|
|
|
- sb.Append("UPDATE world SET randomNumber = temp.randomNumber FROM (VALUES ");
|
|
|
|
- Enumerable.Range(0, lastIndex).ToList().ForEach(i => sb.Append($"(@Id_{i}, @Random_{i}), "));
|
|
|
|
- sb.Append($"(@Id_{lastIndex}, @Random_{lastIndex}) ORDER BY 1) AS temp(id, randomNumber) WHERE temp.id = world.id");
|
|
|
|
return _queries[batchSize] = StringBuilderCache.GetStringAndRelease(sb);
|
|
return _queries[batchSize] = StringBuilderCache.GetStringAndRelease(sb);
|
|
}
|
|
}
|
|
}
|
|
}
|