RawDb.cs 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262
  1. // Copyright (c) .NET Foundation. All rights reserved.
  2. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
  3. using System;
  4. using System.Collections.Generic;
  5. using System.Linq;
  6. using System.Runtime.CompilerServices;
  7. using System.Threading.Tasks;
  8. using Microsoft.Extensions.Caching.Memory;
  9. using Npgsql;
  10. namespace PlatformBenchmarks
  11. {
  12. public class RawDb
  13. {
  14. private readonly ConcurrentRandom _random;
  15. private readonly string _connectionString;
  16. private readonly MemoryCache _cache = new MemoryCache(
  17. new MemoryCacheOptions()
  18. {
  19. ExpirationScanFrequency = TimeSpan.FromMinutes(60)
  20. });
  21. public RawDb(ConcurrentRandom random, AppSettings appSettings)
  22. {
  23. _random = random;
  24. _connectionString = appSettings.ConnectionString;
  25. }
  26. public async Task<World> GetRow()
  27. {
  28. using (var db = new NpgsqlConnection(_connectionString))
  29. {
  30. await db.OpenAsync();
  31. var (cmd, _) = CreateReadCommand(db);
  32. using (cmd)
  33. {
  34. return await ReadSingleRow(cmd);
  35. }
  36. }
  37. }
  38. public async Task<World[]> GetRows(int count)
  39. {
  40. var result = new World[count];
  41. using (var db = new NpgsqlConnection(_connectionString))
  42. {
  43. await db.OpenAsync();
  44. var (cmd, idParameter) = CreateReadCommand(db);
  45. using (cmd)
  46. {
  47. for (int i = 0; i < result.Length; i++)
  48. {
  49. result[i] = await ReadSingleRow(cmd);
  50. idParameter.TypedValue = _random.Next(1, 10001);
  51. }
  52. }
  53. }
  54. return result;
  55. }
  56. public Task<World[]> GetCachedRows(int count)
  57. {
  58. var result = new World[count];
  59. var cacheKeys = _cacheKeys;
  60. var cache = _cache;
  61. var random = _random;
  62. for (var i = 0; i < result.Length; i++)
  63. {
  64. var id = random.Next(1, 10001);
  65. var key = cacheKeys[id];
  66. var data = cache.Get<CachedWorld>(key);
  67. if (data != null)
  68. {
  69. result[i] = data;
  70. }
  71. else
  72. {
  73. return GetUncachedRows(id, i, count, this, result);
  74. }
  75. }
  76. return Task.FromResult(result);
  77. static async Task<World[]> GetUncachedRows(int id, int i, int count, RawDb rawdb, World[] result)
  78. {
  79. using (var db = new NpgsqlConnection(rawdb._connectionString))
  80. {
  81. await db.OpenAsync();
  82. var (cmd, idParameter) = rawdb.CreateReadCommand(db);
  83. using (cmd)
  84. {
  85. Func<ICacheEntry, Task<CachedWorld>> create = async (entry) =>
  86. {
  87. return await rawdb.ReadSingleRow(cmd);
  88. };
  89. var cacheKeys = _cacheKeys;
  90. var key = cacheKeys[id];
  91. idParameter.TypedValue = id;
  92. for (; i < result.Length; i++)
  93. {
  94. var data = await rawdb._cache.GetOrCreateAsync<CachedWorld>(key, create);
  95. result[i] = data;
  96. id = rawdb._random.Next(1, 10001);
  97. idParameter.TypedValue = id;
  98. key = cacheKeys[id];
  99. }
  100. }
  101. }
  102. return result;
  103. }
  104. }
  105. public async Task PopulateCache()
  106. {
  107. using (var db = new NpgsqlConnection(_connectionString))
  108. {
  109. await db.OpenAsync();
  110. var (cmd, idParameter) = CreateReadCommand(db);
  111. using (cmd)
  112. {
  113. var cacheKeys = _cacheKeys;
  114. var cache = _cache;
  115. for (var i = 1; i < 10001; i++)
  116. {
  117. idParameter.TypedValue = i;
  118. cache.Set<CachedWorld>(cacheKeys[i], await ReadSingleRow(cmd));
  119. }
  120. }
  121. }
  122. Console.WriteLine("Caching Populated");
  123. }
  124. public async Task<World[]> UpdateAndGetRows(int count)
  125. {
  126. var results = new World[count];
  127. using (var db = new NpgsqlConnection(_connectionString))
  128. {
  129. await db.OpenAsync();
  130. var (queryCmd, queryParameter) = CreateReadCommand(db);
  131. using (queryCmd)
  132. {
  133. for (int i = 0; i < results.Length; i++)
  134. {
  135. results[i] = await ReadSingleRow(queryCmd);
  136. queryParameter.TypedValue = _random.Next(1, 10001);
  137. }
  138. }
  139. using (var updateCmd = new NpgsqlCommand(BatchUpdateString.Query(count), db))
  140. {
  141. var ids = BatchUpdateString.Ids;
  142. var randoms = BatchUpdateString.Randoms;
  143. for (int i = 0; i < results.Length; i++)
  144. {
  145. var randomNumber = _random.Next(1, 10001);
  146. updateCmd.Parameters.Add(new NpgsqlParameter<int>(parameterName: ids[i], value: results[i].Id));
  147. updateCmd.Parameters.Add(new NpgsqlParameter<int>(parameterName: randoms[i], value: randomNumber));
  148. results[i].RandomNumber = randomNumber;
  149. }
  150. await updateCmd.ExecuteNonQueryAsync();
  151. }
  152. }
  153. return results;
  154. }
  155. public async Task<List<Fortune>> LoadFortunesRows()
  156. {
  157. var result = new List<Fortune>(20);
  158. using (var db = new NpgsqlConnection(_connectionString))
  159. {
  160. await db.OpenAsync();
  161. using (var cmd = new NpgsqlCommand("SELECT id, message FROM fortune", db))
  162. using (var rdr = await cmd.ExecuteReaderAsync())
  163. {
  164. while (await rdr.ReadAsync())
  165. {
  166. result.Add(new Fortune
  167. (
  168. id:rdr.GetInt32(0),
  169. message: rdr.GetString(1)
  170. ));
  171. }
  172. }
  173. }
  174. result.Add(new Fortune(id: 0, message: "Additional fortune added at request time." ));
  175. result.Sort();
  176. return result;
  177. }
  178. private (NpgsqlCommand readCmd, NpgsqlParameter<int> idParameter) CreateReadCommand(NpgsqlConnection connection)
  179. {
  180. var cmd = new NpgsqlCommand("SELECT id, randomnumber FROM world WHERE id = @Id", connection);
  181. var parameter = new NpgsqlParameter<int>(parameterName: "@Id", value: _random.Next(1, 10001));
  182. cmd.Parameters.Add(parameter);
  183. return (cmd, parameter);
  184. }
  185. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  186. private async Task<World> ReadSingleRow(NpgsqlCommand cmd)
  187. {
  188. using (var rdr = await cmd.ExecuteReaderAsync(System.Data.CommandBehavior.SingleRow))
  189. {
  190. await rdr.ReadAsync();
  191. return new World
  192. {
  193. Id = rdr.GetInt32(0),
  194. RandomNumber = rdr.GetInt32(1)
  195. };
  196. }
  197. }
  198. private static readonly object[] _cacheKeys = Enumerable.Range(0, 10001).Select((i) => new CacheKey(i)).ToArray();
  199. public sealed class CacheKey : IEquatable<CacheKey>
  200. {
  201. private readonly int _value;
  202. public CacheKey(int value)
  203. => _value = value;
  204. public bool Equals(CacheKey key)
  205. => key._value == _value;
  206. public override bool Equals(object obj)
  207. => ReferenceEquals(obj, this);
  208. public override int GetHashCode()
  209. => _value;
  210. public override string ToString()
  211. => _value.ToString();
  212. }
  213. }
  214. }