Browse Source

Add Npgsql in natively compiled mode without metadata (#7014)

* Add Npgsql in natively compile mode without metadata

* Improved performance tweaks

Co-authored-by: LLT21 <[email protected]>
LLT21 3 years ago
parent
commit
9dc889e9b4

+ 19 - 0
frameworks/CSharp/appmpower/appmpower-ado-pg.dockerfile

@@ -0,0 +1,19 @@
+FROM mcr.microsoft.com/dotnet/sdk:6.0.100 AS build
+RUN apt-get update
+RUN apt-get -yqq install clang zlib1g-dev libkrb5-dev libtinfo5
+
+WORKDIR /app
+COPY src .
+RUN mv ./appMpower.ado ./appMpower.csproj
+RUN dotnet publish -c Release -o out -r linux-x64
+
+# Construct the actual image that will run
+FROM mcr.microsoft.com/dotnet/aspnet:6.0.0 AS runtime
+RUN apt-get update
+
+WORKDIR /app
+COPY --from=build /app/out ./
+
+EXPOSE 8080
+
+ENTRYPOINT ["./appMpower"]

+ 22 - 0
frameworks/CSharp/appmpower/benchmark_config.json

@@ -64,6 +64,28 @@
         "display_name": "appMpower [Middleware, My, Odbc]",
         "display_name": "appMpower [Middleware, My, Odbc]",
         "notes": "",
         "notes": "",
         "versus": "aspcore-mw-ado-my"
         "versus": "aspcore-mw-ado-my"
+      },
+      "ado-pg": {
+        "db_url": "/db",
+        "query_url": "/queries?c=",
+        "update_url": "/updates?c=",
+        "fortune_url": "/fortunes",
+        "cached_query_url": "/cached-worlds?c=",
+        "port": 8080,
+        "approach": "Realistic",
+        "classification": "Platform",
+        "database": "Postgres",
+        "framework": "appmpower",
+        "language": "C#",
+        "orm": "Raw",
+        "platform": ".NET",
+        "flavor": "CoreCLR",
+        "webserver": "Kestrel",
+        "os": "Linux",
+        "database_os": "Linux",
+        "display_name": "appMpower [Middleware, Pg, Ado]",
+        "notes": "",
+        "versus": "aspcore-mw-ado-pg"
       }
       }
     }
     }
   ]
   ]

+ 17 - 1
frameworks/CSharp/appmpower/config.toml

@@ -44,4 +44,20 @@ os = "Linux"
 orm = "Raw"
 orm = "Raw"
 platform = ".NET"
 platform = ".NET"
 webserver = "Kestrel"
 webserver = "Kestrel"
-versus = "aspcore-mw-ado-my"
+versus = "aspcore-mw-ado-my"
+
+[ado-pg]
+urls.db = "/db"
+urls.query = "/queries?c="
+urls.update = "/updates?c="
+urls.fortune = "/fortunes"
+urls.cached_query = "/cached-worlds?c="
+approach = "Realistic"
+classification = "Micro"
+database = "Postgres"
+database_os = "Linux"
+os = "Linux"
+orm = "Raw"
+platform = ".NET"
+webserver = "Kestrel"
+versus = "aspcore-mw-ado-pg"

+ 0 - 11
frameworks/CSharp/appmpower/src/ConnectionStrings.cs

@@ -1,11 +0,0 @@
-namespace appMpower
-{
-   public static class ConnectionStrings
-   {
-#if MYSQL
-      public const string OdbcConnection = "Driver={MariaDB};Server=tfb-database;Database=hello_world;Uid=benchmarkdbuser;Pwd=benchmarkdbpass;Pooling=false;OPTIONS=67108864;FLAG_FORWARD_CURSOR=1"; 
-#else
-      public const string OdbcConnection = "Driver={PostgreSQL};Server=tfb-database;Database=hello_world;Uid=benchmarkdbuser;Pwd=benchmarkdbpass;UseServerSidePrepare=1;Pooling=false";
-#endif
-   }
-}

+ 4 - 2
frameworks/CSharp/appmpower/src/Db/DataProvider.cs

@@ -3,11 +3,13 @@ namespace appMpower.Db
    public static class DataProvider
    public static class DataProvider
    {
    {
 #if MYSQL
 #if MYSQL
-      public static bool IsOdbcConnection = true; 
       public const string ConnectionString = "Driver={MariaDB};Server=tfb-database;Database=hello_world;Uid=benchmarkdbuser;Pwd=benchmarkdbpass;Pooling=false;OPTIONS=67108864;FLAG_FORWARD_CURSOR=1"; 
       public const string ConnectionString = "Driver={MariaDB};Server=tfb-database;Database=hello_world;Uid=benchmarkdbuser;Pwd=benchmarkdbpass;Pooling=false;OPTIONS=67108864;FLAG_FORWARD_CURSOR=1"; 
+#elif ADO
+      public const string ConnectionString = "Server=tfb-database;Database=hello_world;User Id=benchmarkdbuser;Password=benchmarkdbpass;SSL Mode=Disable;Maximum Pool Size=8;NoResetOnClose=true;Enlist=false;Max Auto Prepare=4;Multiplexing=true;Write Coalescing Buffer Threshold Bytes=1000";
+      //public const string ConnectionString = "Server=localhost;Database=hello_world;User Id=benchmarkdbuser;Password=benchmarkdbpass;SSL Mode=Disable;Maximum Pool Size=9;NoResetOnClose=true;Enlist=false;Max Auto Prepare=4;Multiplexing=true;Write Coalescing Buffer Threshold Bytes=1000";
 #else
 #else
-      public static bool IsOdbcConnection = true;
       public const string ConnectionString = "Driver={PostgreSQL};Server=tfb-database;Database=hello_world;Uid=benchmarkdbuser;Pwd=benchmarkdbpass;UseServerSidePrepare=1;Pooling=false";
       public const string ConnectionString = "Driver={PostgreSQL};Server=tfb-database;Database=hello_world;Uid=benchmarkdbuser;Pwd=benchmarkdbpass;UseServerSidePrepare=1;Pooling=false";
+      //public const string ConnectionString = "Driver={PostgreSQL};Server=localhost;Database=hello_world;Uid=benchmarkdbuser;Pwd=benchmarkdbpass;UseServerSidePrepare=1;Pooling=false";
 #endif
 #endif
    }
    }
 }
 }

+ 0 - 3
frameworks/CSharp/appmpower/src/Db/PooledCommand.cs

@@ -1,6 +1,5 @@
 using System.Data;
 using System.Data;
 using System.Data.Common;
 using System.Data.Common;
-using System.Data.Odbc;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
 
 
 namespace appMpower.Db
 namespace appMpower.Db
@@ -178,13 +177,11 @@ namespace appMpower.Db
 
 
       public async Task<int> ExecuteNonQueryAsync()
       public async Task<int> ExecuteNonQueryAsync()
       {
       {
-         if (DataProvider.IsOdbcConnection) return await (_dbCommand as OdbcCommand).ExecuteNonQueryAsync();
          return await (_dbCommand as DbCommand).ExecuteNonQueryAsync();
          return await (_dbCommand as DbCommand).ExecuteNonQueryAsync();
       }
       }
 
 
       public async Task<DbDataReader> ExecuteReaderAsync(CommandBehavior behavior)
       public async Task<DbDataReader> ExecuteReaderAsync(CommandBehavior behavior)
       {
       {
-         if (DataProvider.IsOdbcConnection) return await (_dbCommand as OdbcCommand).ExecuteReaderAsync(behavior);
          return await (_dbCommand as DbCommand).ExecuteReaderAsync(behavior);
          return await (_dbCommand as DbCommand).ExecuteReaderAsync(behavior);
       }
       }
 
 

+ 4 - 5
frameworks/CSharp/appmpower/src/Db/PooledConnection.cs

@@ -179,11 +179,10 @@ namespace appMpower.Db
             pooledCommand.DbCommand.CommandText = commandText;
             pooledCommand.DbCommand.CommandText = commandText;
             pooledCommand.PooledConnection = this;
             pooledCommand.PooledConnection = this;
 
 
-            //For future use with non odbc drivers like Npgsql which do not support Prepare
-            if (DataProvider.IsOdbcConnection)
-            {
-               pooledCommand.DbCommand.Prepare();
-            }
+            //For non odbc drivers like Npgsql which do not support Prepare
+#if !ADO
+            pooledCommand.DbCommand.Prepare();
+#endif            
 
 
             //Console.WriteLine("prepare pool connection: " + this._number + " for command " + _pooledCommands.Count);
             //Console.WriteLine("prepare pool connection: " + this._number + " for command " + _pooledCommands.Count);
          }
          }

+ 6 - 16
frameworks/CSharp/appmpower/src/Db/PooledConnections.cs

@@ -1,5 +1,4 @@
 using System.Collections.Concurrent;
 using System.Collections.Concurrent;
-using System.Data.Odbc;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
 
 
 namespace appMpower.Db
 namespace appMpower.Db
@@ -8,12 +7,7 @@ namespace appMpower.Db
    {
    {
       private static bool _connectionsCreated = false;
       private static bool _connectionsCreated = false;
       private static short _createdConnections = 0;
       private static short _createdConnections = 0;
-
-#if MYSQL
-      private static short _maxConnections = 500; 
-#else
-      private static short _maxConnections = 500;
-#endif
+      private static short _maxConnections = 240;
 
 
       private static ConcurrentStack<PooledConnection> _stack = new ConcurrentStack<PooledConnection>();
       private static ConcurrentStack<PooledConnection> _stack = new ConcurrentStack<PooledConnection>();
       private static ConcurrentQueue<TaskCompletionSource<PooledConnection>> _waitingQueue = new ConcurrentQueue<TaskCompletionSource<PooledConnection>>();
       private static ConcurrentQueue<TaskCompletionSource<PooledConnection>> _waitingQueue = new ConcurrentQueue<TaskCompletionSource<PooledConnection>>();
@@ -39,15 +33,11 @@ namespace appMpower.Db
          {
          {
             pooledConnection = new PooledConnection();
             pooledConnection = new PooledConnection();
 
 
-            if (DataProvider.IsOdbcConnection)
-            {
-               pooledConnection.DbConnection = new OdbcConnection(connectionString);
-            }
-            else
-            {
-               //For future use with non odbc drivers which can be AOT compiled without reflection
-               //pooledConnection.DbConnection = new NpgsqlConnection(connectionString);
-            }
+#if ADO
+            pooledConnection.DbConnection = new Npgsql.NpgsqlConnection(connectionString);
+#else
+            pooledConnection.DbConnection = new System.Data.Odbc.OdbcConnection(connectionString);
+#endif               
 
 
             _createdConnections++;
             _createdConnections++;
 
 

+ 5 - 1
frameworks/CSharp/appmpower/src/HttpApplication.cs

@@ -61,8 +61,12 @@ namespace appMpower
                   count = 500;
                   count = 500;
                }
                }
 
 
-               //Json.RenderMany(httpResponse.Headers, httpResponseBody.Writer, await RawDb.LoadMultipleQueriesRows(count), _worldSerializer);
+#if ADO
+               Json.RenderMany(httpResponse.Headers, httpResponseBody.Writer, await RawDb.LoadMultipleQueriesRows(count), _worldSerializer);
+#else
                Json.RenderMany(httpResponse.Headers, httpResponseBody.Writer, await RawDb.ReadMultipleRows(count), _worldSerializer);
                Json.RenderMany(httpResponse.Headers, httpResponseBody.Writer, await RawDb.ReadMultipleRows(count), _worldSerializer);
+#endif
+
                return;
                return;
             }
             }
             else if (pathStringLength == 9 && pathStringStart == "f")
             else if (pathStringLength == 9 && pathStringStart == "f")

+ 2 - 2
frameworks/CSharp/appmpower/src/Kestrel/Json.cs

@@ -32,7 +32,7 @@ namespace appMpower.Kestrel
 
 
          jsonSerializer.Serialize(utf8JsonWriter, t);
          jsonSerializer.Serialize(utf8JsonWriter, t);
          utf8JsonWriter.Flush();
          utf8JsonWriter.Flush();
-         headerDictionary.Add(new KeyValuePair<string, StringValues>("Content-Length", ((uint)utf8JsonWriter.BytesCommitted).ToString()));
+         headerDictionary.Add(new KeyValuePair<string, StringValues>("Content-Length", utf8JsonWriter.BytesCommitted.ToString()));
       }
       }
 
 
       public static void RenderMany<T>(IHeaderDictionary headerDictionary, PipeWriter pipeWriter, T[] tArray, IJsonSerializer<T> jsonSerializer)
       public static void RenderMany<T>(IHeaderDictionary headerDictionary, PipeWriter pipeWriter, T[] tArray, IJsonSerializer<T> jsonSerializer)
@@ -52,7 +52,7 @@ namespace appMpower.Kestrel
 
 
          utf8JsonWriter.WriteEndArray();
          utf8JsonWriter.WriteEndArray();
          utf8JsonWriter.Flush();
          utf8JsonWriter.Flush();
-         headerDictionary.Add(new KeyValuePair<string, StringValues>("Content-Length", ((uint)utf8JsonWriter.BytesCommitted).ToString()));
+         headerDictionary.Add(new KeyValuePair<string, StringValues>("Content-Length", utf8JsonWriter.BytesCommitted.ToString()));
       }
       }
    }
    }
 }
 }

+ 22 - 0
frameworks/CSharp/appmpower/src/Microsoft/BatchUpdateString.cs

@@ -38,6 +38,28 @@ namespace PlatformBenchmarks
          {
          {
             sb.Append("UPDATE world SET randomNumber=? WHERE id=?;");
             sb.Append("UPDATE world SET randomNumber=? WHERE id=?;");
          }
          }
+#elif ADO
+         /*
+         sb.Append("UPDATE world SET randomNumber = temp.randomNumber FROM (VALUES ");
+         Enumerable.Range(0, lastIndex).ToList().ForEach(i => sb.Append($"(@i{i}, @r{i}), "));
+         sb.Append($"(@i{lastIndex}, @r{lastIndex}) ORDER BY 1) AS temp(id, randomNumber) WHERE temp.id = world.id");
+         */
+
+         sb.Append("UPDATE world SET randomNumber=CASE id ");
+
+         for (int i = 0; i < batchSize; i++)
+         {
+            sb.Append("WHEN @i" + i + " THEN @r" + i + " ");
+         }
+
+         sb.Append("ELSE randomnumber END WHERE id IN(");
+
+         for (int i = 0; i < lastIndex; i++)
+         {
+            sb.Append("@j" + i + ",");
+         }
+
+         sb.Append("@j" + lastIndex + ")");
 #else
 #else
          sb.Append("UPDATE world SET randomNumber=CASE id ");
          sb.Append("UPDATE world SET randomNumber=CASE id ");
 
 

+ 32 - 0
frameworks/CSharp/appmpower/src/Microsoft/ConcurrentRandom.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);
+      }
+   }
+}

+ 11 - 2
frameworks/CSharp/appmpower/src/RawDb.cs

@@ -13,7 +13,13 @@ namespace appMpower
    public static class RawDb
    public static class RawDb
    {
    {
       private const int MaxBatch = 500;
       private const int MaxBatch = 500;
+
+#if ADO      
+      private static ConcurrentRandom _random = new ConcurrentRandom();
+#else
       private static Random _random = new Random();
       private static Random _random = new Random();
+#endif
+
       private static string[] _queriesMultipleRows = new string[MaxBatch + 1];
       private static string[] _queriesMultipleRows = new string[MaxBatch + 1];
 
 
       private static readonly object[] _cacheKeys = Enumerable.Range(0, 10001).Select((i) => new CacheKey(i)).ToArray();
       private static readonly object[] _cacheKeys = Enumerable.Range(0, 10001).Select((i) => new CacheKey(i)).ToArray();
@@ -147,10 +153,13 @@ namespace appMpower
 
 
       private static (PooledCommand pooledCommand, IDbDataParameter dbDataParameter) CreateReadCommand(PooledConnection pooledConnection)
       private static (PooledCommand pooledCommand, IDbDataParameter dbDataParameter) CreateReadCommand(PooledConnection pooledConnection)
       {
       {
+#if ADO         
+         var pooledCommand = new PooledCommand("SELECT * FROM world WHERE id=@Id", pooledConnection);
+#else         
          var pooledCommand = new PooledCommand("SELECT * FROM world WHERE id=?", pooledConnection);
          var pooledCommand = new PooledCommand("SELECT * FROM world WHERE id=?", pooledConnection);
-         var dbDataParameter = pooledCommand.CreateParameter("@Id", DbType.Int32, _random.Next(1, 10001));
+#endif         
 
 
-         return (pooledCommand, dbDataParameter);
+         return (pooledCommand, pooledCommand.CreateParameter("Id", DbType.Int32, _random.Next(1, 10001)));
       }
       }
 
 
       private static async Task<World> ReadSingleRow(PooledCommand pooledCommand)
       private static async Task<World> ReadSingleRow(PooledCommand pooledCommand)

+ 7 - 0
frameworks/CSharp/appmpower/src/__BlockAllReflectionAttribute.cs

@@ -0,0 +1,7 @@
+#if ADO
+namespace System.Runtime.CompilerServices
+{
+   [AttributeUsage(AttributeTargets.All)]
+   internal class __BlockAllReflectionAttribute : Attribute { }
+}
+#endif

+ 28 - 0
frameworks/CSharp/appmpower/src/appMpower.ado

@@ -0,0 +1,28 @@
+<Project Sdk="Microsoft.NET.Sdk.Web">
+
+   <PropertyGroup>
+      <TargetFramework>net6.0</TargetFramework>
+      <OutputType>Exe</OutputType>
+      <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+
+      <TrimmerDefaultAction>link</TrimmerDefaultAction>
+      <IlcOptimizationPreference>Speed</IlcOptimizationPreference>
+      <IlcPgoOptimize>true</IlcPgoOptimize>
+      <IlcTrimMetadata>true</IlcTrimMetadata>
+
+      <UseSystemResourceKeys>true</UseSystemResourceKeys>
+      <EventSourceSupport>false</EventSourceSupport>
+      <DebuggerSupport>false</DebuggerSupport>
+      <IlcGenerateStackTraceData>false</IlcGenerateStackTraceData>
+   </PropertyGroup>
+
+   <ItemGroup>
+      <PackageReference Include="Npgsql" Version="6.0.2" />
+      <PackageReference Include="Microsoft.DotNet.ILCompiler" Version="7.0.0-*" />
+   </ItemGroup>
+
+   <PropertyGroup>
+      <DefineConstants>$(DefineConstants);ADO</DefineConstants>
+   </PropertyGroup>
+
+</Project>