Browse Source

Reuse pooling objects instead of recreating each time (#6716)

* Reuse pooling objects instead of recreating each time

* Reduced number of connections to 400

* Changed odbc threading parameter

* Improved update statement

* Using standard .net pipewriter functions

* Using the latest unixodbc and pgsqlodbc drivers

Co-authored-by: LLT21 <[email protected]>
LLT21 4 years ago
parent
commit
f637915f74

+ 53 - 1
frameworks/CSharp/appmpower/appmpower.dockerfile

@@ -1,15 +1,67 @@
 FROM mcr.microsoft.com/dotnet/sdk:5.0 AS build
 RUN apt-get update
 RUN apt-get -yqq install clang zlib1g-dev libkrb5-dev libtinfo5
+RUN apt-get install -y make build-essential libssl-dev zlib1g-dev libbz2-dev \
+   libreadline-dev libsqlite3-dev wget curl llvm libncurses5-dev libncursesw5-dev \
+   xz-utils tk-dev libffi-dev liblzma-dev pgpool2 vim-tiny
+
+WORKDIR /odbc
+
+# To compile the latest postgresql odbc driver, postgresql itself needs to be installed
+RUN curl -L -o postgresql-13.3.tar.gz https://ftp.postgresql.org/pub/source/v13.3/postgresql-13.3.tar.gz
+RUN curl -L -o unixODBC-2.3.9.tar.gz ftp://ftp.unixodbc.org/pub/unixODBC/unixODBC-2.3.9.tar.gz
+RUN curl -L -o psqlodbc-13.01.0000.tar.gz https://ftp.postgresql.org/pub/odbc/versions/src/psqlodbc-13.01.0000.tar.gz
+
+RUN tar -xvf postgresql-13.3.tar.gz
+RUN tar -xvf unixODBC-2.3.9.tar.gz
+RUN tar -xvf psqlodbc-13.01.0000.tar.gz
+
+WORKDIR /odbc/postgresql-13.3
+RUN ./configure
+RUN make
+RUN make install
+
+ENV PATH=/usr/local/pgsql/bin:$PATH
+
+WORKDIR /odbc/unixODBC-2.3.9
+RUN ./configure --prefix=/usr/local/unixODBC
+RUN make
+RUN make install
+
+ENV PATH=/usr/local/unixODBC/lib:$PATH
+
+WORKDIR /odbc/psqlodbc-13.01.0000
+RUN ./configure --with-unixodbc=/usr/local/unixODBC --with-libpq=/usr/local/pgsql --prefix=/usr/local/pgsqlodbc
+RUN make
+RUN make install
 
 WORKDIR /app
 COPY src .
 RUN dotnet publish -c Release -o out -r linux-x64
 
+# Construct the actual image that will run
 FROM mcr.microsoft.com/dotnet/aspnet:5.0 AS runtime
 
 RUN apt-get update
-RUN apt-get install -y unixodbc odbc-postgresql
+# The following installs standard versions unixodbc 2.3.6 and pgsqlodbc 11
+#RUN apt-get install -y unixodbc odbc-postgresql
+# unixodbc still needs to be installed even if compiled locally
+RUN apt-get install -y unixodbc
+
+RUN apt-get -yqq install clang zlib1g-dev libkrb5-dev libtinfo5
+RUN apt-get install -y make build-essential libssl-dev zlib1g-dev libbz2-dev \
+   libreadline-dev libsqlite3-dev wget curl llvm libncurses5-dev libncursesw5-dev \
+   xz-utils tk-dev libffi-dev liblzma-dev pgpool2 vim-tiny
+
+COPY --from=build /usr/local/unixODBC /usr/local/unixODBC
+
+# Check unixODBC version by: 
+# 1. Logging into containter: docker run --rm -it --entrypoint=/bin/bash techempower/tfb.test.appmpower
+# 2. odbcinst  --version
+
+ENV PATH=/usr/local/unixODBC/bin:$PATH
+
+COPY --from=build /usr/local/pgsqlodbc /usr/local/pgsqlodbc
 
 WORKDIR /etc/
 COPY odbcinst.ini .

+ 5 - 3
frameworks/CSharp/appmpower/odbcinst.ini

@@ -1,7 +1,7 @@
 [ODBC]
 Trace=0
 Debug=0
-Pooling=No
+Pooling=0
 
 [ODBC Drivers]
 PostgreSQL = Installed
@@ -14,6 +14,8 @@ Description=ODBC for PostgreSQL
 ; WARNING: The old psql odbc driver psqlodbc.so is now renamed psqlodbcw.so
 ; in version 08.x. Note that the library can also be installed under an other
 ; path than /usr/local/lib/ following your installation.
-Driver = /usr/lib/x86_64-linux-gnu/odbc/psqlodbcw.so
-Threading = 1
+; This is the standard location used by apt-get install -y unixodbc
+;Driver = /usr/lib/x86_64-linux-gnu/odbc/psqlodbcw.so
+Driver =/usr/local/pgsqlodbc/lib/psqlodbcw.so
+Threading = 0
 CPTimeout = 0

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

@@ -203,6 +203,11 @@ namespace appMpower.Db
          _odbcCommand.Prepare();
       }
 
+      public void Release()
+      {
+         _pooledConnection.ReleaseCommand(this);
+      }
+
       public void Dispose()
       {
          _pooledConnection.ReleaseCommand(this);

+ 26 - 26
frameworks/CSharp/appmpower/src/Db/PooledConnection.cs

@@ -8,7 +8,7 @@ namespace appMpower.Db
 {
    public class PooledConnection : IDbConnection
    {
-      private bool _isInUse = true;
+      private bool _released = false;
       private short _number = 0;
       private OdbcConnection _odbcConnection;
       private ConcurrentDictionary<string, PooledCommand> _pooledCommands;
@@ -35,18 +35,6 @@ namespace appMpower.Db
          }
       }
 
-      public bool IsInUse
-      {
-         get
-         {
-            return _isInUse;
-         }
-         set
-         {
-            _isInUse = value;
-         }
-      }
-
       public short Number
       {
          get
@@ -107,6 +95,18 @@ namespace appMpower.Db
          }
       }
 
+      public bool Released
+      {
+         get
+         {
+            return _released;
+         }
+         internal set
+         {
+            _released = value;
+         }
+      }
+
       public IDbTransaction BeginTransaction()
       {
          return _odbcConnection.BeginTransaction();
@@ -124,8 +124,8 @@ namespace appMpower.Db
 
       public void Close()
       {
-         PooledConnections.ReleaseConnection(this);
-         _isInUse = false;
+         _odbcConnection.Close();
+         _released = true;
       }
 
       public IDbCommand CreateCommand()
@@ -146,12 +146,19 @@ namespace appMpower.Db
          }
       }
 
+      public void Release()
+      {
+         if (!_released && _odbcConnection.State == ConnectionState.Open)
+         {
+            PooledConnections.Release(this);
+         }
+      }
+
       public void Dispose()
       {
-         if (_isInUse && _odbcConnection.State == ConnectionState.Open)
+         if (!_released && _odbcConnection.State == ConnectionState.Open)
          {
-            PooledConnections.ReleaseConnection(this);
-            _isInUse = false;
+            PooledConnections.Dispose(this);
          }
       }
 
@@ -159,14 +166,7 @@ namespace appMpower.Db
       {
          if (_odbcConnection.State == ConnectionState.Closed)
          {
-            try
-            {
-               await _odbcConnection.OpenAsync();
-            }
-            catch (Exception exception)
-            {
-               Console.WriteLine(exception.Message);
-            }
+            await _odbcConnection.OpenAsync();
          }
       }
 

+ 21 - 10
frameworks/CSharp/appmpower/src/Db/PooledConnections.cs

@@ -9,7 +9,7 @@ namespace appMpower.Db
    {
       private static bool _connectionsCreated = false;
       private static short _createdConnections = 0;
-      private static short _maxConnections = 512; //Math.Min((byte)Environment.ProcessorCount, (byte)21);
+      private static short _maxConnections = 400; //Math.Min((byte)Environment.ProcessorCount, (byte)21);
       private static ConcurrentStack<PooledConnection> _stack = new ConcurrentStack<PooledConnection>();
       private static ConcurrentQueue<TaskCompletionSource<PooledConnection>> _waitingQueue = new ConcurrentQueue<TaskCompletionSource<PooledConnection>>();
 
@@ -19,7 +19,11 @@ namespace appMpower.Db
 
          if (_connectionsCreated)
          {
-            if (!_stack.TryPop(out pooledConnection))
+            if (_stack.TryPop(out pooledConnection))
+            {
+               pooledConnection.Released = false;
+            }
+            else
             {
                pooledConnection = await GetPooledConnectionAsync();
             }
@@ -51,22 +55,29 @@ namespace appMpower.Db
          return taskCompletionSource.Task;
       }
 
-      public static void ReleaseConnection(PooledConnection pooledConnection)
+      public static void Dispose(PooledConnection pooledConnection)
       {
-         TaskCompletionSource<PooledConnection> taskCompletionSource;
-         PooledConnection stackedConnection = new PooledConnection();
+         PooledConnection newPooledConnection = new PooledConnection();
+
+         newPooledConnection.OdbcConnection = pooledConnection.OdbcConnection;
+         newPooledConnection.Number = pooledConnection.Number;
+         newPooledConnection.PooledCommands = pooledConnection.PooledCommands;
 
-         stackedConnection.OdbcConnection = pooledConnection.OdbcConnection;
-         stackedConnection.Number = pooledConnection.Number;
-         stackedConnection.PooledCommands = pooledConnection.PooledCommands;
+         Release(newPooledConnection);
+      }
+
+      public static void Release(PooledConnection pooledConnection)
+      {
+         TaskCompletionSource<PooledConnection> taskCompletionSource;
 
          if (_waitingQueue.TryDequeue(out taskCompletionSource))
          {
-            taskCompletionSource.SetResult(stackedConnection);
+            taskCompletionSource.SetResult(pooledConnection);
          }
          else
          {
-            _stack.Push(stackedConnection);
+            pooledConnection.Released = true;
+            _stack.Push(pooledConnection);
          }
       }
    }

+ 16 - 17
frameworks/CSharp/appmpower/src/FortunesView.cs

@@ -1,5 +1,7 @@
 using System.Collections.Generic;
 using System.IO.Pipelines;
+using System.Text;
+using System.Threading.Tasks;
 using System.Web;
 using Microsoft.AspNetCore.Http;
 using Microsoft.Extensions.Primitives;
@@ -14,33 +16,30 @@ namespace appMpower
       private readonly static KeyValuePair<string, StringValues> _headerContentType =
          new KeyValuePair<string, StringValues>("Content-Type", "text/html; charset=UTF-8");
 
-      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 _fortunesColumn = "</td><td>";
-      private readonly static AsciiString _fortunesRowEnd = "</td></tr>";
-      private readonly static AsciiString _fortunesTableEnd = "</table></body></html>";
+      public static string _fortunesTableStart = "<!DOCTYPE html><html><head><title>Fortunes</title></head><body><table><tr><th>id</th><th>message</th></tr>";
+      public static string _fortunesRowStart = "<tr><td>";
+      public static string _fortunesColumn = "</td><td>";
+      public static string _fortunesRowEnd = "</td></tr>";
+      public static string _fortunesTableEnd = "</table></body></html>";
 
-      public static void Render(IHeaderDictionary headerDictionary, PipeWriter pipeWriter, List<Fortune> fortunes)
+      public static async Task Render(IHeaderDictionary headerDictionary, PipeWriter pipeWriter, List<Fortune> fortunes)
       {
          headerDictionary.Add(_headerServer);
          headerDictionary.Add(_headerContentType);
 
-         var bufferWriter = new BufferWriter<WriterAdapter>(new WriterAdapter(pipeWriter), 1600);
-
-         bufferWriter.Write(_fortunesTableStart);
+         string writer = _fortunesTableStart;
 
          foreach (var fortune in fortunes)
          {
-            bufferWriter.Write(_fortunesRowStart);
-            bufferWriter.WriteNumeric((uint)fortune.Id);
-            bufferWriter.Write(_fortunesColumn);
-            bufferWriter.WriteUtf8String(HttpUtility.HtmlEncode(fortune.Message));
-            bufferWriter.Write(_fortunesRowEnd);
+            writer += _fortunesRowStart + fortune.Id + _fortunesColumn + HttpUtility.HtmlEncode(fortune.Message) + _fortunesRowEnd;
          }
 
-         bufferWriter.Write(_fortunesTableEnd);
-         headerDictionary.Add(new KeyValuePair<string, StringValues>("Content-Length", bufferWriter.Buffered.ToString()));
-         bufferWriter.Commit();
+         writer += _fortunesTableEnd;
+
+         headerDictionary.Add(new KeyValuePair<string, StringValues>("Content-Length", (writer.Length + 32).ToString()));
+
+         await pipeWriter.WriteAsync(Encoding.UTF8.GetBytes(writer));
+         pipeWriter.Complete();
       }
    }
 }

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

@@ -66,7 +66,7 @@ namespace appMpower
             }
             else if (pathStringLength == 9 && pathStringStart == "f")
             {
-               FortunesView.Render(httpResponse.Headers, httpResponseBody.Writer, await RawDb.LoadFortunesRows());
+               await FortunesView.Render(httpResponse.Headers, httpResponseBody.Writer, await RawDb.LoadFortunesRows());
                return;
             }
             else if (pathStringLength == 8 && pathStringStart == "u")

+ 3 - 5
frameworks/CSharp/appmpower/src/Kestrel/PlainText.cs

@@ -14,16 +14,14 @@ namespace appMpower.Kestrel
       private readonly static KeyValuePair<string, StringValues> _headerContentType =
          new KeyValuePair<string, StringValues>("Content-Type", new StringValues("text/plain"));
 
-      public static async Task<FlushResult> RenderAsync(IHeaderDictionary headerDictionary, PipeWriter pipeWriter, ReadOnlyMemory<byte> utf8String)
+      public static async Task RenderAsync(IHeaderDictionary headerDictionary, PipeWriter pipeWriter, ReadOnlyMemory<byte> utf8String)
       {
          headerDictionary.Add(_headerServer);
          headerDictionary.Add(_headerContentType);
          headerDictionary.Add(new KeyValuePair<string, StringValues>("Content-Length", utf8String.Length.ToString()));
 
-         var result = await pipeWriter.WriteAsync(utf8String);
-         await pipeWriter.FlushAsync();
-
-         return result;
+         await pipeWriter.WriteAsync(utf8String);
+         pipeWriter.Complete();
       }
    }
 }

+ 0 - 58
frameworks/CSharp/appmpower/src/Microsoft/AsciiString.cs

@@ -1,58 +0,0 @@
-// 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
-{
-   public readonly struct AsciiString : IEquatable<AsciiString>
-   {
-      private readonly byte[] _data;
-
-      public AsciiString(string s) => _data = Encoding.ASCII.GetBytes(s);
-
-      private AsciiString(byte[] b) => _data = b;
-
-      public int Length => _data.Length;
-
-      public ReadOnlySpan<byte> AsSpan() => _data;
-
-      public static implicit operator ReadOnlySpan<byte>(AsciiString str) => str._data;
-      public static implicit operator byte[](AsciiString str) => str._data;
-
-      public static implicit operator AsciiString(string str) => new AsciiString(str);
-
-      public override string ToString() => Encoding.ASCII.GetString(_data);
-      public static explicit operator string(AsciiString str) => str.ToString();
-
-      public bool Equals(AsciiString other) => ReferenceEquals(_data, other._data) || SequenceEqual(_data, other._data);
-      private bool SequenceEqual(byte[] data1, byte[] data2) => new Span<byte>(data1).SequenceEqual(data2);
-
-      public static bool operator ==(AsciiString a, AsciiString b) => a.Equals(b);
-      public static bool operator !=(AsciiString a, AsciiString b) => !a.Equals(b);
-      public override bool Equals(object other) => (other is AsciiString) && Equals((AsciiString)other);
-
-      public static AsciiString operator +(AsciiString a, AsciiString b)
-      {
-         var result = new byte[a.Length + b.Length];
-         a._data.CopyTo(result, 0);
-         b._data.CopyTo(result, a.Length);
-         return new AsciiString(result);
-      }
-
-      public override int GetHashCode()
-      {
-         // Copied from x64 version of string.GetLegacyNonRandomizedHashCode()
-         // https://github.com/dotnet/coreclr/blob/master/src/mscorlib/src/System/String.Comparison.cs
-         var data = _data;
-         int hash1 = 5381;
-         int hash2 = hash1;
-         foreach (int b in data)
-         {
-            hash1 = ((hash1 << 5) + hash1) ^ b;
-         }
-         return hash1 + (hash2 * 1566083941);
-      }
-   }
-}

+ 6 - 3
frameworks/CSharp/appmpower/src/Microsoft/BatchUpdateString.cs

@@ -37,10 +37,12 @@ namespace PlatformBenchmarks
          //   Enumerable.Range(0, batchSize).ToList().ForEach(i => sb.Append($"UPDATE world SET randomnumber = @Random_{i} WHERE id = @Id_{i};"));
          //}
 
-         //sb.Append("UPDATE world SET randomNumber = CAST(temp.randomNumber AS INTEGER) FROM (VALUES ");
-         //Enumerable.Range(0, lastIndex).ToList().ForEach(i => sb.Append("(?, ?), "));
-         //sb.Append("(?, ?) ORDER BY 1) AS temp(id, randomNumber) WHERE CAST(temp.id AS INTEGER) = world.id");
+         sb.Append("UPDATE world SET randomNumber = temp.randomNumber FROM (VALUES ");
+         Enumerable.Range(0, lastIndex).ToList().ForEach(i => sb.Append("(?::int,?::int),"));
+         //sb.Append("(?::int,?::int) ORDER BY 1) AS temp(id, randomNumber) WHERE temp.id = world.id");
+         sb.Append("(?::int,?::int)) AS temp(id, randomNumber) WHERE temp.id = world.id");
 
+         /* --- only for alternative update statement - will be used for MySQL
          sb.Append("UPDATE world SET randomNumber = CASE id ");
 
          for (int i = 0; i < batchSize; i++)
@@ -58,6 +60,7 @@ namespace PlatformBenchmarks
 
          //Enumerable.Range(0, lastIndex).ToList().ForEach(i => sb.Append("?, "));
          sb.Append("?)");
+         */
 
          return _queries[batchSize] = StringBuilderCache.GetStringAndRelease(sb);
       }

+ 0 - 63
frameworks/CSharp/appmpower/src/Microsoft/BufferExtensions.cs

@@ -1,63 +0,0 @@
-// 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.Buffers;
-using System.Runtime.CompilerServices;
-using System.Text;
-
-namespace PlatformBenchmarks
-{
-   // Same as KestrelHttpServer\src\Kestrel.Core\Internal\Http\PipelineExtensions.cs
-   // However methods accept T : struct, IBufferWriter<byte> rather than PipeWriter.
-   // This allows a struct wrapper to turn CountingBufferWriter into a non-shared generic,
-   // while still offering the WriteNumeric extension.
-
-   public static class BufferExtensions
-   {
-      private const int _maxULongByteLength = 20;
-
-      [ThreadStatic]
-      private static byte[] _numericBytesScratch;
-
-      internal static void WriteUtf8String<T>(ref this BufferWriter<T> buffer, string text)
-           where T : struct, IBufferWriter<byte>
-      {
-         var byteCount = Encoding.UTF8.GetByteCount(text);
-         buffer.Ensure(byteCount);
-         byteCount = Encoding.UTF8.GetBytes(text.AsSpan(), buffer.Span);
-         buffer.Advance(byteCount);
-      }
-      [MethodImpl(MethodImplOptions.NoInlining)]
-      internal static void WriteNumericMultiWrite<T>(ref this BufferWriter<T> buffer, uint number)
-           where T : IBufferWriter<byte>
-      {
-         const byte AsciiDigitStart = (byte)'0';
-
-         var value = number;
-         var position = _maxULongByteLength;
-         var byteBuffer = NumericBytesScratch;
-         do
-         {
-            // Consider using Math.DivRem() if available
-            var quotient = value / 10;
-            byteBuffer[--position] = (byte)(AsciiDigitStart + (value - quotient * 10)); // 0x30 = '0'
-            value = quotient;
-         }
-         while (value != 0);
-
-         var length = _maxULongByteLength - position;
-         buffer.Write(new ReadOnlySpan<byte>(byteBuffer, position, length));
-      }
-
-      private static byte[] NumericBytesScratch => _numericBytesScratch ?? CreateNumericBytesScratch();
-
-      [MethodImpl(MethodImplOptions.NoInlining)]
-      private static byte[] CreateNumericBytesScratch()
-      {
-         var bytes = new byte[_maxULongByteLength];
-         _numericBytesScratch = bytes;
-         return bytes;
-      }
-   }
-}

+ 0 - 143
frameworks/CSharp/appmpower/src/Microsoft/BufferWriter.cs

@@ -1,143 +0,0 @@
-// 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.Buffers;
-using System.Runtime.CompilerServices;
-
-namespace PlatformBenchmarks
-{
-   public ref struct BufferWriter<T> where T : IBufferWriter<byte>
-   {
-      private T _output;
-      private Span<byte> _span;
-      private int _buffered;
-
-      public BufferWriter(T output, int sizeHint)
-      {
-         _buffered = 0;
-         _output = output;
-         _span = output.GetSpan(sizeHint);
-      }
-
-      public Span<byte> Span => _span;
-
-      public T Output => _output;
-
-      public int Buffered => _buffered;
-
-      [MethodImpl(MethodImplOptions.AggressiveInlining)]
-      public void Commit()
-      {
-         var buffered = _buffered;
-         if (buffered > 0)
-         {
-            _buffered = 0;
-            _output.Advance(buffered);
-         }
-      }
-
-      [MethodImpl(MethodImplOptions.AggressiveInlining)]
-      public void Advance(int count)
-      {
-         _buffered += count;
-         _span = _span.Slice(count);
-      }
-
-      [MethodImpl(MethodImplOptions.AggressiveInlining)]
-      public void Write(ReadOnlySpan<byte> source)
-      {
-         if (_span.Length >= source.Length)
-         {
-            source.CopyTo(_span);
-            Advance(source.Length);
-         }
-         else
-         {
-            WriteMultiBuffer(source);
-         }
-      }
-
-      [MethodImpl(MethodImplOptions.AggressiveInlining)]
-      public void Ensure(int count = 1)
-      {
-         if (_span.Length < count)
-         {
-            EnsureMore(count);
-         }
-      }
-
-      [MethodImpl(MethodImplOptions.NoInlining)]
-      private void EnsureMore(int count = 0)
-      {
-         if (_buffered > 0)
-         {
-            Commit();
-         }
-
-         _span = _output.GetSpan(count);
-      }
-
-      private void WriteMultiBuffer(ReadOnlySpan<byte> source)
-      {
-         while (source.Length > 0)
-         {
-            if (_span.Length == 0)
-            {
-               EnsureMore();
-            }
-
-            var writable = Math.Min(source.Length, _span.Length);
-            source.Slice(0, writable).CopyTo(_span);
-            source = source.Slice(writable);
-            Advance(writable);
-         }
-      }
-
-      [MethodImpl(MethodImplOptions.AggressiveInlining)]
-      internal void WriteNumeric(uint number)
-      {
-         const byte AsciiDigitStart = (byte)'0';
-
-         var span = this.Span;
-
-         // Fast path, try copying to the available memory directly
-         var advanceBy = 0;
-         if (span.Length >= 3)
-         {
-            if (number < 10)
-            {
-               span[0] = (byte)(number + AsciiDigitStart);
-               advanceBy = 1;
-            }
-            else if (number < 100)
-            {
-               var tens = (byte)((number * 205u) >> 11); // div10, valid to 1028
-
-               span[0] = (byte)(tens + AsciiDigitStart);
-               span[1] = (byte)(number - (tens * 10) + AsciiDigitStart);
-               advanceBy = 2;
-            }
-            else if (number < 1000)
-            {
-               var digit0 = (byte)((number * 41u) >> 12); // div100, valid to 1098
-               var digits01 = (byte)((number * 205u) >> 11); // div10, valid to 1028
-
-               span[0] = (byte)(digit0 + AsciiDigitStart);
-               span[1] = (byte)(digits01 - (digit0 * 10) + AsciiDigitStart);
-               span[2] = (byte)(number - (digits01 * 10) + AsciiDigitStart);
-               advanceBy = 3;
-            }
-         }
-
-         if (advanceBy > 0)
-         {
-            Advance(advanceBy);
-         }
-         else
-         {
-            BufferExtensions.WriteNumericMultiWrite(ref this, number);
-         }
-      }
-   }
-}

+ 0 - 23
frameworks/CSharp/appmpower/src/Microsoft/WriterAdapter.cs

@@ -1,23 +0,0 @@
-using System;
-using System.Buffers;
-using System.IO.Pipelines;
-
-namespace PlatformBenchmarks
-{
-   public struct WriterAdapter : IBufferWriter<byte>
-   {
-      public PipeWriter Writer;
-
-      public WriterAdapter(PipeWriter writer)
-          => Writer = writer;
-
-      public void Advance(int count)
-          => Writer.Advance(count);
-
-      public Memory<byte> GetMemory(int sizeHint = 0)
-          => Writer.GetMemory(sizeHint);
-
-      public Span<byte> GetSpan(int sizeHint = 0)
-          => Writer.GetSpan(sizeHint);
-   }
-}

+ 74 - 63
frameworks/CSharp/appmpower/src/RawDb.cs

@@ -15,35 +15,36 @@ namespace appMpower
 
       public static async Task<World> LoadSingleQueryRow()
       {
-         using var pooledConnection = await PooledConnections.GetConnection(ConnectionStrings.OdbcConnection);
-         await pooledConnection.OpenAsync();
+         var pooledConnection = await PooledConnections.GetConnection(ConnectionStrings.OdbcConnection);
+         pooledConnection.Open();
 
          var (pooledCommand, _) = CreateReadCommand(pooledConnection);
+         var world = await ReadSingleRow(pooledCommand);
 
-         using (pooledCommand)
-         {
-            return await ReadSingleRow(pooledCommand);
-         }
+         pooledCommand.Release();
+         pooledConnection.Release();
+
+         return world;
       }
 
       public static async Task<World[]> LoadMultipleQueriesRows(int count)
       {
          var worlds = new World[count];
 
-         using var pooledConnection = await PooledConnections.GetConnection(ConnectionStrings.OdbcConnection);
-         await pooledConnection.OpenAsync();
+         var pooledConnection = await PooledConnections.GetConnection(ConnectionStrings.OdbcConnection);
+         pooledConnection.Open();
 
          var (pooledCommand, dbDataParameter) = CreateReadCommand(pooledConnection);
 
-         using (pooledCommand)
+         for (int i = 0; i < count; i++)
          {
-            for (int i = 0; i < count; i++)
-            {
-               worlds[i] = await ReadSingleRow(pooledCommand);
-               dbDataParameter.Value = _random.Next(1, 10001);
-            }
+            worlds[i] = await ReadSingleRow(pooledCommand);
+            dbDataParameter.Value = _random.Next(1, 10001);
          }
 
+         pooledCommand.Release();
+         pooledConnection.Release();
+
          return worlds;
       }
 
@@ -51,25 +52,25 @@ namespace appMpower
       {
          var fortunes = new List<Fortune>();
 
-         using var pooledConnection = await PooledConnections.GetConnection(ConnectionStrings.OdbcConnection);
-         await pooledConnection.OpenAsync();
+         var pooledConnection = await PooledConnections.GetConnection(ConnectionStrings.OdbcConnection);
+         pooledConnection.Open();
 
-         var pooledCommand = new PooledCommand("SELECT id, message FROM fortune", pooledConnection);
+         var pooledCommand = new PooledCommand("SELECT * FROM fortune", pooledConnection);
+         var dataReader = await pooledCommand.ExecuteReaderAsync(CommandBehavior.SingleResult);
 
-         using (pooledCommand)
+         while (dataReader.Read())
          {
-            using var dataReader = await pooledCommand.ExecuteReaderAsync(CommandBehavior.SingleResult);
-
-            while (await dataReader.ReadAsync())
-            {
-               fortunes.Add(new Fortune
-               (
-                   id: dataReader.GetInt32(0),
-                   message: dataReader.GetString(1)
-               ));
-            }
+            fortunes.Add(new Fortune
+            (
+                id: dataReader.GetInt32(0),
+                message: dataReader.GetString(1)
+            ));
          }
 
+         dataReader.Close();
+         pooledCommand.Release();
+         pooledConnection.Release();
+
          fortunes.Add(new Fortune(id: 0, message: "Additional fortune added at request time."));
          fortunes.Sort();
 
@@ -80,52 +81,53 @@ namespace appMpower
       {
          var worlds = new World[count];
 
-         using var pooledConnection = await PooledConnections.GetConnection(ConnectionStrings.OdbcConnection);
-         await pooledConnection.OpenAsync();
+         var pooledConnection = await PooledConnections.GetConnection(ConnectionStrings.OdbcConnection);
+         pooledConnection.Open();
 
          var (queryCommand, dbDataParameter) = CreateReadCommand(pooledConnection);
 
-         using (queryCommand)
+         for (int i = 0; i < count; i++)
          {
-            for (int i = 0; i < count; i++)
-            {
-               worlds[i] = await ReadSingleRow(queryCommand);
-               dbDataParameter.Value = _random.Next(1, 10001);
-            }
+            worlds[i] = await ReadSingleRow(queryCommand);
+            dbDataParameter.Value = _random.Next(1, 10001);
          }
 
+         queryCommand.Release();
+
          var updateCommand = new PooledCommand(PlatformBenchmarks.BatchUpdateString.Query(count), pooledConnection);
 
-         using (updateCommand)
+         var ids = PlatformBenchmarks.BatchUpdateString.Ids;
+         var randoms = PlatformBenchmarks.BatchUpdateString.Randoms;
+         // --- only for alternative update statement - will be used for MySQL
+         //var jds = PlatformBenchmarks.BatchUpdateString.Jds;
+
+         for (int i = 0; i < count; i++)
          {
-            var ids = PlatformBenchmarks.BatchUpdateString.Ids;
-            var randoms = PlatformBenchmarks.BatchUpdateString.Randoms;
-            var jds = PlatformBenchmarks.BatchUpdateString.Jds;
+            var randomNumber = _random.Next(1, 10001);
 
-            for (int i = 0; i < count; i++)
-            {
-               var randomNumber = _random.Next(1, 10001);
+            updateCommand.CreateParameter(ids[i], DbType.Int32, worlds[i].Id);
+            updateCommand.CreateParameter(randoms[i], DbType.Int32, randomNumber);
 
-               updateCommand.CreateParameter(ids[i], DbType.Int32, worlds[i].Id);
-               updateCommand.CreateParameter(randoms[i], DbType.Int32, randomNumber);
+            worlds[i].RandomNumber = randomNumber;
+         }
 
-               worlds[i].RandomNumber = randomNumber;
-            }
+         // --- only for alternative update statement - will be used for MySQL
+         //for (int i = 0; i < count; i++)
+         //{
+         //   updateCommand.CreateParameter(jds[i], DbType.Int32, worlds[i].Id);
+         //}
 
-            for (int i = 0; i < count; i++)
-            {
-               updateCommand.CreateParameter(jds[i], DbType.Int32, worlds[i].Id);
-            }
+         await updateCommand.ExecuteNonQueryAsync();
 
-            await updateCommand.ExecuteNonQueryAsync();
-         }
+         updateCommand.Release();
+         pooledConnection.Release();
 
          return worlds;
       }
 
       private static (PooledCommand pooledCommand, IDbDataParameter dbDataParameter) CreateReadCommand(PooledConnection pooledConnection)
       {
-         var pooledCommand = new PooledCommand("SELECT id, randomnumber 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));
 
          return (pooledCommand, dbDataParameter);
@@ -133,14 +135,19 @@ namespace appMpower
 
       private static async Task<World> ReadSingleRow(PooledCommand pooledCommand)
       {
-         using var dataReader = await pooledCommand.ExecuteReaderAsync(CommandBehavior.SingleRow);
-         await dataReader.ReadAsync();
+         var dataReader = await pooledCommand.ExecuteReaderAsync(CommandBehavior.SingleRow);
 
-         return new World
+         dataReader.Read();
+
+         var world = new World
          {
             Id = dataReader.GetInt32(0),
             RandomNumber = dataReader.GetInt32(1)
          };
+
+         dataReader.Close();
+
+         return world;
       }
 
       public static async Task<World[]> ReadMultipleRows(int count)
@@ -160,27 +167,27 @@ namespace appMpower
 
             for (int i = 0; i < count; i++)
             {
-               stringBuilder.Append("SELECT id, randomnumber FROM world WHERE id =?;");
+               stringBuilder.Append("SELECT * FROM world WHERE id=?;");
             }
 
             queryString = _queriesMultipleRows[count] = PlatformBenchmarks.StringBuilderCache.GetStringAndRelease(stringBuilder);
          }
 
-         using var pooledConnection = await PooledConnections.GetConnection(ConnectionStrings.OdbcConnection);
-         await pooledConnection.OpenAsync();
+         var pooledConnection = await PooledConnections.GetConnection(ConnectionStrings.OdbcConnection);
+         pooledConnection.Open();
 
-         using var pooledCommand = new PooledCommand(queryString, pooledConnection);
+         var pooledCommand = new PooledCommand(queryString, pooledConnection);
 
          for (int i = 0; i < count; i++)
          {
             pooledCommand.CreateParameter(ids[i], DbType.Int32, _random.Next(1, 10001));
          }
 
-         using var dataReader = await pooledCommand.ExecuteReaderAsync(CommandBehavior.Default);
+         var dataReader = await pooledCommand.ExecuteReaderAsync(CommandBehavior.Default);
 
          do
          {
-            await dataReader.ReadAsync();
+            dataReader.Read();
 
             worlds[j] = new World
             {
@@ -191,6 +198,10 @@ namespace appMpower
             j++;
          } while (await dataReader.NextResultAsync());
 
+         dataReader.Close();
+         pooledCommand.Release();
+         pooledConnection.Release();
+
          return worlds;
       }
    }