Browse Source

[aspnetcore-corert] Upgrade to .NET 6.0-RC2 (#6862)

* [aspnetcore-corert] Upgrade to .NET 6.0-RC2

* Delete unnecessary target

* Update aspcore-corert.dockerfile

* Include DB benchmarks

* Do not copy file that doesn't exist
Michal Strehovský 3 years ago
parent
commit
12b43943c6
32 changed files with 1183 additions and 252 deletions
  1. 20 0
      frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/BenchmarkApplication.Caching.cs
  2. 57 0
      frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/BenchmarkApplication.Fortunes.cs
  3. 1 41
      frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/BenchmarkApplication.HttpConnection.cs
  4. 7 5
      frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/BenchmarkApplication.Json.cs
  5. 46 0
      frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/BenchmarkApplication.MultipleQueries.cs
  6. 45 0
      frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/BenchmarkApplication.SingleQuery.cs
  7. 45 0
      frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/BenchmarkApplication.Updates.cs
  8. 10 8
      frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/BenchmarkApplication.cs
  9. 0 2
      frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/BenchmarkConfigurationHelpers.cs
  10. 12 0
      frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/Configuration/AppSettings.cs
  11. 13 0
      frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/Configuration/DatabaseServer.cs
  12. 44 0
      frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/Data/BatchUpdateString.cs
  13. 14 0
      frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/Data/CachedWorld.cs
  14. 25 0
      frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/Data/Fortune.cs
  15. 272 0
      frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/Data/Providers/RawDbMySqlConnector.cs
  16. 265 0
      frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/Data/Providers/RawDbNpgsql.cs
  17. 32 0
      frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/Data/Random.cs
  18. 15 0
      frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/Data/World.cs
  19. 15 15
      frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/PlatformBenchmarks.csproj
  20. 20 12
      frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/Program.cs
  21. 0 1
      frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/Startup.cs
  22. 59 0
      frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/StringBuilderCache.cs
  23. 0 165
      frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/Utf8Json.Generated.cs
  24. 4 0
      frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/appsettings.mysql.json
  25. 4 0
      frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/appsettings.postgresql.json
  26. 4 0
      frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/appsettings.postgresql.updates.json
  27. 51 0
      frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/npgsql.rd.xml
  28. 17 0
      frameworks/CSharp/aspnetcore-corert/aspcore-corert-ado-pg-up.dockerfile
  29. 17 0
      frameworks/CSharp/aspnetcore-corert/aspcore-corert-ado-pg.dockerfile
  30. 3 3
      frameworks/CSharp/aspnetcore-corert/aspcore-corert.dockerfile
  31. 39 0
      frameworks/CSharp/aspnetcore-corert/benchmark_config.json
  32. 27 0
      frameworks/CSharp/aspnetcore-corert/config.toml

+ 20 - 0
frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/BenchmarkApplication.Caching.cs

@@ -0,0 +1,20 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+#if DATABASE
+
+using System.IO.Pipelines;
+using System.Threading.Tasks;
+
+namespace PlatformBenchmarks
+{
+    public partial class BenchmarkApplication
+    {
+        private async Task Caching(PipeWriter pipeWriter, int count)
+        {
+            OutputMultipleQueries(pipeWriter, await Db.LoadCachedQueries(count), SerializerContext.CachedWorldArray);
+        }
+    }
+}
+
+#endif

+ 57 - 0
frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/BenchmarkApplication.Fortunes.cs

@@ -0,0 +1,57 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+#if DATABASE
+
+using System.Collections.Generic;
+using System.IO.Pipelines;
+using System.Text.Encodings.Web;
+using System.Threading.Tasks;
+
+namespace PlatformBenchmarks
+{
+    public partial class BenchmarkApplication
+    {
+        private readonly static AsciiString _fortunesPreamble =
+            _http11OK +
+            _headerServer + _crlf +
+            _headerContentTypeHtml + _crlf +
+            _headerContentLength;
+
+        private async Task Fortunes(PipeWriter pipeWriter)
+        {
+            OutputFortunes(pipeWriter, await Db.LoadFortunesRows());
+        }
+
+        private void OutputFortunes(PipeWriter pipeWriter, List<Fortune> model)
+        {
+            var writer = GetWriter(pipeWriter, sizeHint: 1600); // in reality it's 1361
+
+            writer.Write(_fortunesPreamble);
+
+            var lengthWriter = writer;
+            writer.Write(_contentLengthGap);
+
+            // Date header
+            writer.Write(DateHeader.HeaderBytes);
+
+            var bodyStart = writer.Buffered;
+            // Body
+            writer.Write(_fortunesTableStart);
+            foreach (var item in model)
+            {
+                writer.Write(_fortunesRowStart);
+                writer.WriteNumeric((uint)item.Id);
+                writer.Write(_fortunesColumn);
+                writer.WriteUtf8String(HtmlEncoder.Encode(item.Message));
+                writer.Write(_fortunesRowEnd);
+            }
+            writer.Write(_fortunesTableEnd);
+            lengthWriter.WriteNumeric((uint)(writer.Buffered - bodyStart));
+
+            writer.Commit();
+        }
+    }
+}
+
+#endif

+ 1 - 41
frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/BenchmarkApplication.HttpConnection.cs

@@ -112,21 +112,10 @@ namespace PlatformBenchmarks
 
 
             if (state == State.StartLine)
             if (state == State.StartLine)
             {
             {
-#if NET5_0
                 if (Parser.ParseRequestLine(new ParsingAdapter(this), ref reader))
                 if (Parser.ParseRequestLine(new ParsingAdapter(this), ref reader))
                 {
                 {
                     state = State.Headers;
                     state = State.Headers;
                 }
                 }
-#else
-                var unconsumedSequence = reader.Sequence.Slice(reader.Position);
-                if (Parser.ParseRequestLine(new ParsingAdapter(this), unconsumedSequence, out var consumed, out _))
-                {
-                    state = State.Headers;
-
-                    var parsedLength = unconsumedSequence.Slice(reader.Position, consumed).Length;
-                    reader.Advance(parsedLength);
-                }
-#endif
             }
             }
 
 
             if (state == State.Headers)
             if (state == State.Headers)
@@ -197,21 +186,10 @@ namespace PlatformBenchmarks
 
 
             if (state == State.StartLine)
             if (state == State.StartLine)
             {
             {
-#if NET5_0
                 if (Parser.ParseRequestLine(new ParsingAdapter(this), ref reader))
                 if (Parser.ParseRequestLine(new ParsingAdapter(this), ref reader))
                 {
                 {
                     state = State.Headers;
                     state = State.Headers;
                 }
                 }
-#else
-                var unconsumedSequence = reader.Sequence.Slice(reader.Position);
-                if (Parser.ParseRequestLine(new ParsingAdapter(this), unconsumedSequence, out var consumed, out _))
-                {
-                    state = State.Headers;
-
-                    var parsedLength = unconsumedSequence.Slice(reader.Position, consumed).Length;
-                    reader.Advance(parsedLength);
-                }
-#endif
             }
             }
 
 
             if (state == State.Headers)
             if (state == State.Headers)
@@ -246,8 +224,6 @@ namespace PlatformBenchmarks
         }
         }
 #endif
 #endif
 
 
-#if NET5_0
-
         public void OnStaticIndexedHeader(int index)
         public void OnStaticIndexedHeader(int index)
         {
         {
         }
         }
@@ -259,17 +235,10 @@ namespace PlatformBenchmarks
         public void OnHeader(ReadOnlySpan<byte> name, ReadOnlySpan<byte> value)
         public void OnHeader(ReadOnlySpan<byte> name, ReadOnlySpan<byte> value)
         {
         {
         }
         }
+
         public void OnHeadersComplete(bool endStream)
         public void OnHeadersComplete(bool endStream)
         {
         {
         }
         }
-#else
-        public void OnHeader(Span<byte> name, Span<byte> value)
-        {
-        }
-        public void OnHeadersComplete()
-        {
-        }
-#endif
 
 
         private static void ThrowUnexpectedEndOfData()
         private static void ThrowUnexpectedEndOfData()
         {
         {
@@ -311,7 +280,6 @@ namespace PlatformBenchmarks
             public ParsingAdapter(BenchmarkApplication requestHandler)
             public ParsingAdapter(BenchmarkApplication requestHandler)
                 => RequestHandler = requestHandler;
                 => RequestHandler = requestHandler;
 
 
-#if NET5_0
             public void OnStaticIndexedHeader(int index) 
             public void OnStaticIndexedHeader(int index) 
                 => RequestHandler.OnStaticIndexedHeader(index);
                 => RequestHandler.OnStaticIndexedHeader(index);
 
 
@@ -326,14 +294,6 @@ namespace PlatformBenchmarks
 
 
             public void OnStartLine(HttpVersionAndMethod versionAndMethod, TargetOffsetPathLength targetPath, Span<byte> startLine)
             public void OnStartLine(HttpVersionAndMethod versionAndMethod, TargetOffsetPathLength targetPath, Span<byte> startLine)
                 => RequestHandler.OnStartLine(versionAndMethod, targetPath, startLine);
                 => RequestHandler.OnStartLine(versionAndMethod, targetPath, startLine);
-#else
-            public void OnHeader(Span<byte> name, Span<byte> value)
-                => RequestHandler.OnHeader(name, value);
-            public void OnHeadersComplete()
-                => RequestHandler.OnHeadersComplete();
-            public void OnStartLine(HttpMethod method, HttpVersion version, Span<byte> target, Span<byte> path, Span<byte> query, Span<byte> customMethod, bool pathEncoded)
-                => RequestHandler.OnStartLine(method, version, target, path, query, customMethod, pathEncoded);
-#endif
         }
         }
     }
     }
 }
 }

+ 7 - 5
frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/BenchmarkApplication.Json.cs

@@ -1,15 +1,14 @@
 // Copyright (c) .NET Foundation. All rights reserved.
 // Copyright (c) .NET Foundation. All rights reserved.
 // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
 // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
 
 
-using System;
 using System.Buffers;
 using System.Buffers;
-using Utf8Json;
+using System.Text.Json;
 
 
 namespace PlatformBenchmarks
 namespace PlatformBenchmarks
 {
 {
     public partial class BenchmarkApplication
     public partial class BenchmarkApplication
     {
     {
-        private static readonly uint _jsonPayloadSize = (uint)JsonSerializer.SerializeUnsafe(new JsonMessage { message = "Hello, World!" }).Count;
+        private readonly static uint _jsonPayloadSize = (uint)JsonSerializer.SerializeToUtf8Bytes(new JsonMessage { message = "Hello, World!" }, SerializerContext.JsonMessage).Length;
 
 
         private readonly static AsciiString _jsonPreamble =
         private readonly static AsciiString _jsonPreamble =
             _http11OK +
             _http11OK +
@@ -26,8 +25,11 @@ namespace PlatformBenchmarks
 
 
             writer.Commit();
             writer.Commit();
 
 
-            var jsonPayload = JsonSerializer.SerializeUnsafe(new JsonMessage { message = "Hello, World!" });
-            bodyWriter.Write(jsonPayload);
+            Utf8JsonWriter utf8JsonWriter = t_writer ??= new Utf8JsonWriter(bodyWriter, new JsonWriterOptions { SkipValidation = true });
+            utf8JsonWriter.Reset(bodyWriter);
+
+            // Body
+            JsonSerializer.Serialize(utf8JsonWriter, new JsonMessage { message = "Hello, World!" }, SerializerContext.JsonMessage);        
         }
         }
     }
     }
 }
 }

+ 46 - 0
frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/BenchmarkApplication.MultipleQueries.cs

@@ -0,0 +1,46 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+#if DATABASE
+
+using System.IO.Pipelines;
+using System.Text.Json;
+using System.Text.Json.Serialization.Metadata;
+using System.Threading.Tasks;
+
+namespace PlatformBenchmarks
+{
+    public partial class BenchmarkApplication
+    {
+        private async Task MultipleQueries(PipeWriter pipeWriter, int count)
+        {
+            OutputMultipleQueries(pipeWriter, await Db.LoadMultipleQueriesRows(count), SerializerContext.WorldArray);
+        }
+
+        private static void OutputMultipleQueries<TWorld>(PipeWriter pipeWriter, TWorld[] rows, JsonTypeInfo<TWorld[]> jsonTypeInfo)
+        {
+            var writer = GetWriter(pipeWriter, sizeHint: 160 * rows.Length); // in reality it's 152 for one
+
+            writer.Write(_dbPreamble);
+
+            var lengthWriter = writer;
+            writer.Write(_contentLengthGap);
+
+            // Date header
+            writer.Write(DateHeader.HeaderBytes);
+
+            writer.Commit();
+
+            Utf8JsonWriter utf8JsonWriter = t_writer ??= new Utf8JsonWriter(pipeWriter, new JsonWriterOptions { SkipValidation = true });
+            utf8JsonWriter.Reset(pipeWriter);
+
+            // Body
+            JsonSerializer.Serialize<TWorld[]>(utf8JsonWriter, rows, jsonTypeInfo);
+
+            // Content-Length
+            lengthWriter.WriteNumeric((uint)utf8JsonWriter.BytesCommitted);
+        }
+    }
+}
+
+#endif

+ 45 - 0
frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/BenchmarkApplication.SingleQuery.cs

@@ -0,0 +1,45 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+#if DATABASE
+
+using System.IO.Pipelines;
+using System.Text.Json;
+using System.Threading.Tasks;
+
+namespace PlatformBenchmarks
+{
+    public partial class BenchmarkApplication
+    {
+        private async Task SingleQuery(PipeWriter pipeWriter)
+        {
+            OutputSingleQuery(pipeWriter, await Db.LoadSingleQueryRow());
+        }
+
+        private static void OutputSingleQuery(PipeWriter pipeWriter, World row)
+        {
+            var writer = GetWriter(pipeWriter, sizeHint: 180); // in reality it's 150
+
+            writer.Write(_dbPreamble);
+
+            var lengthWriter = writer;
+            writer.Write(_contentLengthGap);
+
+            // Date header
+            writer.Write(DateHeader.HeaderBytes);
+
+            writer.Commit();
+
+            Utf8JsonWriter utf8JsonWriter = t_writer ??= new Utf8JsonWriter(pipeWriter, new JsonWriterOptions { SkipValidation = true });
+            utf8JsonWriter.Reset(pipeWriter);
+
+            // Body
+            JsonSerializer.Serialize(utf8JsonWriter, row, SerializerContext.World);
+
+            // Content-Length
+            lengthWriter.WriteNumeric((uint)utf8JsonWriter.BytesCommitted);
+        }
+    }
+}
+
+#endif

+ 45 - 0
frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/BenchmarkApplication.Updates.cs

@@ -0,0 +1,45 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+#if DATABASE
+
+using System.IO.Pipelines;
+using System.Text.Json;
+using System.Threading.Tasks;
+
+namespace PlatformBenchmarks
+{
+    public partial class BenchmarkApplication
+    {
+        private async Task Updates(PipeWriter pipeWriter, int count)
+        {
+            OutputUpdates(pipeWriter, await Db.LoadMultipleUpdatesRows(count));
+        }
+
+        private static void OutputUpdates(PipeWriter pipeWriter, World[] rows)
+        {
+            var writer = GetWriter(pipeWriter, sizeHint: 120 * rows.Length); // in reality it's 112 for one
+
+            writer.Write(_dbPreamble);
+
+            var lengthWriter = writer;
+            writer.Write(_contentLengthGap);
+
+            // Date header
+            writer.Write(DateHeader.HeaderBytes);
+
+            writer.Commit();
+
+            Utf8JsonWriter utf8JsonWriter = t_writer ??= new Utf8JsonWriter(pipeWriter, new JsonWriterOptions { SkipValidation = true });
+            utf8JsonWriter.Reset(pipeWriter);
+
+            // Body
+            JsonSerializer.Serialize( utf8JsonWriter, rows, SerializerContext.WorldArray);
+
+            // Content-Length
+            lengthWriter.WriteNumeric((uint)utf8JsonWriter.BytesCommitted);
+        }
+    }
+}
+
+#endif

+ 10 - 8
frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/BenchmarkApplication.cs

@@ -5,6 +5,7 @@ using System;
 using System.Buffers.Text;
 using System.Buffers.Text;
 using System.IO.Pipelines;
 using System.IO.Pipelines;
 using System.Text.Json;
 using System.Text.Json;
+using System.Text.Json.Serialization;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
 
 
 using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http;
 using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http;
@@ -35,7 +36,15 @@ namespace PlatformBenchmarks
 
 
         private readonly static AsciiString _plainTextBody = "Hello, World!";
         private readonly static AsciiString _plainTextBody = "Hello, World!";
 
 
-        private static readonly JsonSerializerOptions SerializerOptions = new JsonSerializerOptions();
+        private static readonly JsonContext SerializerContext = JsonContext.Default;
+
+        [JsonSourceGenerationOptions(GenerationMode = JsonSourceGenerationMode.Serialization, PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase)]
+        [JsonSerializable(typeof(JsonMessage))]
+        [JsonSerializable(typeof(CachedWorld[]))]
+        [JsonSerializable(typeof(World[]))]
+        private sealed partial class JsonContext : JsonSerializerContext
+        {
+        }
 
 
         private readonly static AsciiString _fortunesTableStart = "<!DOCTYPE html><html><head><title>Fortunes</title></head><body><table><tr><th>id</th><th>message</th></tr>";
         private readonly static AsciiString _fortunesTableStart = "<!DOCTYPE html><html><head><title>Fortunes</title></head><body><table><tr><th>id</th><th>message</th></tr>";
         private readonly static AsciiString _fortunesRowStart = "<tr><td>";
         private readonly static AsciiString _fortunesRowStart = "<tr><td>";
@@ -65,17 +74,10 @@ namespace PlatformBenchmarks
         private RequestType _requestType;
         private RequestType _requestType;
         private int _queries;
         private int _queries;
 
 
-#if NET5_0
         public void OnStartLine(HttpVersionAndMethod versionAndMethod, TargetOffsetPathLength targetPath, Span<byte> startLine)
         public void OnStartLine(HttpVersionAndMethod versionAndMethod, TargetOffsetPathLength targetPath, Span<byte> startLine)
         {
         {
             _requestType = versionAndMethod.Method == HttpMethod.Get ? GetRequestType(startLine.Slice(targetPath.Offset, targetPath.Length), ref _queries) : RequestType.NotRecognized;
             _requestType = versionAndMethod.Method == HttpMethod.Get ? GetRequestType(startLine.Slice(targetPath.Offset, targetPath.Length), ref _queries) : RequestType.NotRecognized;
         }
         }
-#else
-        public void OnStartLine(HttpMethod method, HttpVersion version, Span<byte> target, Span<byte> path, Span<byte> query, Span<byte> customMethod, bool pathEncoded)
-        {
-            _requestType = method == HttpMethod.Get ? GetRequestType(path, ref _queries) : RequestType.NotRecognized;
-        }
-#endif
 
 
         private RequestType GetRequestType(ReadOnlySpan<byte> path, ref int queries)
         private RequestType GetRequestType(ReadOnlySpan<byte> path, ref int queries)
         {
         {

+ 0 - 2
frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/BenchmarkConfigurationHelpers.cs

@@ -24,7 +24,6 @@ namespace PlatformBenchmarks
                     options.IOQueueCount = threadCount;
                     options.IOQueueCount = threadCount;
                 }
                 }
 
 
-#if NET5_0
                 options.WaitForDataBeforeAllocatingBuffer = false;
                 options.WaitForDataBeforeAllocatingBuffer = false;
                 if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
                 if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
                 {
                 {
@@ -32,7 +31,6 @@ namespace PlatformBenchmarks
                 }
                 }
 
 
                 Console.WriteLine($"Options: WaitForData={options.WaitForDataBeforeAllocatingBuffer}, PreferInlineScheduling={options.UnsafePreferInlineScheduling}, IOQueue={options.IOQueueCount}");
                 Console.WriteLine($"Options: WaitForData={options.WaitForDataBeforeAllocatingBuffer}, PreferInlineScheduling={options.UnsafePreferInlineScheduling}, IOQueue={options.IOQueueCount}");
-#endif
             });
             });
 
 
             return builder;
             return builder;

+ 12 - 0
frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/Configuration/AppSettings.cs

@@ -0,0 +1,12 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+namespace PlatformBenchmarks
+{
+    public class AppSettings
+    {
+        public string ConnectionString { get; set; }
+
+        public DatabaseServer Database { get; set; } = DatabaseServer.None;
+    }
+}

+ 13 - 0
frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/Configuration/DatabaseServer.cs

@@ -0,0 +1,13 @@
+// Copyright (c) .NET Foundation. All rights reserved. 
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 
+
+namespace PlatformBenchmarks
+{
+    public enum DatabaseServer
+    {
+        None,
+        SqlServer,
+        PostgreSql,
+        MySql
+    }
+}

+ 44 - 0
frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/Data/BatchUpdateString.cs

@@ -0,0 +1,44 @@
+// Copyright (c) .NET Foundation. All rights reserved. 
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 
+
+using System.Linq;
+
+namespace PlatformBenchmarks
+{
+    internal class BatchUpdateString
+    {
+        private const int MaxBatch = 500;
+
+        public static DatabaseServer DatabaseServer;
+
+        internal static readonly string[] Ids = Enumerable.Range(0, MaxBatch).Select(i => $"@Id_{i}").ToArray();
+        internal static readonly string[] Randoms = Enumerable.Range(0, MaxBatch).Select(i => $"@Random_{i}").ToArray();
+
+        private static string[] _queries = new string[MaxBatch + 1];
+
+        public static string Query(int batchSize)
+        {
+            if (_queries[batchSize] != null)
+            {
+                return _queries[batchSize];
+            }
+
+            var lastIndex = batchSize - 1;
+
+            var sb = StringBuilderCache.Acquire();
+
+            if (DatabaseServer == DatabaseServer.PostgreSql)
+            {
+                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");
+            }
+            else
+            {
+                Enumerable.Range(0, batchSize).ToList().ForEach(i => sb.Append($"UPDATE world SET randomnumber = @Random_{i} WHERE id = @Id_{i};"));
+            }
+
+            return _queries[batchSize] = StringBuilderCache.GetStringAndRelease(sb);
+        }
+    }
+}

+ 14 - 0
frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/Data/CachedWorld.cs

@@ -0,0 +1,14 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+namespace PlatformBenchmarks
+{
+    public sealed class CachedWorld
+    {
+        public int Id { get; set; }
+
+        public int RandomNumber { get; set; }
+
+        public static implicit operator CachedWorld(World world) => new CachedWorld { Id = world.Id, RandomNumber = world.RandomNumber };
+    }
+}

+ 25 - 0
frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/Data/Fortune.cs

@@ -0,0 +1,25 @@
+// Copyright (c) .NET Foundation. All rights reserved. 
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 
+
+using System;
+
+namespace PlatformBenchmarks
+{
+    public readonly struct Fortune : IComparable<Fortune>, IComparable
+    {
+        public Fortune(int id, string message)
+        {
+            Id = id;
+            Message = message;
+        }
+
+        public int Id { get; }
+
+        public string Message { get; }
+
+        public int CompareTo(object obj) => throw new InvalidOperationException("The non-generic CompareTo should not be used");
+
+        // Performance critical, using culture insensitive comparison
+        public int CompareTo(Fortune other) => string.CompareOrdinal(Message, other.Message);
+    }
+}

+ 272 - 0
frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/Data/Providers/RawDbMySqlConnector.cs

@@ -0,0 +1,272 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+#if MYSQLCONNECTOR
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.CompilerServices;
+using System.Threading.Tasks;
+using Microsoft.Extensions.Caching.Memory;
+using MySqlConnector;
+
+namespace PlatformBenchmarks
+{
+    // Is semantically identical to RawDbNpgsql.cs.
+    // If you are changing RawDbMySqlConnector.cs, also consider changing RawDbNpgsql.cs.
+    public class RawDb
+    {
+        private readonly ConcurrentRandom _random;
+        private readonly string _connectionString;
+        private readonly MemoryCache _cache = new MemoryCache(
+            new MemoryCacheOptions()
+            {
+                ExpirationScanFrequency = TimeSpan.FromMinutes(60)
+            });
+
+        public RawDb(ConcurrentRandom random, AppSettings appSettings)
+        {
+            _random = random;
+            _connectionString = appSettings.ConnectionString;
+        }
+
+        public async Task<World> LoadSingleQueryRow()
+        {
+            using (var db = new MySqlConnection(_connectionString))
+            {
+                await db.OpenAsync();
+
+                var (cmd, _) = await CreateReadCommandAsync(db);
+                using (cmd)
+                {
+                    return await ReadSingleRow(cmd);
+                }
+            }
+        }
+
+        public async Task<World[]> LoadMultipleQueriesRows(int count)
+        {
+            var result = new World[count];
+
+            using (var db = new MySqlConnection(_connectionString))
+            {
+                await db.OpenAsync();
+
+                var (cmd, idParameter) = await CreateReadCommandAsync(db);
+                using (cmd)
+                {
+                    for (int i = 0; i < result.Length; i++)
+                    {
+                        result[i] = await ReadSingleRow(cmd);
+                        idParameter.Value = _random.Next(1, 10001);
+                    }
+                }
+            }
+
+            return result;
+        }
+
+        public Task<CachedWorld[]> LoadCachedQueries(int count)
+        {
+            var result = new CachedWorld[count];
+            var cacheKeys = _cacheKeys;
+            var cache = _cache;
+            var random = _random;
+            for (var i = 0; i < result.Length; i++)
+            {
+                var id = random.Next(1, 10001);
+                var key = cacheKeys[id];
+                if (cache.TryGetValue(key, out object cached))
+                {
+                    result[i] = (CachedWorld)cached;
+                }
+                else
+                {
+                    return LoadUncachedQueries(id, i, count, this, result);
+                }
+            }
+
+            return Task.FromResult(result);
+
+            static async Task<CachedWorld[]> LoadUncachedQueries(int id, int i, int count, RawDb rawdb, CachedWorld[] result)
+            {
+                using (var db = new MySqlConnection(rawdb._connectionString))
+                {
+                    await db.OpenAsync();
+
+                    var (cmd, idParameter) = await rawdb.CreateReadCommandAsync(db);
+                    using (cmd)
+                    {
+                        Func<ICacheEntry, Task<CachedWorld>> create = async (entry) =>
+                        {
+                            return await rawdb.ReadSingleRow(cmd);
+                        };
+
+                        var cacheKeys = _cacheKeys;
+                        var key = cacheKeys[id];
+
+                        idParameter.Value = id;
+
+                        for (; i < result.Length; i++)
+                        {
+                            result[i] = await rawdb._cache.GetOrCreateAsync<CachedWorld>(key, create);
+
+                            id = rawdb._random.Next(1, 10001);
+                            idParameter.Value = id;
+                            key = cacheKeys[id];
+                        }
+                    }
+                }
+
+                return result;
+            }
+        }
+
+        public async Task PopulateCache()
+        {
+            using (var db = new MySqlConnection(_connectionString))
+            {
+                await db.OpenAsync();
+
+                var (cmd, idParameter) = await CreateReadCommandAsync(db);
+                using (cmd)
+                {
+                    var cacheKeys = _cacheKeys;
+                    var cache = _cache;
+                    for (var i = 1; i < 10001; i++)
+                    {
+                        idParameter.Value = i;
+                        cache.Set<CachedWorld>(cacheKeys[i], await ReadSingleRow(cmd));
+                    }
+                }
+            }
+
+            Console.WriteLine("Caching Populated");
+        }
+
+        public async Task<World[]> LoadMultipleUpdatesRows(int count)
+        {
+            var results = new World[count];
+
+            using (var db = new MySqlConnection(_connectionString))
+            {
+                await db.OpenAsync();
+
+                var (queryCmd, queryParameter) = await CreateReadCommandAsync(db);
+                using (queryCmd)
+                {
+                    for (int i = 0; i < results.Length; i++)
+                    {
+                        results[i] = await ReadSingleRow(queryCmd);
+                        queryParameter.Value = _random.Next(1, 10001);
+                    }
+                }
+
+                using (var updateCmd = new MySqlCommand(BatchUpdateString.Query(count), db))
+                {
+                    var ids = BatchUpdateString.Ids;
+                    var randoms = BatchUpdateString.Randoms;
+
+                    for (int i = 0; i < results.Length; i++)
+                    {
+                        var randomNumber = _random.Next(1, 10001);
+
+                        updateCmd.Parameters.Add(new MySqlParameter(ids[i], results[i].Id));
+                        updateCmd.Parameters.Add(new MySqlParameter(randoms[i], randomNumber));
+
+                        results[i].RandomNumber = randomNumber;
+                    }
+
+                    await updateCmd.ExecuteNonQueryAsync();
+                }
+            }
+
+            return results;
+        }
+
+        public async Task<List<Fortune>> LoadFortunesRows()
+        {
+            var result = new List<Fortune>();
+
+            using (var db = new MySqlConnection(_connectionString))
+            {
+                await db.OpenAsync();
+
+                using (var cmd = new MySqlCommand("SELECT id, message FROM fortune", db))
+                {
+                    await cmd.PrepareAsync();
+                    
+                    using (var rdr = await cmd.ExecuteReaderAsync())
+                    {
+                        while (await rdr.ReadAsync())
+                        {
+                            result.Add(
+                                new Fortune
+                                (
+                                    id: rdr.GetInt32(0),
+                                    message: rdr.GetString(1)
+                                ));
+                        }
+                    }
+                }
+            }
+
+            result.Add(new Fortune(id: 0, message: "Additional fortune added at request time." ));
+            result.Sort();
+
+            return result;
+        }
+
+        private async Task<(MySqlCommand readCmd, MySqlParameter idParameter)> CreateReadCommandAsync(MySqlConnection connection)
+        {
+            var cmd = new MySqlCommand("SELECT id, randomnumber FROM world WHERE id = @Id", connection);
+            var parameter = new MySqlParameter("@Id", _random.Next(1, 10001));
+
+            cmd.Parameters.Add(parameter);
+
+            await cmd.PrepareAsync();
+            
+            return (cmd, parameter);
+        }
+        
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        private async Task<World> ReadSingleRow(MySqlCommand cmd)
+        {
+            using (var rdr = await cmd.ExecuteReaderAsync(System.Data.CommandBehavior.SingleRow))
+            {
+                await rdr.ReadAsync();
+
+                return new World
+                {
+                    Id = rdr.GetInt32(0),
+                    RandomNumber = rdr.GetInt32(1)
+                };
+            }
+        }
+        
+        private static readonly object[] _cacheKeys = Enumerable.Range(0, 10001).Select((i) => new CacheKey(i)).ToArray();
+
+        public sealed class CacheKey : IEquatable<CacheKey>
+        {
+            private readonly int _value;
+
+            public CacheKey(int value)
+                => _value = value;
+
+            public bool Equals(CacheKey key)
+                => key._value == _value;
+
+            public override bool Equals(object obj)
+                => ReferenceEquals(obj, this);
+
+            public override int GetHashCode()
+                => _value;
+
+            public override string ToString()
+                => _value.ToString();
+        }
+    }
+}
+
+#endif

+ 265 - 0
frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/Data/Providers/RawDbNpgsql.cs

@@ -0,0 +1,265 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+#if NPGSQL
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.CompilerServices;
+using System.Threading.Tasks;
+using Microsoft.Extensions.Caching.Memory;
+using Npgsql;
+
+namespace PlatformBenchmarks
+{
+    // Is semantically identical to RawDbMySqlConnector.cs.
+    // If you are changing RawDbNpgsql.cs, also consider changing RawDbMySqlConnector.cs.
+    public class RawDb
+    {
+        private readonly ConcurrentRandom _random;
+        private readonly string _connectionString;
+        private readonly MemoryCache _cache = new MemoryCache(
+            new MemoryCacheOptions()
+            {
+                ExpirationScanFrequency = TimeSpan.FromMinutes(60)
+            });
+
+        public RawDb(ConcurrentRandom random, AppSettings appSettings)
+        {
+            _random = random;
+            _connectionString = appSettings.ConnectionString;
+        }
+
+        public async Task<World> LoadSingleQueryRow()
+        {
+            using (var db = new NpgsqlConnection(_connectionString))
+            {
+                await db.OpenAsync();
+
+                var (cmd, _) = CreateReadCommand(db);
+                using (cmd)
+                {
+                    return await ReadSingleRow(cmd);
+                }
+            }
+        }
+
+        public async Task<World[]> LoadMultipleQueriesRows(int count)
+        {
+            var result = new World[count];
+
+            using (var db = new NpgsqlConnection(_connectionString))
+            {
+                await db.OpenAsync();
+
+                var (cmd, idParameter) = CreateReadCommand(db);
+                using (cmd)
+                {
+                    for (int i = 0; i < result.Length; i++)
+                    {
+                        result[i] = await ReadSingleRow(cmd);
+                        idParameter.TypedValue = _random.Next(1, 10001);
+                    }
+                }
+            }
+
+            return result;
+        }
+
+        public Task<CachedWorld[]> LoadCachedQueries(int count)
+        {
+            var result = new CachedWorld[count];
+            var cacheKeys = _cacheKeys;
+            var cache = _cache;
+            var random = _random;
+            for (var i = 0; i < result.Length; i++)
+            {
+                var id = random.Next(1, 10001);
+                var key = cacheKeys[id];
+                if (cache.TryGetValue(key, out object cached))
+                {
+                    result[i] = (CachedWorld)cached;
+                }
+                else
+                {
+                    return LoadUncachedQueries(id, i, count, this, result);
+                }
+            }
+
+            return Task.FromResult(result);
+
+            static async Task<CachedWorld[]> LoadUncachedQueries(int id, int i, int count, RawDb rawdb, CachedWorld[] result)
+            {
+                using (var db = new NpgsqlConnection(rawdb._connectionString))
+                {
+                    await db.OpenAsync();
+
+                    var (cmd, idParameter) = rawdb.CreateReadCommand(db);
+                    using (cmd)
+                    {
+                        Func<ICacheEntry, Task<CachedWorld>> create = async (entry) =>
+                        {
+                            return await rawdb.ReadSingleRow(cmd);
+                        };
+
+                        var cacheKeys = _cacheKeys;
+                        var key = cacheKeys[id];
+
+                        idParameter.TypedValue = id;
+
+                        for (; i < result.Length; i++)
+                        {
+                            result[i] = await rawdb._cache.GetOrCreateAsync<CachedWorld>(key, create);
+
+                            id = rawdb._random.Next(1, 10001);
+                            idParameter.TypedValue = id;
+                            key = cacheKeys[id];
+                        }
+                    }
+                }
+
+                return result;
+            }
+        }
+
+        public async Task PopulateCache()
+        {
+            using (var db = new NpgsqlConnection(_connectionString))
+            {
+                await db.OpenAsync();
+
+                var (cmd, idParameter) = CreateReadCommand(db);
+                using (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));
+                    }
+                }
+            }
+
+            Console.WriteLine("Caching Populated");
+        }
+
+        public async Task<World[]> LoadMultipleUpdatesRows(int count)
+        {
+            var results = new World[count];
+
+            using (var db = new NpgsqlConnection(_connectionString))
+            {
+                await db.OpenAsync();
+
+                var (queryCmd, queryParameter) = CreateReadCommand(db);
+                using (queryCmd)
+                {
+                    for (int i = 0; i < results.Length; i++)
+                    {
+                        results[i] = await ReadSingleRow(queryCmd);
+                        queryParameter.TypedValue = _random.Next(1, 10001);
+                    }
+                }
+
+                using (var updateCmd = new NpgsqlCommand(BatchUpdateString.Query(count), db))
+                {
+                    var ids = BatchUpdateString.Ids;
+                    var randoms = BatchUpdateString.Randoms;
+
+                    for (int i = 0; i < results.Length; i++)
+                    {
+                        var randomNumber = _random.Next(1, 10001);
+
+                        updateCmd.Parameters.Add(new NpgsqlParameter<int>(parameterName: ids[i], value: results[i].Id));
+                        updateCmd.Parameters.Add(new NpgsqlParameter<int>(parameterName: randoms[i], value: randomNumber));
+
+                        results[i].RandomNumber = randomNumber;
+                    }
+
+                    await updateCmd.ExecuteNonQueryAsync();
+                }
+            }
+
+            return results;
+        }
+
+        public async Task<List<Fortune>> LoadFortunesRows()
+        {
+            var result = new List<Fortune>(20);
+
+            using (var db = new NpgsqlConnection(_connectionString))
+            {
+                await db.OpenAsync();
+
+                using (var cmd = new NpgsqlCommand("SELECT id, message FROM fortune", db))
+                using (var rdr = await cmd.ExecuteReaderAsync())
+                {
+                    while (await rdr.ReadAsync())
+                    {
+                        result.Add(new Fortune
+                        (
+                            id:rdr.GetInt32(0),
+                            message: rdr.GetString(1)
+                        ));
+                    }
+                }
+            }
+
+            result.Add(new Fortune(id: 0, message: "Additional fortune added at request time." ));
+            result.Sort();
+
+            return result;
+        }
+
+        private (NpgsqlCommand readCmd, NpgsqlParameter<int> idParameter) CreateReadCommand(NpgsqlConnection connection)
+        {
+            var cmd = new NpgsqlCommand("SELECT id, randomnumber FROM world WHERE id = @Id", connection);
+            var parameter = new NpgsqlParameter<int>(parameterName: "@Id", value: _random.Next(1, 10001));
+
+            cmd.Parameters.Add(parameter);
+
+            return (cmd, parameter);
+        }
+
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        private async Task<World> ReadSingleRow(NpgsqlCommand cmd)
+        {
+            using (var rdr = await cmd.ExecuteReaderAsync(System.Data.CommandBehavior.SingleRow))
+            {
+                await rdr.ReadAsync();
+
+                return new World
+                {
+                    Id = rdr.GetInt32(0),
+                    RandomNumber = rdr.GetInt32(1)
+                };
+            }
+        }
+
+        private static readonly object[] _cacheKeys = Enumerable.Range(0, 10001).Select((i) => new CacheKey(i)).ToArray();
+
+        public sealed class CacheKey : IEquatable<CacheKey>
+        {
+            private readonly int _value;
+
+            public CacheKey(int value)
+                => _value = value;
+
+            public bool Equals(CacheKey key)
+                => key._value == _value;
+
+            public override bool Equals(object obj)
+                => ReferenceEquals(obj, this);
+
+            public override int GetHashCode()
+                => _value;
+
+            public override string ToString()
+                => _value.ToString();
+        }
+    }
+}
+
+#endif

+ 32 - 0
frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/Data/Random.cs

@@ -0,0 +1,32 @@
+// Copyright (c) .NET Foundation. All rights reserved. 
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 
+
+using System;
+using System.Runtime.CompilerServices;
+using System.Threading;
+
+namespace PlatformBenchmarks
+{
+    public class ConcurrentRandom
+    {
+        private static int nextSeed = 0;
+
+        // Random isn't thread safe
+        [ThreadStatic]
+        private static Random _random;
+
+        private static Random Random => _random ?? CreateRandom();
+
+        [MethodImpl(MethodImplOptions.NoInlining)]
+        private static Random CreateRandom()
+        {
+            _random = new Random(Interlocked.Increment(ref nextSeed));
+            return _random;
+        }
+
+        public int Next(int minValue, int maxValue)
+        {
+            return Random.Next(minValue, maxValue);
+        }
+    }
+}

+ 15 - 0
frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/Data/World.cs

@@ -0,0 +1,15 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Runtime.InteropServices;
+
+namespace PlatformBenchmarks
+{
+    [StructLayout(LayoutKind.Sequential, Size = 8)]
+    public struct World
+    {
+        public int Id { get; set; }
+
+        public int RandomNumber { get; set; }
+    }
+}

+ 15 - 15
frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/PlatformBenchmarks.csproj

@@ -1,13 +1,14 @@
 <Project Sdk="Microsoft.NET.Sdk.Web">
 <Project Sdk="Microsoft.NET.Sdk.Web">
-
   <PropertyGroup>
   <PropertyGroup>
-    <TargetFramework>net5.0</TargetFramework>
+    <TargetFramework>net6.0</TargetFramework>
     <OutputType>Exe</OutputType>
     <OutputType>Exe</OutputType>
     <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
     <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
 
 
     <!-- Opt out of the "easy mode" of the CoreRT compiler (http://aka.ms/OptimizeCoreRT) -->
     <!-- Opt out of the "easy mode" of the CoreRT compiler (http://aka.ms/OptimizeCoreRT) -->
+    <TrimmerDefaultAction>link</TrimmerDefaultAction>
     <IlcOptimizationPreference>Speed</IlcOptimizationPreference>
     <IlcOptimizationPreference>Speed</IlcOptimizationPreference>
     <IlcPgoOptimize>true</IlcPgoOptimize>
     <IlcPgoOptimize>true</IlcPgoOptimize>
+    <IlcTrimMetadata>true</IlcTrimMetadata>
 
 
     <!-- This benchmark is marked Stripped, so we might as well do this: -->
     <!-- This benchmark is marked Stripped, so we might as well do this: -->
     <UseSystemResourceKeys>true</UseSystemResourceKeys>
     <UseSystemResourceKeys>true</UseSystemResourceKeys>
@@ -16,25 +17,24 @@
     <IlcGenerateStackTraceData>false</IlcGenerateStackTraceData>
     <IlcGenerateStackTraceData>false</IlcGenerateStackTraceData>
   </PropertyGroup>
   </PropertyGroup>
   
   
+  <PropertyGroup>
+    <DefineConstants Condition=" '$(DatabaseProvider)' != '' ">$(DefineConstants);DATABASE</DefineConstants>
+    <DefineConstants Condition=" '$(DatabaseProvider)' == 'Npgsql' ">$(DefineConstants);NPGSQL</DefineConstants>
+    <DefineConstants Condition=" '$(DatabaseProvider)' == 'MySqlConnector' ">$(DefineConstants);MYSQLCONNECTOR</DefineConstants>
+  </PropertyGroup>
+  
   <ItemGroup>
   <ItemGroup>
-    <IlcArg Include="--reflectedonly" />
+    <PackageReference Condition=" '$(DatabaseProvider)' == 'Npgsql' " Include="Npgsql" Version="6.0.0-preview4" />
+    <PackageReference Condition=" '$(DatabaseProvider)' == 'MySqlConnector' " Include="MySqlConnector" Version="1.3.12" />
+    <PackageReference Include="Microsoft.DotNet.ILCompiler" Version="7.0.0-*" />
   </ItemGroup>
   </ItemGroup>
-  
+
   <ItemGroup>
   <ItemGroup>
       <None Include="appsettings.json" CopyToOutputDirectory="PreserveNewest" />
       <None Include="appsettings.json" CopyToOutputDirectory="PreserveNewest" />
   </ItemGroup>
   </ItemGroup>
 
 
   <ItemGroup>
   <ItemGroup>
-    <PackageReference Include="Utf8Json" Version="1.3.7" />
-    <PackageReference Include="Microsoft.DotNet.ILCompiler" Version="7.0.0-*" />
+    <RdXmlFile Condition=" '$(DatabaseProvider)' == 'Npgsql' " Include="npgsql.rd.xml" />
   </ItemGroup>
   </ItemGroup>
-   
-  <Target Name="ConfigureTrimming"
-    BeforeTargets="PrepareForILLink">
-    <ItemGroup>
-      <ManagedAssemblyToLink>
-        <TrimMode>link</TrimMode>
-      </ManagedAssemblyToLink>
-    </ItemGroup>
-  </Target>
+
 </Project>
 </Project>

+ 20 - 12
frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/Program.cs

@@ -3,12 +3,10 @@
 
 
 using System;
 using System;
 using System.Net;
 using System.Net;
+using System.Runtime.InteropServices;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
 using Microsoft.AspNetCore.Hosting;
 using Microsoft.AspNetCore.Hosting;
 using Microsoft.Extensions.Configuration;
 using Microsoft.Extensions.Configuration;
-#if DATABASE
-using Npgsql;
-#endif
 
 
 namespace PlatformBenchmarks
 namespace PlatformBenchmarks
 {
 {
@@ -18,9 +16,6 @@ namespace PlatformBenchmarks
 
 
         public static async Task Main(string[] args)
         public static async Task Main(string[] args)
         {
         {
-            Utf8Json.Resolvers.CompositeResolver.RegisterAndSetAsDefault(    
-                Utf8Json.Resolvers.GeneratedResolver.Instance);
-
             Args = args;
             Args = args;
 
 
             Console.WriteLine(BenchmarkApplication.ApplicationName);
             Console.WriteLine(BenchmarkApplication.ApplicationName);
@@ -36,9 +31,9 @@ namespace PlatformBenchmarks
             DateHeader.SyncDateTimer();
             DateHeader.SyncDateTimer();
 
 
             var host = BuildWebHost(args);
             var host = BuildWebHost(args);
-#if DATABASE
             var config = (IConfiguration)host.Services.GetService(typeof(IConfiguration));
             var config = (IConfiguration)host.Services.GetService(typeof(IConfiguration));
             BatchUpdateString.DatabaseServer = config.Get<AppSettings>().Database;
             BatchUpdateString.DatabaseServer = config.Get<AppSettings>().Database;
+#if DATABASE
             await BenchmarkApplication.Db.PopulateCache();
             await BenchmarkApplication.Db.PopulateCache();
 #endif
 #endif
             await host.RunAsync();
             await host.RunAsync();
@@ -53,12 +48,13 @@ namespace PlatformBenchmarks
                 .AddCommandLine(args)
                 .AddCommandLine(args)
                 .Build();
                 .Build();
 
 
-#if DATABASE
             var appSettings = config.Get<AppSettings>();
             var appSettings = config.Get<AppSettings>();
+#if DATABASE
             Console.WriteLine($"Database: {appSettings.Database}");
             Console.WriteLine($"Database: {appSettings.Database}");
             Console.WriteLine($"ConnectionString: {appSettings.ConnectionString}");
             Console.WriteLine($"ConnectionString: {appSettings.ConnectionString}");
 
 
-            if (appSettings.Database == DatabaseServer.PostgreSql)
+            if (appSettings.Database is DatabaseServer.PostgreSql
+                                     or DatabaseServer.MySql)
             {
             {
                 BenchmarkApplication.Db = new RawDb(new ConcurrentRandom(), appSettings);
                 BenchmarkApplication.Db = new RawDb(new ConcurrentRandom(), appSettings);
             }
             }
@@ -68,7 +64,7 @@ namespace PlatformBenchmarks
             }
             }
 #endif
 #endif
 
 
-            var host = new WebHostBuilder()
+            var hostBuilder = new WebHostBuilder()
                 .UseBenchmarksConfiguration(config)
                 .UseBenchmarksConfiguration(config)
                 .UseKestrel((context, options) =>
                 .UseKestrel((context, options) =>
                 {
                 {
@@ -79,8 +75,20 @@ namespace PlatformBenchmarks
                         builder.UseHttpApplication<BenchmarkApplication>();
                         builder.UseHttpApplication<BenchmarkApplication>();
                     });
                     });
                 })
                 })
-                .UseStartup<Startup>()
-                .Build();
+                .UseStartup<Startup>();
+
+            hostBuilder.UseSockets(options =>
+            {
+                options.WaitForDataBeforeAllocatingBuffer = false;
+
+                if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
+                {
+                    options.UnsafePreferInlineScheduling = Environment.GetEnvironmentVariable("DOTNET_SYSTEM_NET_SOCKETS_INLINE_COMPLETIONS") == "1";
+                }
+            });
+
+
+            var host = hostBuilder.Build();
 
 
             return host;
             return host;
         }
         }

+ 0 - 1
frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/Startup.cs

@@ -10,7 +10,6 @@ namespace PlatformBenchmarks
     {
     {
         public void Configure(IApplicationBuilder app)
         public void Configure(IApplicationBuilder app)
         {
         {
-            
         }
         }
     }
     }
 }
 }

+ 59 - 0
frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/StringBuilderCache.cs

@@ -0,0 +1,59 @@
+// Copyright (c) .NET Foundation. All rights reserved. 
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 
+
+using System;
+using System.Text;
+
+namespace PlatformBenchmarks
+{
+    internal static class StringBuilderCache
+    {
+        private const int DefaultCapacity = 1386;
+        private const int MaxBuilderSize = DefaultCapacity * 3;
+
+        [ThreadStatic]
+        private static StringBuilder t_cachedInstance;
+
+        /// <summary>Get a StringBuilder for the specified capacity.</summary>
+        /// <remarks>If a StringBuilder of an appropriate size is cached, it will be returned and the cache emptied.</remarks>
+        public static StringBuilder Acquire(int capacity = DefaultCapacity)
+        {
+            if (capacity <= MaxBuilderSize)
+            {
+                StringBuilder sb = t_cachedInstance;
+                if (capacity < DefaultCapacity)
+                {
+                    capacity = DefaultCapacity;
+                }
+
+                if (sb != null)
+                {
+                    // Avoid stringbuilder block fragmentation by getting a new StringBuilder
+                    // when the requested size is larger than the current capacity
+                    if (capacity <= sb.Capacity)
+                    {
+                        t_cachedInstance = null;
+                        sb.Clear();
+                        return sb;
+                    }
+                }
+            }
+            return new StringBuilder(capacity);
+        }
+
+        public static void Release(StringBuilder sb)
+        {
+            if (sb.Capacity <= MaxBuilderSize)
+            {
+                t_cachedInstance = sb;
+            }
+        }
+
+        public static string GetStringAndRelease(StringBuilder sb)
+        {
+            string result = sb.ToString();
+            Release(sb);
+            return result;
+        }
+    }
+}

+ 0 - 165
frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/Utf8Json.Generated.cs

@@ -1,165 +0,0 @@
-// Generated by Utf8Json.UniversalCodeGenerator. Do not hand edit.
-
-#pragma warning disable 618
-#pragma warning disable 612
-#pragma warning disable 414
-#pragma warning disable 168
-
-namespace Utf8Json.Resolvers
-{
-    using System;
-    using Utf8Json;
-
-    public class GeneratedResolver : global::Utf8Json.IJsonFormatterResolver
-    {
-        public static readonly global::Utf8Json.IJsonFormatterResolver Instance = new GeneratedResolver();
-
-        GeneratedResolver()
-        {
-
-        }
-
-        public global::Utf8Json.IJsonFormatter<T> GetFormatter<T>()
-        {
-            return FormatterCache<T>.formatter;
-        }
-
-        static class FormatterCache<T>
-        {
-            public static readonly global::Utf8Json.IJsonFormatter<T> formatter;
-
-            static FormatterCache()
-            {
-                var f = GeneratedResolverGetFormatterHelper.GetFormatter(typeof(T));
-                if (f != null)
-                {
-                    formatter = (global::Utf8Json.IJsonFormatter<T>)f;
-                }
-            }
-        }
-    }
-
-    internal static class GeneratedResolverGetFormatterHelper
-    {
-        static readonly global::System.Collections.Generic.Dictionary<Type, int> lookup;
-
-        static GeneratedResolverGetFormatterHelper()
-        {
-            lookup = new global::System.Collections.Generic.Dictionary<Type, int>(1)
-            {
-                {typeof(global::PlatformBenchmarks.JsonMessage), 0 },
-            };
-        }
-
-        internal static object GetFormatter(Type t)
-        {
-            int key;
-            if (!lookup.TryGetValue(t, out key)) return null;
-
-            switch (key)
-            {
-                case 0: return new Utf8Json.Formatters.PlatformBenchmarks.JsonMessageFormatter();
-                default: return null;
-            }
-        }
-    }
-}
-
-#pragma warning disable 168
-#pragma warning restore 414
-#pragma warning restore 618
-#pragma warning restore 612
-
-#pragma warning disable 618
-#pragma warning disable 612
-#pragma warning disable 414
-#pragma warning disable 219
-#pragma warning disable 168
-
-namespace Utf8Json.Formatters.PlatformBenchmarks
-{
-    using System;
-    using Utf8Json;
-
-
-    public sealed class JsonMessageFormatter : global::Utf8Json.IJsonFormatter<global::PlatformBenchmarks.JsonMessage>
-    {
-        readonly global::Utf8Json.Internal.AutomataDictionary ____keyMapping;
-        readonly byte[][] ____stringByteKeys;
-
-        public JsonMessageFormatter()
-        {
-            this.____keyMapping = new global::Utf8Json.Internal.AutomataDictionary()
-            {
-                { JsonWriter.GetEncodedPropertyNameWithoutQuotation("message"), 0},
-            };
-
-            this.____stringByteKeys = new byte[][]
-            {
-                JsonWriter.GetEncodedPropertyNameWithBeginObject("message"),
-                
-            };
-        }
-
-        public void Serialize(ref JsonWriter writer, global::PlatformBenchmarks.JsonMessage value, global::Utf8Json.IJsonFormatterResolver formatterResolver)
-        {
-            
-
-            writer.WriteRaw(this.____stringByteKeys[0]);
-            writer.WriteString(value.message);
-            
-            writer.WriteEndObject();
-        }
-
-        public global::PlatformBenchmarks.JsonMessage Deserialize(ref JsonReader reader, global::Utf8Json.IJsonFormatterResolver formatterResolver)
-        {
-            if (reader.ReadIsNull())
-            {
-                throw new InvalidOperationException("typecode is null, struct not supported");
-            }
-            
-
-            var __message__ = default(string);
-            var __message__b__ = false;
-
-            var ____count = 0;
-            reader.ReadIsBeginObjectWithVerify();
-            while (!reader.ReadIsEndObjectWithSkipValueSeparator(ref ____count))
-            {
-                var stringKey = reader.ReadPropertyNameSegmentRaw();
-                int key;
-                if (!____keyMapping.TryGetValueSafe(stringKey, out key))
-                {
-                    reader.ReadNextBlock();
-                    goto NEXT_LOOP;
-                }
-
-                switch (key)
-                {
-                    case 0:
-                        __message__ = reader.ReadString();
-                        __message__b__ = true;
-                        break;
-                    default:
-                        reader.ReadNextBlock();
-                        break;
-                }
-
-                NEXT_LOOP:
-                continue;
-            }
-
-            var ____result = new global::PlatformBenchmarks.JsonMessage();
-            if(__message__b__) ____result.message = __message__;
-
-            return ____result;
-        }
-    }
-
-}
-
-#pragma warning disable 168
-#pragma warning restore 219
-#pragma warning restore 414
-#pragma warning restore 618
-#pragma warning restore 612

+ 4 - 0
frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/appsettings.mysql.json

@@ -0,0 +1,4 @@
+{
+  "ConnectionString": "Server=tfb-database;Database=hello_world;User Id=benchmarkdbuser;Password=benchmarkdbpass;Maximum Pool Size=1024;SslMode=None;ConnectionReset=false;ConnectionIdlePingTime=900;ConnectionIdleTimeout=0;AutoEnlist=false;DefaultCommandTimeout=0;ConnectionTimeout=0;IgnorePrepare=false;",
+  "Database": "mysql"
+}

+ 4 - 0
frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/appsettings.postgresql.json

@@ -0,0 +1,4 @@
+{
+  "ConnectionString": "Server=tfb-database;Database=hello_world;User Id=benchmarkdbuser;Password=benchmarkdbpass;Maximum Pool Size=18;Enlist=false;Max Auto Prepare=4;Multiplexing=true;Write Coalescing Buffer Threshold Bytes=1000",
+  "Database": "postgresql"
+}

+ 4 - 0
frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/appsettings.postgresql.updates.json

@@ -0,0 +1,4 @@
+{
+  "ConnectionString": "Server=tfb-database;Database=hello_world;User Id=benchmarkdbuser;Password=benchmarkdbpass;Maximum Pool Size=18;Enlist=false;Max Auto Prepare=4;Multiplexing=true;Write Coalescing Buffer Threshold Bytes=1000",  
+  "Database": "postgresql"
+}

+ 51 - 0
frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/npgsql.rd.xml

@@ -0,0 +1,51 @@
+<Directives>
+  <Application>
+    <Assembly Name="Npgsql">
+      <Type Name="Npgsql.Internal.TypeHandlers.NetworkHandlers.InetHandler" Dynamic="Required All" />
+      <Type Name="Npgsql.Internal.TypeHandlers.DateTimeHandlers.DateHandler" Dynamic="Required All" />
+      <Type Name="Npgsql.Internal.TypeHandlers.DateTimeHandlers.IntervalHandler" Dynamic="Required All" />
+      <Type Name="Npgsql.Internal.TypeHandlers.DateTimeHandlers.TimestampTzHandler" Dynamic="Required All" />
+      <Type Name="Npgsql.Internal.TypeHandlers.BoolHandler" Dynamic="Required All" />
+      <Type Name="Npgsql.Internal.TypeHandlers.EnumHandler`1" Dynamic="Required All" />
+      <Type Name="Npgsql.Internal.TypeHandlers.UuidHandler" Dynamic="Required All" />
+      <Type Name="Npgsql.Internal.TypeHandlers.VoidHandler" Dynamic="Required All" />
+      <Type Name="Npgsql.Internal.TypeHandlers.NumericHandlers.DoubleHandler" Dynamic="Required All" />
+      <Type Name="Npgsql.Internal.TypeHandlers.NumericHandlers.Int16Handler" Dynamic="Required All" />
+      <Type Name="Npgsql.Internal.TypeHandlers.NumericHandlers.Int32Handler" Dynamic="Required All" />
+      <Type Name="Npgsql.Internal.TypeHandlers.NumericHandlers.Int64Handler" Dynamic="Required All" />
+      <Type Name="Npgsql.Internal.TypeHandlers.NumericHandlers.MoneyHandler" Dynamic="Required All" />
+      <Type Name="Npgsql.Internal.TypeHandlers.NumericHandlers.SingleHandler" Dynamic="Required All" />
+      <Type Name="Npgsql.Internal.TypeHandlers.NumericHandlers.UInt32Handler" Dynamic="Required All" />
+      <Type Name="Npgsql.Internal.TypeHandlers.NetworkHandlers.CidrHandler" Dynamic="Required All" />
+      <Type Name="Npgsql.Internal.TypeHandlers.NetworkHandlers.MacaddrHandler" Dynamic="Required All" />
+      <Type Name="Npgsql.Internal.TypeHandlers.InternalTypeHandlers.InternalCharHandler" Dynamic="Required All" />
+      <Type Name="Npgsql.Internal.TypeHandlers.InternalTypeHandlers.PgLsnHandler" Dynamic="Required All" />
+      <Type Name="Npgsql.Internal.TypeHandlers.InternalTypeHandlers.TidHandler" Dynamic="Required All" />
+      <Type Name="Npgsql.Internal.TypeHandlers.GeometricHandlers.BoxHandler" Dynamic="Required All" />
+      <Type Name="Npgsql.Internal.TypeHandlers.GeometricHandlers.CircleHandler" Dynamic="Required All" />
+      <Type Name="Npgsql.Internal.TypeHandlers.GeometricHandlers.LineHandler" Dynamic="Required All" />
+      <Type Name="Npgsql.Internal.TypeHandlers.GeometricHandlers.LineSegmentHandler" Dynamic="Required All" />
+      <Type Name="Npgsql.Internal.TypeHandlers.GeometricHandlers.PointHandler" Dynamic="Required All" />
+      <Type Name="Npgsql.Internal.TypeHandlers.DateTimeHandlers.TimeHandler" Dynamic="Required All" />
+      <Type Name="Npgsql.Internal.TypeHandlers.DateTimeHandlers.TimeTzHandler" Dynamic="Required All" />
+      <Type Name="Npgsql.Internal.TypeHandlers.BitStringHandler" Dynamic="Required All" />
+      <Type Name="Npgsql.Internal.TypeHandlers.ByteaHandler" Dynamic="Required All" />
+      <Type Name="Npgsql.Internal.TypeHandlers.HstoreHandler" Dynamic="Required All" />
+      <Type Name="Npgsql.Internal.TypeHandlers.JsonHandler" Dynamic="Required All" />
+      <Type Name="Npgsql.Internal.TypeHandlers.JsonPathHandler" Dynamic="Required All" />
+      <Type Name="Npgsql.Internal.TypeHandlers.RangeHandler`2" Dynamic="Required All" />
+      <Type Name="Npgsql.Internal.TypeHandlers.RecordHandler" Dynamic="Required All" />
+      <Type Name="Npgsql.Internal.TypeHandlers.UnknownTypeHandler" Dynamic="Required All" />
+      <Type Name="Npgsql.Internal.TypeHandlers.UnmappedEnumHandler" Dynamic="Required All" />
+      <Type Name="Npgsql.Internal.TypeHandlers.LTreeHandlers.LQueryHandler" Dynamic="Required All" />
+      <Type Name="Npgsql.Internal.TypeHandlers.LTreeHandlers.LTreeHandler" Dynamic="Required All" />
+      <Type Name="Npgsql.Internal.TypeHandlers.LTreeHandlers.LTxtQueryHandler" Dynamic="Required All" />
+      <Type Name="Npgsql.Internal.TypeHandlers.NumericHandlers.NumericHandler" Dynamic="Required All" />
+      <Type Name="Npgsql.Internal.TypeHandlers.GeometricHandlers.PathHandler" Dynamic="Required All" />
+      <Type Name="Npgsql.Internal.TypeHandlers.GeometricHandlers.PolygonHandler" Dynamic="Required All" />
+      <Type Name="Npgsql.Internal.TypeHandlers.FullTextSearchHandlers.TsQueryHandler" Dynamic="Required All" />
+      <Type Name="Npgsql.Internal.TypeHandlers.FullTextSearchHandlers.TsVectorHandler" Dynamic="Required All" />
+      <Type Name="Npgsql.Internal.TypeHandlers.CompositeHandlers.CompositeHandler`1" Dynamic="Required All" />
+    </Assembly>
+  </Application>
+</Directives>

+ 17 - 0
frameworks/CSharp/aspnetcore-corert/aspcore-corert-ado-pg-up.dockerfile

@@ -0,0 +1,17 @@
+FROM mcr.microsoft.com/dotnet/sdk:6.0.100-rc.2 AS build
+RUN apt-get update
+RUN apt-get -yqq install clang zlib1g-dev libkrb5-dev
+WORKDIR /app
+COPY PlatformBenchmarks .
+RUN dotnet publish -c Release -o out /p:DatabaseProvider=Npgsql -r linux-x64
+
+FROM mcr.microsoft.com/dotnet/aspnet:6.0.0-rc.2 AS runtime
+ENV ASPNETCORE_URLS http://+:8080
+
+WORKDIR /app
+COPY --from=build /app/out ./
+COPY PlatformBenchmarks/appsettings.postgresql.updates.json ./appsettings.json
+
+EXPOSE 8080
+
+ENTRYPOINT ["./PlatformBenchmarks"]

+ 17 - 0
frameworks/CSharp/aspnetcore-corert/aspcore-corert-ado-pg.dockerfile

@@ -0,0 +1,17 @@
+FROM mcr.microsoft.com/dotnet/sdk:6.0.100-rc.2 AS build
+RUN apt-get update
+RUN apt-get -yqq install clang zlib1g-dev libkrb5-dev
+WORKDIR /app
+COPY PlatformBenchmarks .
+RUN dotnet publish -c Release -o out /p:DatabaseProvider=Npgsql -r linux-x64
+
+FROM mcr.microsoft.com/dotnet/aspnet:6.0.0-rc.2 AS runtime
+ENV ASPNETCORE_URLS http://+:8080
+
+WORKDIR /app
+COPY --from=build /app/out ./
+COPY PlatformBenchmarks/appsettings.postgresql.json ./appsettings.json
+
+EXPOSE 8080
+
+ENTRYPOINT ["./PlatformBenchmarks"]

+ 3 - 3
frameworks/CSharp/aspnetcore-corert/aspcore-corert.dockerfile

@@ -1,11 +1,11 @@
-FROM mcr.microsoft.com/dotnet/sdk:5.0 AS build
+FROM mcr.microsoft.com/dotnet/sdk:6.0.100-rc.2 AS build
 RUN apt-get update
 RUN apt-get update
-RUN apt-get -yqq install clang zlib1g-dev libkrb5-dev libtinfo5
+RUN apt-get -yqq install clang zlib1g-dev libkrb5-dev
 WORKDIR /app
 WORKDIR /app
 COPY PlatformBenchmarks .
 COPY PlatformBenchmarks .
 RUN dotnet publish -c Release -o out -r linux-x64
 RUN dotnet publish -c Release -o out -r linux-x64
 
 
-FROM mcr.microsoft.com/dotnet/aspnet:5.0 AS runtime
+FROM mcr.microsoft.com/dotnet/aspnet:6.0.0-rc.2 AS runtime
 ENV ASPNETCORE_URLS http://+:8080
 ENV ASPNETCORE_URLS http://+:8080
 ENV DOTNET_SYSTEM_NET_SOCKETS_INLINE_COMPLETIONS 1
 ENV DOTNET_SYSTEM_NET_SOCKETS_INLINE_COMPLETIONS 1
 WORKDIR /app
 WORKDIR /app

+ 39 - 0
frameworks/CSharp/aspnetcore-corert/benchmark_config.json

@@ -19,6 +19,45 @@
       "display_name": "ASP.NET Core [Platform, CoreRT]",
       "display_name": "ASP.NET Core [Platform, CoreRT]",
       "notes": "",
       "notes": "",
       "versus": "aspcore"
       "versus": "aspcore"
+    },
+    "ado-pg": {
+      "fortune_url": "/fortunes",
+      "db_url": "/db",
+      "query_url": "/queries/",
+      "cached_query_url": "/cached-worlds/",
+      "port": 8080,
+      "approach": "Stripped",
+      "classification": "Platform",
+      "database": "Postgres",
+      "framework": "ASP.NET Core",
+      "language": "C#",
+      "orm": "Raw",
+      "platform": ".NET",
+      "flavor": "CoreRT",
+      "webserver": "Kestrel",
+      "os": "Linux",
+      "database_os": "Linux",
+      "display_name": "ASP.NET Core [Platform, CoreRT, Pg]",
+      "notes": "",
+      "versus": "aspcore-ado-pg"
+    },
+    "ado-pg-up": {
+      "update_url": "/updates/",
+      "port": 8080,
+      "approach": "Stripped",
+      "classification": "Platform",
+      "database": "Postgres",
+      "framework": "ASP.NET Core",
+      "language": "C#",
+      "orm": "Raw",
+      "platform": ".NET",
+      "flavor": "CoreRT",
+      "webserver": "Kestrel",
+      "os": "Linux",
+      "database_os": "Linux",
+      "display_name": "ASP.NET Core [Platform, CoreRT, Pg]",
+      "notes": "",
+      "versus": "aspcore-ado-pg-up"
     }
     }
   }]
   }]
 }
 }

+ 27 - 0
frameworks/CSharp/aspnetcore-corert/config.toml

@@ -1,6 +1,21 @@
 [framework]
 [framework]
 name = "aspnetcore-corert"
 name = "aspnetcore-corert"
 
 
+[ado-pg]
+urls.db = "/db"
+urls.query = "/queries/"
+urls.fortune = "/fortunes"
+urls.cached_query = "/cached-worlds/"
+approach = "Stripped"
+classification = "Platform"
+database = "Postgres"
+database_os = "Linux"
+os = "Linux"
+orm = "Raw"
+platform = ".NET"
+webserver = "Kestrel"
+versus = "aspcore-ado-pg"
+
 [main]
 [main]
 urls.plaintext = "/plaintext"
 urls.plaintext = "/plaintext"
 urls.json = "/json"
 urls.json = "/json"
@@ -13,3 +28,15 @@ orm = "Raw"
 platform = ".NET"
 platform = ".NET"
 webserver = "Kestrel"
 webserver = "Kestrel"
 versus = "aspcore"
 versus = "aspcore"
+
+[ado-pg-up]
+urls.update = "/updates/"
+approach = "Stripped"
+classification = "Platform"
+database = "Postgres"
+database_os = "Linux"
+os = "Linux"
+orm = "Raw"
+platform = ".NET"
+webserver = "Kestrel"
+versus = "aspcore-ado-pg-up"