فهرست منبع

Merge pull request #19 from TechEmpower/master

aa
三刀 3 سال پیش
والد
کامیت
57ca747e38
100فایلهای تغییر یافته به همراه2494 افزوده شده و 738 حذف شده
  1. 5 5
      frameworks/CSharp/appmpower/appmpower-odbc-my.dockerfile
  2. 8 8
      frameworks/CSharp/appmpower/appmpower-odbc-pg.dockerfile
  3. 2 2
      frameworks/CSharp/appmpower/appmpower.dockerfile
  4. 5 3
      frameworks/CSharp/appmpower/benchmark_config.json
  5. 2 0
      frameworks/CSharp/appmpower/config.toml
  6. 11 0
      frameworks/CSharp/appmpower/src/CachedWorld.cs
  7. 15 0
      frameworks/CSharp/appmpower/src/CachedWorldSerializer.cs
  8. 13 0
      frameworks/CSharp/appmpower/src/Db/DataProvider.cs
  9. 40 38
      frameworks/CSharp/appmpower/src/Db/PooledCommand.cs
  10. 32 32
      frameworks/CSharp/appmpower/src/Db/PooledConnection.cs
  11. 12 2
      frameworks/CSharp/appmpower/src/Db/PooledConnections.cs
  12. 17 0
      frameworks/CSharp/appmpower/src/HttpApplication.cs
  13. 257 0
      frameworks/CSharp/appmpower/src/Memory/CacheEntry.cs
  14. 32 0
      frameworks/CSharp/appmpower/src/Memory/CacheEntryHelper.cs
  15. 62 0
      frameworks/CSharp/appmpower/src/Memory/CacheEntryState.cs
  16. 140 0
      frameworks/CSharp/appmpower/src/Memory/CacheEntryTokens.cs
  17. 520 0
      frameworks/CSharp/appmpower/src/Memory/MemoryCache.cs
  18. 67 0
      frameworks/CSharp/appmpower/src/Memory/MemoryCacheOptions.cs
  19. 24 0
      frameworks/CSharp/appmpower/src/Microsoft/CachKey.cs
  20. 101 5
      frameworks/CSharp/appmpower/src/RawDb.cs
  21. 15 7
      frameworks/CSharp/appmpower/src/appMpower.csproj
  22. 4 4
      frameworks/CSharp/genhttp/Benchmarks/Benchmarks.csproj
  23. 1 1
      frameworks/CSharp/genhttp/Benchmarks/Model/DatabaseContext.cs
  24. 4 1
      frameworks/CSharp/genhttp/Benchmarks/Tests/FortuneHandler.cs
  25. 1 1
      frameworks/CSharp/genhttp/Benchmarks/Tests/QueryResource.cs
  26. 12 13
      frameworks/CSharp/genhttp/genhttp.dockerfile
  27. 6 0
      frameworks/FSharp/giraffe/giraffe-newtonsoft.dockerfile
  28. 6 0
      frameworks/FSharp/giraffe/giraffe-utf8json.dockerfile
  29. 6 0
      frameworks/FSharp/giraffe/giraffe.dockerfile
  30. 2 3
      frameworks/Java/helidon/README.md
  31. 8 1
      frameworks/Java/helidon/helidon.dockerfile
  32. 30 19
      frameworks/Java/helidon/pom.xml
  33. 14 38
      frameworks/Java/helidon/src/main/java/io/helidon/benchmark/Main.java
  34. 3 2
      frameworks/Java/helidon/src/main/java/io/helidon/benchmark/models/DbRepository.java
  35. 54 47
      frameworks/Java/helidon/src/main/java/io/helidon/benchmark/models/JdbcRepository.java
  36. 3 1
      frameworks/Java/helidon/src/main/java/io/helidon/benchmark/models/World.java
  37. 31 43
      frameworks/Java/helidon/src/main/java/io/helidon/benchmark/services/DbService.java
  38. 21 26
      frameworks/Java/helidon/src/main/java/io/helidon/benchmark/services/FortuneService.java
  39. 20 8
      frameworks/Java/helidon/src/main/java/io/helidon/benchmark/services/JsonService.java
  40. 18 4
      frameworks/Java/helidon/src/main/java/io/helidon/benchmark/services/PlainTextService.java
  41. 4 4
      frameworks/Java/helidon/src/main/resources/application.yaml
  42. 0 20
      frameworks/Java/helidon/src/main/resources/fortunes.mustache
  43. 24 0
      frameworks/Java/helidon/src/main/resources/views/fortunes.rocker.html
  44. 1 1
      frameworks/JavaScript/express/README.md
  45. 1 1
      frameworks/JavaScript/express/express-mongodb.dockerfile
  46. 1 1
      frameworks/JavaScript/express/express-mysql.dockerfile
  47. 1 1
      frameworks/JavaScript/express/express-postgres.dockerfile
  48. 1 1
      frameworks/JavaScript/express/express.dockerfile
  49. 1 1
      frameworks/JavaScript/hapi/hapi-mysql.dockerfile
  50. 1 1
      frameworks/JavaScript/hapi/hapi-nginx.dockerfile
  51. 1 1
      frameworks/JavaScript/hapi/hapi-postgres.dockerfile
  52. 1 1
      frameworks/JavaScript/hapi/hapi.dockerfile
  53. 1 1
      frameworks/JavaScript/nodejs/README.md
  54. 1 1
      frameworks/JavaScript/nodejs/nodejs.dockerfile
  55. 29 4
      frameworks/OCaml/httpaf/httpaf.dockerfile
  56. 4 4
      frameworks/PHP/comet/comet-mysql.dockerfile
  57. 4 4
      frameworks/PHP/comet/comet.dockerfile
  58. 4 4
      frameworks/PHP/kumbiaphp/kumbiaphp-workerman-mysql.dockerfile
  59. 3 3
      frameworks/PHP/php-ngx/php-ngx-async.dockerfile
  60. 3 3
      frameworks/PHP/php-ngx/php-ngx-mysql.dockerfile
  61. 3 3
      frameworks/PHP/php-ngx/php-ngx-pgsql.dockerfile
  62. 3 3
      frameworks/PHP/php-ngx/php-ngx.dockerfile
  63. 4 4
      frameworks/PHP/webman/webman.dockerfile
  64. 267 1
      frameworks/Rust/axum/Cargo.lock
  65. 2 1
      frameworks/Rust/axum/Cargo.toml
  66. 4 1
      frameworks/Rust/axum/axum-bb8.dockerfile
  67. 4 1
      frameworks/Rust/axum/axum-mongo.dockerfile
  68. 5 1
      frameworks/Rust/axum/axum-sqlx.dockerfile
  69. 4 1
      frameworks/Rust/axum/axum.dockerfile
  70. 3 0
      frameworks/Rust/axum/run.sh
  71. 1 0
      frameworks/Rust/axum/src/common.rs
  72. 0 17
      frameworks/Rust/axum/src/common_handlers.rs
  73. 2 1
      frameworks/Rust/axum/src/database_bb8.rs
  74. 2 1
      frameworks/Rust/axum/src/database_sqlx.rs
  75. 20 6
      frameworks/Rust/axum/src/main.rs
  76. 3 8
      frameworks/Rust/axum/src/main_bb8.rs
  77. 9 11
      frameworks/Rust/axum/src/main_mongo.rs
  78. 3 8
      frameworks/Rust/axum/src/main_sqlx.rs
  79. 35 0
      frameworks/Rust/axum/src/server.rs
  80. 105 110
      frameworks/Rust/xitca-web/Cargo.lock
  81. 19 14
      frameworks/Rust/xitca-web/Cargo.toml
  82. 22 31
      frameworks/Rust/xitca-web/src/db_diesel.rs
  83. 139 69
      frameworks/Rust/xitca-web/src/main.rs
  84. 46 10
      frameworks/Rust/xitca-web/src/main_diesel.rs
  85. 9 41
      frameworks/Rust/xitca-web/src/util.rs
  86. 2 2
      frameworks/Rust/xitca-web/xitca-web-diesel.dockerfile
  87. 2 2
      frameworks/Rust/xitca-web/xitca-web.dockerfile
  88. 3 3
      frameworks/Scala/http4s/README.md
  89. 6 5
      frameworks/Scala/http4s/blaze/build.sbt
  90. 0 0
      frameworks/Scala/http4s/blaze/project/build.properties
  91. 0 0
      frameworks/Scala/http4s/blaze/project/plugins.sbt
  92. 0 0
      frameworks/Scala/http4s/blaze/src/main/resources/application.properties
  93. 0 0
      frameworks/Scala/http4s/blaze/src/main/resources/logback.xml
  94. 0 0
      frameworks/Scala/http4s/blaze/src/main/scala/http4s/techempower/benchmark/DatabaseService.scala
  95. 2 6
      frameworks/Scala/http4s/blaze/src/main/scala/http4s/techempower/benchmark/WebServer.scala
  96. 0 0
      frameworks/Scala/http4s/blaze/src/main/twirl/index.scala.html
  97. 11 5
      frameworks/Scala/http4s/http4s.dockerfile
  98. 5 1
      frameworks/Swift/vapor/README.md
  99. 38 0
      frameworks/Swift/vapor/benchmark_config.json
  100. 1 0
      frameworks/Swift/vapor/config.toml

+ 5 - 5
frameworks/CSharp/appmpower/appmpower-odbc-my.dockerfile

@@ -1,4 +1,4 @@
-FROM mcr.microsoft.com/dotnet/sdk:5.0 AS build
+FROM mcr.microsoft.com/dotnet/sdk:6.0.100 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 libtinfo5
 RUN apt-get install -y make build-essential libssl-dev zlib1g-dev libbz2-dev \
 RUN apt-get install -y make build-essential libssl-dev zlib1g-dev libbz2-dev \
@@ -22,7 +22,7 @@ COPY src .
 RUN dotnet publish -c Release -o out -r linux-x64 /p:Database=mysql
 RUN dotnet publish -c Release -o out -r linux-x64 /p:Database=mysql
 
 
 # Construct the actual image that will run
 # Construct the actual image that will run
-FROM mcr.microsoft.com/dotnet/aspnet:5.0 AS runtime
+FROM mcr.microsoft.com/dotnet/aspnet:6.0.0 AS runtime
 
 
 RUN apt-get update
 RUN apt-get update
 # The following installs standard versions unixodbc 2.3.6 and pgsqlodbc 11
 # The following installs standard versions unixodbc 2.3.6 and pgsqlodbc 11
@@ -32,9 +32,9 @@ RUN apt-get install -y unixodbc wget curl
 
 
 WORKDIR /odbc
 WORKDIR /odbc
 
 
-RUN curl -L -o mariadb-connector-odbc-3.1.13-debian-9-stretch-amd64.tar.gz https://downloads.mariadb.com/Connectors/odbc/connector-odbc-3.1.13/mariadb-connector-odbc-3.1.13-debian-9-stretch-amd64.tar.gz
-RUN tar -xvzf mariadb-connector-odbc-3.1.13-debian-9-stretch-amd64.tar.gz
-RUN cp mariadb-connector-odbc-3.1.13-debian-9-stretch-amd64/lib/mariadb/libm* /usr/lib/
+RUN curl -L -o mariadb-connector-odbc-3.1.14-debian-9-stretch-amd64.tar.gz https://downloads.mariadb.com/Connectors/odbc/connector-odbc-3.1.14/mariadb-connector-odbc-3.1.14-debian-9-stretch-amd64.tar.gz
+RUN tar -xvzf mariadb-connector-odbc-3.1.14-debian-9-stretch-amd64.tar.gz
+RUN cp mariadb-connector-odbc-3.1.14-debian-9-stretch-amd64/lib/mariadb/libm* /usr/lib/
 
 
 COPY --from=build /usr/local/unixODBC /usr/local/unixODBC
 COPY --from=build /usr/local/unixODBC /usr/local/unixODBC
 
 

+ 8 - 8
frameworks/CSharp/appmpower/appmpower-odbc-pg.dockerfile

@@ -1,4 +1,4 @@
-FROM mcr.microsoft.com/dotnet/sdk:5.0 AS build
+FROM mcr.microsoft.com/dotnet/sdk:6.0.100 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 libtinfo5
 RUN apt-get install -y make build-essential libssl-dev zlib1g-dev libbz2-dev \
 RUN apt-get install -y make build-essential libssl-dev zlib1g-dev libbz2-dev \
@@ -8,15 +8,15 @@ RUN apt-get install -y make build-essential libssl-dev zlib1g-dev libbz2-dev \
 WORKDIR /odbc
 WORKDIR /odbc
 
 
 # To compile the latest postgresql odbc driver, postgresql itself needs to be installed
 # 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 postgresql-14.1.tar.gz https://ftp.postgresql.org/pub/source/v14.1/postgresql-14.1.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 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 curl -L -o psqlodbc-13.02.0000.tar.gz https://ftp.postgresql.org/pub/odbc/versions/src/psqlodbc-13.02.0000.tar.gz
 
 
-RUN tar -xvf postgresql-13.3.tar.gz
+RUN tar -xvf postgresql-14.1.tar.gz
 RUN tar -xvf unixODBC-2.3.9.tar.gz
 RUN tar -xvf unixODBC-2.3.9.tar.gz
-RUN tar -xvf psqlodbc-13.01.0000.tar.gz
+RUN tar -xvf psqlodbc-13.02.0000.tar.gz
 
 
-WORKDIR /odbc/postgresql-13.3
+WORKDIR /odbc/postgresql-14.1
 RUN ./configure
 RUN ./configure
 RUN make
 RUN make
 RUN make install
 RUN make install
@@ -30,7 +30,7 @@ RUN make install
 
 
 ENV PATH=/usr/local/unixODBC/lib:$PATH
 ENV PATH=/usr/local/unixODBC/lib:$PATH
 
 
-WORKDIR /odbc/psqlodbc-13.01.0000
+WORKDIR /odbc/psqlodbc-13.02.0000
 RUN ./configure --with-unixodbc=/usr/local/unixODBC --with-libpq=/usr/local/pgsql --prefix=/usr/local/pgsqlodbc
 RUN ./configure --with-unixodbc=/usr/local/unixODBC --with-libpq=/usr/local/pgsql --prefix=/usr/local/pgsqlodbc
 RUN make
 RUN make
 RUN make install
 RUN make install
@@ -40,7 +40,7 @@ COPY src .
 RUN dotnet publish -c Release -o out -r linux-x64  /p:Database=postgresql
 RUN dotnet publish -c Release -o out -r linux-x64  /p:Database=postgresql
 
 
 # Construct the actual image that will run
 # Construct the actual image that will run
-FROM mcr.microsoft.com/dotnet/aspnet:5.0 AS runtime
+FROM mcr.microsoft.com/dotnet/aspnet:6.0.0 AS runtime
 
 
 RUN apt-get update
 RUN apt-get update
 # The following installs standard versions unixodbc 2.3.6 and pgsqlodbc 11
 # The following installs standard versions unixodbc 2.3.6 and pgsqlodbc 11

+ 2 - 2
frameworks/CSharp/appmpower/appmpower.dockerfile

@@ -1,4 +1,4 @@
-FROM mcr.microsoft.com/dotnet/sdk:5.0 AS build
+FROM mcr.microsoft.com/dotnet/sdk:6.0.100 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 libtinfo5
 
 
@@ -7,7 +7,7 @@ COPY src .
 RUN dotnet publish -c Release -o out -r linux-x64
 RUN dotnet publish -c Release -o out -r linux-x64
 
 
 # Construct the actual image that will run
 # Construct the actual image that will run
-FROM mcr.microsoft.com/dotnet/aspnet:5.0 AS runtime
+FROM mcr.microsoft.com/dotnet/aspnet:6.0.0 AS runtime
 RUN apt-get update
 RUN apt-get update
 
 
 WORKDIR /app
 WORKDIR /app

+ 5 - 3
frameworks/CSharp/appmpower/benchmark_config.json

@@ -9,7 +9,7 @@
         "approach": "Realistic",
         "approach": "Realistic",
         "classification": "Platform",
         "classification": "Platform",
         "database": "None",
         "database": "None",
-        "framework": "ASP.NET Core",
+        "framework": "appmpower",
         "language": "C#",
         "language": "C#",
         "orm": "Raw",
         "orm": "Raw",
         "platform": ".NET",
         "platform": ".NET",
@@ -26,11 +26,12 @@
         "query_url": "/queries?c=",
         "query_url": "/queries?c=",
         "update_url": "/updates?c=",
         "update_url": "/updates?c=",
         "fortune_url": "/fortunes",
         "fortune_url": "/fortunes",
+        "cached_query_url": "/cached-worlds?c=",
         "port": 8080,
         "port": 8080,
         "approach": "Realistic",
         "approach": "Realistic",
         "classification": "Platform",
         "classification": "Platform",
         "database": "Postgres",
         "database": "Postgres",
-        "framework": "ASP.NET Core",
+        "framework": "appmpower",
         "language": "C#",
         "language": "C#",
         "orm": "Raw",
         "orm": "Raw",
         "platform": ".NET",
         "platform": ".NET",
@@ -47,11 +48,12 @@
         "query_url": "/queries?c=",
         "query_url": "/queries?c=",
         "update_url": "/updates?c=",
         "update_url": "/updates?c=",
         "fortune_url": "/fortunes",
         "fortune_url": "/fortunes",
+        "cached_query_url": "/cached-worlds?c=",
         "port": 8080,
         "port": 8080,
         "approach": "Realistic",
         "approach": "Realistic",
         "classification": "Platform",
         "classification": "Platform",
         "database": "MySQL",
         "database": "MySQL",
-        "framework": "ASP.NET Core",
+        "framework": "appmpower",
         "language": "C#",
         "language": "C#",
         "orm": "Raw",
         "orm": "Raw",
         "platform": ".NET",
         "platform": ".NET",

+ 2 - 0
frameworks/CSharp/appmpower/config.toml

@@ -19,6 +19,7 @@ urls.db = "/db"
 urls.query = "/queries?c="
 urls.query = "/queries?c="
 urls.update = "/updates?c="
 urls.update = "/updates?c="
 urls.fortune = "/fortunes"
 urls.fortune = "/fortunes"
+urls.cached_query = "/cached-worlds?c="
 approach = "Realistic"
 approach = "Realistic"
 classification = "Micro"
 classification = "Micro"
 database = "Postgres"
 database = "Postgres"
@@ -34,6 +35,7 @@ urls.db = "/db"
 urls.query = "/queries?c="
 urls.query = "/queries?c="
 urls.update = "/updates?c="
 urls.update = "/updates?c="
 urls.fortune = "/fortunes"
 urls.fortune = "/fortunes"
+urls.cached_query = "/cached-worlds?c="
 approach = "Realistic"
 approach = "Realistic"
 classification = "Micro"
 classification = "Micro"
 database = "MySQL"
 database = "MySQL"

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

@@ -0,0 +1,11 @@
+namespace appMpower
+{
+   public struct 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 };
+   }
+}

+ 15 - 0
frameworks/CSharp/appmpower/src/CachedWorldSerializer.cs

@@ -0,0 +1,15 @@
+using System.Text.Json;
+
+namespace appMpower
+{
+   public class CachedWorldSerializer : Kestrel.IJsonSerializer<CachedWorld>
+   {
+      public void Serialize(Utf8JsonWriter utf8JsonWriter, CachedWorld world)
+      {
+         utf8JsonWriter.WriteStartObject();
+         utf8JsonWriter.WriteNumber("id", world.Id);
+         utf8JsonWriter.WriteNumber("randomNumber", world.RandomNumber);
+         utf8JsonWriter.WriteEndObject();
+      }
+   }
+}

+ 13 - 0
frameworks/CSharp/appmpower/src/Db/DataProvider.cs

@@ -0,0 +1,13 @@
+namespace appMpower.Db
+{
+   public static class DataProvider
+   {
+#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"; 
+#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";
+#endif
+   }
+}

+ 40 - 38
frameworks/CSharp/appmpower/src/Db/PooledCommand.cs

@@ -7,12 +7,12 @@ namespace appMpower.Db
 {
 {
    public class PooledCommand : IDbCommand
    public class PooledCommand : IDbCommand
    {
    {
-      private OdbcCommand _odbcCommand;
+      private IDbCommand _dbCommand;
       private PooledConnection _pooledConnection;
       private PooledConnection _pooledConnection;
 
 
       public PooledCommand(PooledConnection pooledConnection)
       public PooledCommand(PooledConnection pooledConnection)
       {
       {
-         _odbcCommand = (OdbcCommand)pooledConnection.CreateCommand();
+         _dbCommand = pooledConnection.CreateCommand();
          _pooledConnection = pooledConnection;
          _pooledConnection = pooledConnection;
       }
       }
 
 
@@ -21,21 +21,21 @@ namespace appMpower.Db
          pooledConnection.GetCommand(commandText, this);
          pooledConnection.GetCommand(commandText, this);
       }
       }
 
 
-      internal PooledCommand(OdbcCommand odbcCommand, PooledConnection pooledConnection)
+      internal PooledCommand(IDbCommand dbCommand, PooledConnection pooledConnection)
       {
       {
-         _odbcCommand = odbcCommand;
+         _dbCommand = dbCommand;
          _pooledConnection = pooledConnection;
          _pooledConnection = pooledConnection;
       }
       }
 
 
-      internal OdbcCommand OdbcCommand
+      internal IDbCommand DbCommand
       {
       {
          get
          get
          {
          {
-            return _odbcCommand;
+            return _dbCommand;
          }
          }
          set
          set
          {
          {
-            _odbcCommand = value;
+            _dbCommand = value;
          }
          }
       }
       }
 
 
@@ -55,11 +55,11 @@ namespace appMpower.Db
       {
       {
          get
          get
          {
          {
-            return _odbcCommand.CommandText;
+            return _dbCommand.CommandText;
          }
          }
          set
          set
          {
          {
-            _odbcCommand.CommandText = value;
+            _dbCommand.CommandText = value;
          }
          }
       }
       }
 
 
@@ -67,22 +67,22 @@ namespace appMpower.Db
       {
       {
          get
          get
          {
          {
-            return _odbcCommand.CommandTimeout;
+            return _dbCommand.CommandTimeout;
          }
          }
          set
          set
          {
          {
-            _odbcCommand.CommandTimeout = value;
+            _dbCommand.CommandTimeout = value;
          }
          }
       }
       }
       public CommandType CommandType
       public CommandType CommandType
       {
       {
          get
          get
          {
          {
-            return _odbcCommand.CommandType;
+            return _dbCommand.CommandType;
          }
          }
          set
          set
          {
          {
-            _odbcCommand.CommandType = value;
+            _dbCommand.CommandType = value;
          }
          }
       }
       }
 
 
@@ -91,11 +91,11 @@ namespace appMpower.Db
       {
       {
          get
          get
          {
          {
-            return _odbcCommand.Connection;
+            return _dbCommand.Connection;
          }
          }
          set
          set
          {
          {
-            _odbcCommand.Connection = (OdbcConnection?)value;
+            _dbCommand.Connection = (IDbConnection?)value;
          }
          }
       }
       }
 #nullable disable
 #nullable disable
@@ -105,7 +105,7 @@ namespace appMpower.Db
       {
       {
          get
          get
          {
          {
-            return _odbcCommand.Parameters;
+            return _dbCommand.Parameters;
          }
          }
       }
       }
 
 
@@ -114,11 +114,11 @@ namespace appMpower.Db
       {
       {
          get
          get
          {
          {
-            return _odbcCommand.Transaction;
+            return _dbCommand.Transaction;
          }
          }
          set
          set
          {
          {
-            _odbcCommand.Transaction = (OdbcTransaction?)value;
+            _dbCommand.Transaction = (IDbTransaction?)value;
          }
          }
       }
       }
 #nullable disable
 #nullable disable
@@ -127,80 +127,82 @@ namespace appMpower.Db
       {
       {
          get
          get
          {
          {
-            return _odbcCommand.UpdatedRowSource;
+            return _dbCommand.UpdatedRowSource;
          }
          }
          set
          set
          {
          {
-            _odbcCommand.UpdatedRowSource = value;
+            _dbCommand.UpdatedRowSource = value;
          }
          }
       }
       }
       public void Cancel()
       public void Cancel()
       {
       {
-         _odbcCommand.Cancel();
+         _dbCommand.Cancel();
       }
       }
 
 
       public IDbDataParameter CreateParameter()
       public IDbDataParameter CreateParameter()
       {
       {
-         return _odbcCommand.CreateParameter();
+         return _dbCommand.CreateParameter();
       }
       }
 
 
       public IDbDataParameter CreateParameter(string name, DbType dbType, object value)
       public IDbDataParameter CreateParameter(string name, DbType dbType, object value)
       {
       {
-         OdbcParameter odbcParameter = null;
+         IDbDataParameter dbDataParameter = null;
 
 
          if (this.Parameters.Contains(name))
          if (this.Parameters.Contains(name))
          {
          {
-            odbcParameter = (OdbcParameter)this.Parameters[name];
-            odbcParameter.Value = value;
+            dbDataParameter = this.Parameters[name] as IDbDataParameter;
+            dbDataParameter.Value = value;
          }
          }
          else
          else
          {
          {
-            odbcParameter = _odbcCommand.CreateParameter();
+            dbDataParameter = _dbCommand.CreateParameter();
 
 
-            odbcParameter.ParameterName = name;
-            odbcParameter.DbType = dbType;
-            odbcParameter.Value = value;
-            this.Parameters.Add(odbcParameter);
+            dbDataParameter.ParameterName = name;
+            dbDataParameter.DbType = dbType;
+            dbDataParameter.Value = value;
+            this.Parameters.Add(dbDataParameter);
          }
          }
 
 
-         return odbcParameter;
+         return dbDataParameter;
       }
       }
 
 
       public int ExecuteNonQuery()
       public int ExecuteNonQuery()
       {
       {
-         return _odbcCommand.ExecuteNonQuery();
+         return _dbCommand.ExecuteNonQuery();
       }
       }
 
 
       public IDataReader ExecuteReader()
       public IDataReader ExecuteReader()
       {
       {
-         return _odbcCommand.ExecuteReader();
+         return _dbCommand.ExecuteReader();
       }
       }
 
 
       public async Task<int> ExecuteNonQueryAsync()
       public async Task<int> ExecuteNonQueryAsync()
       {
       {
-         return await _odbcCommand.ExecuteNonQueryAsync();
+         if (DataProvider.IsOdbcConnection) return await (_dbCommand as OdbcCommand).ExecuteNonQueryAsync();
+         return await (_dbCommand as DbCommand).ExecuteNonQueryAsync();
       }
       }
 
 
       public async Task<DbDataReader> ExecuteReaderAsync(CommandBehavior behavior)
       public async Task<DbDataReader> ExecuteReaderAsync(CommandBehavior behavior)
       {
       {
-         return await _odbcCommand.ExecuteReaderAsync(behavior);
+         if (DataProvider.IsOdbcConnection) return await (_dbCommand as OdbcCommand).ExecuteReaderAsync(behavior);
+         return await (_dbCommand as DbCommand).ExecuteReaderAsync(behavior);
       }
       }
 
 
       public IDataReader ExecuteReader(CommandBehavior behavior)
       public IDataReader ExecuteReader(CommandBehavior behavior)
       {
       {
-         return _odbcCommand.ExecuteReader(behavior);
+         return _dbCommand.ExecuteReader(behavior);
       }
       }
 
 
 #nullable enable
 #nullable enable
       public object? ExecuteScalar()
       public object? ExecuteScalar()
       {
       {
-         return _odbcCommand.ExecuteScalar();
+         return _dbCommand.ExecuteScalar();
       }
       }
 #nullable disable
 #nullable disable
 
 
       public void Prepare()
       public void Prepare()
       {
       {
-         _odbcCommand.Prepare();
+         _dbCommand.Prepare();
       }
       }
 
 
       public void Release()
       public void Release()

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

@@ -1,7 +1,6 @@
-using System;
 using System.Collections.Concurrent;
 using System.Collections.Concurrent;
 using System.Data;
 using System.Data;
-using System.Data.Odbc;
+using System.Data.Common;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
 
 
 namespace appMpower.Db
 namespace appMpower.Db
@@ -10,16 +9,16 @@ namespace appMpower.Db
    {
    {
       private bool _released = false;
       private bool _released = false;
       private short _number = 0;
       private short _number = 0;
-      private OdbcConnection _odbcConnection;
+      private IDbConnection _dbConnection;
       private ConcurrentDictionary<string, PooledCommand> _pooledCommands;
       private ConcurrentDictionary<string, PooledCommand> _pooledCommands;
 
 
       internal PooledConnection()
       internal PooledConnection()
       {
       {
       }
       }
 
 
-      internal PooledConnection(OdbcConnection odbcConnection)
+      internal PooledConnection(IDbConnection dbConnection)
       {
       {
-         _odbcConnection = odbcConnection;
+         _dbConnection = dbConnection;
          _pooledCommands = new ConcurrentDictionary<string, PooledCommand>();
          _pooledCommands = new ConcurrentDictionary<string, PooledCommand>();
       }
       }
 
 
@@ -47,15 +46,15 @@ namespace appMpower.Db
          }
          }
       }
       }
 
 
-      public OdbcConnection OdbcConnection
+      public IDbConnection DbConnection
       {
       {
          get
          get
          {
          {
-            return _odbcConnection;
+            return _dbConnection;
          }
          }
          set
          set
          {
          {
-            _odbcConnection = value;
+            _dbConnection = value;
          }
          }
       }
       }
 
 
@@ -63,11 +62,11 @@ namespace appMpower.Db
       {
       {
          get
          get
          {
          {
-            return _odbcConnection.ConnectionString;
+            return _dbConnection.ConnectionString;
          }
          }
          set
          set
          {
          {
-            _odbcConnection.ConnectionString = value;
+            _dbConnection.ConnectionString = value;
          }
          }
       }
       }
 
 
@@ -75,7 +74,7 @@ namespace appMpower.Db
       {
       {
          get
          get
          {
          {
-            return _odbcConnection.ConnectionTimeout;
+            return _dbConnection.ConnectionTimeout;
          }
          }
       }
       }
 
 
@@ -83,7 +82,7 @@ namespace appMpower.Db
       {
       {
          get
          get
          {
          {
-            return _odbcConnection.Database;
+            return _dbConnection.Database;
          }
          }
       }
       }
 
 
@@ -91,7 +90,7 @@ namespace appMpower.Db
       {
       {
          get
          get
          {
          {
-            return _odbcConnection.State;
+            return _dbConnection.State;
          }
          }
       }
       }
 
 
@@ -109,46 +108,41 @@ namespace appMpower.Db
 
 
       public IDbTransaction BeginTransaction()
       public IDbTransaction BeginTransaction()
       {
       {
-         return _odbcConnection.BeginTransaction();
+         return _dbConnection.BeginTransaction();
       }
       }
 
 
       public IDbTransaction BeginTransaction(IsolationLevel il)
       public IDbTransaction BeginTransaction(IsolationLevel il)
       {
       {
-         return _odbcConnection.BeginTransaction(il);
+         return _dbConnection.BeginTransaction(il);
       }
       }
 
 
       public void ChangeDatabase(string databaseName)
       public void ChangeDatabase(string databaseName)
       {
       {
-         _odbcConnection.ChangeDatabase(databaseName);
+         _dbConnection.ChangeDatabase(databaseName);
       }
       }
 
 
       public void Close()
       public void Close()
       {
       {
-         _odbcConnection.Close();
+         _dbConnection.Close();
          _released = true;
          _released = true;
       }
       }
 
 
       public IDbCommand CreateCommand()
       public IDbCommand CreateCommand()
       {
       {
-         return _odbcConnection.CreateCommand();
-      }
-
-      public OdbcCommand CreateOdbcCommand()
-      {
-         return _odbcConnection.CreateCommand();
+         return _dbConnection.CreateCommand();
       }
       }
 
 
       public void Open()
       public void Open()
       {
       {
-         if (_odbcConnection.State == ConnectionState.Closed)
+         if (_dbConnection.State == ConnectionState.Closed)
          {
          {
-            _odbcConnection.Open();
+            _dbConnection.Open();
          }
          }
       }
       }
 
 
       public void Release()
       public void Release()
       {
       {
-         if (!_released && _odbcConnection.State == ConnectionState.Open)
+         if (!_released && _dbConnection.State == ConnectionState.Open)
          {
          {
             PooledConnections.Release(this);
             PooledConnections.Release(this);
          }
          }
@@ -156,7 +150,7 @@ namespace appMpower.Db
 
 
       public void Dispose()
       public void Dispose()
       {
       {
-         if (!_released && _odbcConnection.State == ConnectionState.Open)
+         if (!_released && _dbConnection.State == ConnectionState.Open)
          {
          {
             PooledConnections.Dispose(this);
             PooledConnections.Dispose(this);
          }
          }
@@ -164,9 +158,9 @@ namespace appMpower.Db
 
 
       public async Task OpenAsync()
       public async Task OpenAsync()
       {
       {
-         if (_odbcConnection.State == ConnectionState.Closed)
+         if (_dbConnection.State == ConnectionState.Closed)
          {
          {
-            await _odbcConnection.OpenAsync();
+            await (_dbConnection as DbConnection).OpenAsync();
          }
          }
       }
       }
 
 
@@ -176,15 +170,21 @@ namespace appMpower.Db
 
 
          if (_pooledCommands.TryRemove(commandText, out internalCommand))
          if (_pooledCommands.TryRemove(commandText, out internalCommand))
          {
          {
-            pooledCommand.OdbcCommand = internalCommand.OdbcCommand;
+            pooledCommand.DbCommand = internalCommand.DbCommand;
             pooledCommand.PooledConnection = internalCommand.PooledConnection;
             pooledCommand.PooledConnection = internalCommand.PooledConnection;
          }
          }
          else
          else
          {
          {
-            pooledCommand.OdbcCommand = new OdbcCommand(commandText, this.OdbcConnection);
-            pooledCommand.OdbcCommand.Prepare();
+            pooledCommand.DbCommand = this.DbConnection.CreateCommand();
+            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();
+            }
+
             //Console.WriteLine("prepare pool connection: " + this._number + " for command " + _pooledCommands.Count);
             //Console.WriteLine("prepare pool connection: " + this._number + " for command " + _pooledCommands.Count);
          }
          }
 
 

+ 12 - 2
frameworks/CSharp/appmpower/src/Db/PooledConnections.cs

@@ -38,7 +38,17 @@ namespace appMpower.Db
          else
          else
          {
          {
             pooledConnection = new PooledConnection();
             pooledConnection = new PooledConnection();
-            pooledConnection.OdbcConnection = new OdbcConnection(connectionString);
+
+            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);
+            }
+
             _createdConnections++;
             _createdConnections++;
 
 
             if (_createdConnections == _maxConnections) _connectionsCreated = true;
             if (_createdConnections == _maxConnections) _connectionsCreated = true;
@@ -63,7 +73,7 @@ namespace appMpower.Db
       {
       {
          PooledConnection newPooledConnection = new PooledConnection();
          PooledConnection newPooledConnection = new PooledConnection();
 
 
-         newPooledConnection.OdbcConnection = pooledConnection.OdbcConnection;
+         newPooledConnection.DbConnection = pooledConnection.DbConnection;
          newPooledConnection.Number = pooledConnection.Number;
          newPooledConnection.Number = pooledConnection.Number;
          newPooledConnection.PooledCommands = pooledConnection.PooledCommands;
          newPooledConnection.PooledCommands = pooledConnection.PooledCommands;
 
 

+ 17 - 0
frameworks/CSharp/appmpower/src/HttpApplication.cs

@@ -13,6 +13,7 @@ namespace appMpower
       public static readonly byte[] _plainText = Encoding.UTF8.GetBytes("Hello, World!");
       public static readonly byte[] _plainText = Encoding.UTF8.GetBytes("Hello, World!");
       private readonly static JsonMessageSerializer _jsonMessageSerializer = new JsonMessageSerializer();
       private readonly static JsonMessageSerializer _jsonMessageSerializer = new JsonMessageSerializer();
       private readonly static WorldSerializer _worldSerializer = new WorldSerializer();
       private readonly static WorldSerializer _worldSerializer = new WorldSerializer();
+      private readonly static CachedWorldSerializer _cachedWorldSerializer = new CachedWorldSerializer();
 
 
       public IFeatureCollection CreateContext(IFeatureCollection featureCollection)
       public IFeatureCollection CreateContext(IFeatureCollection featureCollection)
       {
       {
@@ -85,6 +86,22 @@ namespace appMpower
                Json.RenderMany(httpResponse.Headers, httpResponseBody.Writer, await RawDb.LoadMultipleUpdatesRows(count), _worldSerializer);
                Json.RenderMany(httpResponse.Headers, httpResponseBody.Writer, await RawDb.LoadMultipleUpdatesRows(count), _worldSerializer);
                return;
                return;
             }
             }
+            else if (pathStringLength == 14 && pathStringStart == "c")
+            {
+               int count = 1;
+
+               if (!Int32.TryParse(request.QueryString.Substring(request.QueryString.LastIndexOf("=") + 1), out count) || count < 1)
+               {
+                  count = 1;
+               }
+               else if (count > 500)
+               {
+                  count = 500;
+               }
+
+               Json.RenderMany(httpResponse.Headers, httpResponseBody.Writer, await RawDb.LoadCachedQueries(count), _cachedWorldSerializer);
+               return;
+            }
          }
          }
       }
       }
 
 

+ 257 - 0
frameworks/CSharp/appmpower/src/Memory/CacheEntry.cs

@@ -0,0 +1,257 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Collections.Generic;
+using System.Runtime.CompilerServices;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.Extensions.Primitives;
+using Microsoft.Extensions.Caching.Memory;
+
+namespace appMpower.Memory
+{
+   internal sealed partial class CacheEntry : ICacheEntry
+   {
+      private static readonly Action<object> ExpirationCallback = ExpirationTokensExpired;
+
+      private readonly MemoryCache _cache;
+
+      private CacheEntryTokens _tokens; // might be null if user is not using the tokens or callbacks
+      private TimeSpan? _absoluteExpirationRelativeToNow;
+      private TimeSpan? _slidingExpiration;
+      private long? _size;
+      private CacheEntry _previous; // this field is not null only before the entry is added to the cache and tracking is enabled
+      private object _value;
+      private CacheEntryState _state;
+
+      internal CacheEntry(object key, MemoryCache memoryCache)
+      {
+         Key = key ?? throw new ArgumentNullException(nameof(key));
+         _cache = memoryCache ?? throw new ArgumentNullException(nameof(memoryCache));
+         _previous = memoryCache.TrackLinkedCacheEntries ? CacheEntryHelper.EnterScope(this) : null;
+         _state = new CacheEntryState(CacheItemPriority.Normal);
+      }
+
+      /// <summary>
+      /// Gets or sets an absolute expiration date for the cache entry.
+      /// </summary>
+      public DateTimeOffset? AbsoluteExpiration { get; set; }
+
+      /// <summary>
+      /// Gets or sets an absolute expiration time, relative to now.
+      /// </summary>
+      public TimeSpan? AbsoluteExpirationRelativeToNow
+      {
+         get => _absoluteExpirationRelativeToNow;
+         set
+         {
+            // this method does not set AbsoluteExpiration as it would require calling Clock.UtcNow twice:
+            // once here and once in MemoryCache.SetEntry
+
+            if (value <= TimeSpan.Zero)
+            {
+               throw new ArgumentOutOfRangeException(
+                   nameof(AbsoluteExpirationRelativeToNow),
+                   value,
+                   "The relative expiration value must be positive.");
+            }
+
+            _absoluteExpirationRelativeToNow = value;
+         }
+      }
+
+      /// <summary>
+      /// Gets or sets how long a cache entry can be inactive (e.g. not accessed) before it will be removed.
+      /// This will not extend the entry lifetime beyond the absolute expiration (if set).
+      /// </summary>
+      public TimeSpan? SlidingExpiration
+      {
+         get => _slidingExpiration;
+         set
+         {
+            if (value <= TimeSpan.Zero)
+            {
+               throw new ArgumentOutOfRangeException(
+                   nameof(SlidingExpiration),
+                   value,
+                   "The sliding expiration value must be positive.");
+            }
+
+            _slidingExpiration = value;
+         }
+      }
+
+      /// <summary>
+      /// Gets the <see cref="IChangeToken"/> instances which cause the cache entry to expire.
+      /// </summary>
+      public IList<IChangeToken> ExpirationTokens => GetOrCreateTokens().ExpirationTokens;
+
+      /// <summary>
+      /// Gets or sets the callbacks will be fired after the cache entry is evicted from the cache.
+      /// </summary>
+      public IList<PostEvictionCallbackRegistration> PostEvictionCallbacks => GetOrCreateTokens().PostEvictionCallbacks;
+
+      /// <summary>
+      /// Gets or sets the priority for keeping the cache entry in the cache during a
+      /// memory pressure triggered cleanup. The default is <see cref="CacheItemPriority.Normal"/>.
+      /// </summary>
+      public CacheItemPriority Priority { get => _state.Priority; set => _state.Priority = value; }
+
+      /// <summary>
+      /// Gets or sets the size of the cache entry value.
+      /// </summary>
+      public long? Size
+      {
+         get => _size;
+         set
+         {
+            if (value < 0)
+            {
+               throw new ArgumentOutOfRangeException(nameof(value), value, $"{nameof(value)} must be non-negative.");
+            }
+
+            _size = value;
+         }
+      }
+
+      public object Key { get; private set; }
+
+      public object Value
+      {
+         get => _value;
+         set
+         {
+            _value = value;
+            _state.IsValueSet = true;
+         }
+      }
+
+      internal DateTimeOffset LastAccessed { get; set; }
+
+      internal EvictionReason EvictionReason { get => _state.EvictionReason; private set => _state.EvictionReason = value; }
+
+      public void Dispose()
+      {
+         if (!_state.IsDisposed)
+         {
+            _state.IsDisposed = true;
+
+            if (_cache.TrackLinkedCacheEntries)
+            {
+               CacheEntryHelper.ExitScope(this, _previous);
+            }
+
+            // Don't commit or propagate options if the CacheEntry Value was never set.
+            // We assume an exception occurred causing the caller to not set the Value successfully,
+            // so don't use this entry.
+            if (_state.IsValueSet)
+            {
+               _cache.SetEntry(this);
+
+               if (_previous != null && CanPropagateOptions())
+               {
+                  PropagateOptions(_previous);
+               }
+            }
+
+            _previous = null; // we don't want to root unnecessary objects
+         }
+      }
+
+      [MethodImpl(MethodImplOptions.AggressiveInlining)] // added based on profiling
+      internal bool CheckExpired(in DateTimeOffset now)
+          => _state.IsExpired
+              || CheckForExpiredTime(now)
+              || (_tokens != null && _tokens.CheckForExpiredTokens(this));
+
+      internal void SetExpired(EvictionReason reason)
+      {
+         if (EvictionReason == EvictionReason.None)
+         {
+            EvictionReason = reason;
+         }
+         _state.IsExpired = true;
+         _tokens?.DetachTokens();
+      }
+
+      [MethodImpl(MethodImplOptions.AggressiveInlining)] // added based on profiling
+      private bool CheckForExpiredTime(in DateTimeOffset now)
+      {
+         if (!AbsoluteExpiration.HasValue && !_slidingExpiration.HasValue)
+         {
+            return false;
+         }
+
+         return FullCheck(now);
+
+         bool FullCheck(in DateTimeOffset offset)
+         {
+            if (AbsoluteExpiration.HasValue && AbsoluteExpiration.Value <= offset)
+            {
+               SetExpired(EvictionReason.Expired);
+               return true;
+            }
+
+            if (_slidingExpiration.HasValue
+                && (offset - LastAccessed) >= _slidingExpiration)
+            {
+               SetExpired(EvictionReason.Expired);
+               return true;
+            }
+
+            return false;
+         }
+      }
+
+      internal void AttachTokens() => _tokens?.AttachTokens(this);
+
+      private static void ExpirationTokensExpired(object obj)
+      {
+         // start a new thread to avoid issues with callbacks called from RegisterChangeCallback
+         Task.Factory.StartNew(state =>
+         {
+            var entry = (CacheEntry)state;
+            entry.SetExpired(EvictionReason.TokenExpired);
+            entry._cache.EntryExpired(entry);
+         }, obj, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
+      }
+
+      internal void InvokeEvictionCallbacks() => _tokens?.InvokeEvictionCallbacks(this);
+
+      // this simple check very often allows us to avoid expensive call to PropagateOptions(CacheEntryHelper.Current)
+      [MethodImpl(MethodImplOptions.AggressiveInlining)] // added based on profiling
+      internal bool CanPropagateOptions() => (_tokens != null && _tokens.CanPropagateTokens()) || AbsoluteExpiration.HasValue;
+
+      internal void PropagateOptions(CacheEntry parent)
+      {
+         if (parent == null)
+         {
+            return;
+         }
+
+         // Copy expiration tokens and AbsoluteExpiration to the cache entries hierarchy.
+         // We do this regardless of it gets cached because the tokens are associated with the value we'll return.
+         _tokens?.PropagateTokens(parent);
+
+         if (AbsoluteExpiration.HasValue)
+         {
+            if (!parent.AbsoluteExpiration.HasValue || AbsoluteExpiration < parent.AbsoluteExpiration)
+            {
+               parent.AbsoluteExpiration = AbsoluteExpiration;
+            }
+         }
+      }
+
+      private CacheEntryTokens GetOrCreateTokens()
+      {
+         if (_tokens != null)
+         {
+            return _tokens;
+         }
+
+         CacheEntryTokens result = new CacheEntryTokens();
+         return Interlocked.CompareExchange(ref _tokens, result, null) ?? result;
+      }
+   }
+}

+ 32 - 0
frameworks/CSharp/appmpower/src/Memory/CacheEntryHelper.cs

@@ -0,0 +1,32 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Diagnostics;
+using System.Threading;
+
+namespace appMpower.Memory
+{
+   internal static class CacheEntryHelper
+   {
+      private static readonly AsyncLocal<CacheEntry> _current = new AsyncLocal<CacheEntry>();
+
+      internal static CacheEntry Current
+      {
+         get => _current.Value;
+         private set => _current.Value = value;
+      }
+
+      internal static CacheEntry EnterScope(CacheEntry current)
+      {
+         CacheEntry previous = Current;
+         Current = current;
+         return previous;
+      }
+
+      internal static void ExitScope(CacheEntry current, CacheEntry previous)
+      {
+         Debug.Assert(Current == current, "Entries disposed in invalid order");
+         Current = previous;
+      }
+   }
+}

+ 62 - 0
frameworks/CSharp/appmpower/src/Memory/CacheEntryState.cs

@@ -0,0 +1,62 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using Microsoft.Extensions.Caching.Memory;
+
+namespace appMpower.Memory
+{
+   internal sealed partial class CacheEntry
+   {
+      // this type exists just to reduce CacheEntry size by replacing many enum & boolean fields with one of a size of Int32
+      private struct CacheEntryState
+      {
+         private byte _flags;
+         private byte _evictionReason;
+         private byte _priority;
+
+         internal CacheEntryState(CacheItemPriority priority) : this() => _priority = (byte)priority;
+
+         internal bool IsDisposed
+         {
+            get => ((Flags)_flags & Flags.IsDisposed) != 0;
+            set => SetFlag(Flags.IsDisposed, value);
+         }
+
+         internal bool IsExpired
+         {
+            get => ((Flags)_flags & Flags.IsExpired) != 0;
+            set => SetFlag(Flags.IsExpired, value);
+         }
+
+         internal bool IsValueSet
+         {
+            get => ((Flags)_flags & Flags.IsValueSet) != 0;
+            set => SetFlag(Flags.IsValueSet, value);
+         }
+
+         internal EvictionReason EvictionReason
+         {
+            get => (EvictionReason)_evictionReason;
+            set => _evictionReason = (byte)value;
+         }
+
+         internal CacheItemPriority Priority
+         {
+            get => (CacheItemPriority)_priority;
+            set => _priority = (byte)value;
+         }
+
+         private void SetFlag(Flags option, bool value) => _flags = (byte)(value ? (_flags | (byte)option) : (_flags & ~(byte)option));
+
+         [Flags]
+         private enum Flags : byte
+         {
+            Default = 0,
+            IsValueSet = 1 << 0,
+            IsExpired = 1 << 1,
+            IsDisposed = 1 << 2,
+         }
+      }
+   }
+}

+ 140 - 0
frameworks/CSharp/appmpower/src/Memory/CacheEntryTokens.cs

@@ -0,0 +1,140 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Collections.Generic;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Primitives;
+using Microsoft.Extensions.Caching.Memory;
+
+namespace appMpower.Memory
+{
+   internal sealed partial class CacheEntry
+   {
+      // this type exists just to reduce average CacheEntry size
+      // which typically is not using expiration tokens or callbacks
+      private sealed class CacheEntryTokens
+      {
+         private List<IChangeToken> _expirationTokens;
+         private List<IDisposable> _expirationTokenRegistrations;
+         private List<PostEvictionCallbackRegistration> _postEvictionCallbacks; // this is not really related to tokens, but was moved here to shrink typical CacheEntry size
+
+         internal List<IChangeToken> ExpirationTokens => _expirationTokens ??= new List<IChangeToken>();
+         internal List<PostEvictionCallbackRegistration> PostEvictionCallbacks => _postEvictionCallbacks ??= new List<PostEvictionCallbackRegistration>();
+
+         internal void AttachTokens(CacheEntry cacheEntry)
+         {
+            if (_expirationTokens != null)
+            {
+               lock (this)
+               {
+                  for (int i = 0; i < _expirationTokens.Count; i++)
+                  {
+                     IChangeToken expirationToken = _expirationTokens[i];
+                     if (expirationToken.ActiveChangeCallbacks)
+                     {
+                        _expirationTokenRegistrations ??= new List<IDisposable>(1);
+                        IDisposable registration = expirationToken.RegisterChangeCallback(ExpirationCallback, cacheEntry);
+                        _expirationTokenRegistrations.Add(registration);
+                     }
+                  }
+               }
+            }
+         }
+
+         internal bool CheckForExpiredTokens(CacheEntry cacheEntry)
+         {
+            if (_expirationTokens != null)
+            {
+               for (int i = 0; i < _expirationTokens.Count; i++)
+               {
+                  IChangeToken expiredToken = _expirationTokens[i];
+                  if (expiredToken.HasChanged)
+                  {
+                     cacheEntry.SetExpired(EvictionReason.TokenExpired);
+                     return true;
+                  }
+               }
+            }
+            return false;
+         }
+
+         internal bool CanPropagateTokens() => _expirationTokens != null;
+
+         internal void PropagateTokens(CacheEntry parentEntry)
+         {
+            if (_expirationTokens != null)
+            {
+               lock (this)
+               {
+                  lock (parentEntry.GetOrCreateTokens())
+                  {
+                     foreach (IChangeToken expirationToken in _expirationTokens)
+                     {
+                        parentEntry.AddExpirationToken(expirationToken);
+                     }
+                  }
+               }
+            }
+         }
+
+         internal void DetachTokens()
+         {
+            // _expirationTokenRegistrations is not checked for null, because AttachTokens might initialize it under lock
+            // instead we are checking for _expirationTokens, because if they are not null, then _expirationTokenRegistrations might also be not null
+            if (_expirationTokens != null)
+            {
+               lock (this)
+               {
+                  List<IDisposable> registrations = _expirationTokenRegistrations;
+                  if (registrations != null)
+                  {
+                     _expirationTokenRegistrations = null;
+                     for (int i = 0; i < registrations.Count; i++)
+                     {
+                        IDisposable registration = registrations[i];
+                        registration.Dispose();
+                     }
+                  }
+               }
+            }
+         }
+
+         internal void InvokeEvictionCallbacks(CacheEntry cacheEntry)
+         {
+            if (_postEvictionCallbacks != null)
+            {
+               Task.Factory.StartNew(state => InvokeCallbacks((CacheEntry)state), cacheEntry,
+                   CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
+            }
+         }
+
+         private static void InvokeCallbacks(CacheEntry entry)
+         {
+            List<PostEvictionCallbackRegistration> callbackRegistrations = Interlocked.Exchange(ref entry._tokens._postEvictionCallbacks, null);
+
+            if (callbackRegistrations == null)
+            {
+               return;
+            }
+
+            for (int i = 0; i < callbackRegistrations.Count; i++)
+            {
+               PostEvictionCallbackRegistration registration = callbackRegistrations[i];
+
+               try
+               {
+                  registration.EvictionCallback?.Invoke(entry.Key, entry.Value, entry.EvictionReason, registration.State);
+               }
+               catch (Exception e)
+               {
+                  // This will be invoked on a background thread, don't let it throw.
+                  entry._cache._logger.LogError(e, "EvictionCallback invoked failed");
+               }
+            }
+         }
+      }
+   }
+}

+ 520 - 0
frameworks/CSharp/appmpower/src/Memory/MemoryCache.cs

@@ -0,0 +1,520 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.Runtime.CompilerServices;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.Extensions.Internal;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Logging.Abstractions;
+using Microsoft.Extensions.Options;
+using Microsoft.Extensions.Caching.Memory;
+
+namespace appMpower.Memory
+{
+   /// <summary>
+   /// An implementation of <see cref="IMemoryCache"/> using a dictionary to
+   /// store its entries.
+   /// </summary>
+   public class MemoryCache : IMemoryCache
+   {
+      internal readonly ILogger _logger;
+
+      private readonly MemoryCacheOptions _options;
+      private readonly ConcurrentDictionary<object, CacheEntry> _entries;
+
+      private long _cacheSize;
+      private bool _disposed;
+      private DateTimeOffset _lastExpirationScan;
+
+      /// <summary>
+      /// Creates a new <see cref="MemoryCache"/> instance.
+      /// </summary>
+      /// <param name="optionsAccessor">The options of the cache.</param>
+      public MemoryCache(IOptions<MemoryCacheOptions> optionsAccessor)
+          : this(optionsAccessor, NullLoggerFactory.Instance) { }
+
+      /// <summary>
+      /// Creates a new <see cref="MemoryCache"/> instance.
+      /// </summary>
+      /// <param name="optionsAccessor">The options of the cache.</param>
+      /// <param name="loggerFactory">The factory used to create loggers.</param>
+      public MemoryCache(IOptions<MemoryCacheOptions> optionsAccessor, ILoggerFactory loggerFactory)
+      {
+         if (optionsAccessor == null)
+         {
+            throw new ArgumentNullException(nameof(optionsAccessor));
+         }
+
+         if (loggerFactory == null)
+         {
+            throw new ArgumentNullException(nameof(loggerFactory));
+         }
+
+         _options = optionsAccessor.Value;
+         //_logger = loggerFactory.CreateLogger<MemoryCache>();
+         _logger = loggerFactory.CreateLogger("MemoryCache");
+
+         _entries = new ConcurrentDictionary<object, CacheEntry>();
+
+         if (_options.Clock == null)
+         {
+            _options.Clock = new SystemClock();
+         }
+
+         _lastExpirationScan = _options.Clock.UtcNow;
+         TrackLinkedCacheEntries = _options.TrackLinkedCacheEntries; // we store the setting now so it's consistent for entire MemoryCache lifetime
+      }
+
+      /// <summary>
+      /// Cleans up the background collection events.
+      /// </summary>
+      ~MemoryCache() => Dispose(false);
+
+      /// <summary>
+      /// Gets the count of the current entries for diagnostic purposes.
+      /// </summary>
+      public int Count => _entries.Count;
+
+      // internal for testing
+      internal long Size { get => Interlocked.Read(ref _cacheSize); }
+
+      internal bool TrackLinkedCacheEntries { get; }
+
+      private ICollection<KeyValuePair<object, CacheEntry>> EntriesCollection => _entries;
+
+      /// <inheritdoc />
+      public ICacheEntry CreateEntry(object key)
+      {
+         CheckDisposed();
+         ValidateCacheKey(key);
+
+         return new CacheEntry(key, this);
+      }
+
+      internal void SetEntry(CacheEntry entry)
+      {
+         if (_disposed)
+         {
+            // No-op instead of throwing since this is called during CacheEntry.Dispose
+            return;
+         }
+
+         if (_options.SizeLimit.HasValue && !entry.Size.HasValue)
+         {
+            //throw new InvalidOperationException(SR.Format(SR.CacheEntryHasEmptySize, nameof(entry.Size), nameof(_options.SizeLimit)));
+            throw new InvalidOperationException();
+         }
+
+         DateTimeOffset utcNow = _options.Clock.UtcNow;
+
+         DateTimeOffset? absoluteExpiration = null;
+         if (entry.AbsoluteExpirationRelativeToNow.HasValue)
+         {
+            absoluteExpiration = utcNow + entry.AbsoluteExpirationRelativeToNow;
+         }
+         else if (entry.AbsoluteExpiration.HasValue)
+         {
+            absoluteExpiration = entry.AbsoluteExpiration;
+         }
+
+         // Applying the option's absolute expiration only if it's not already smaller.
+         // This can be the case if a dependent cache entry has a smaller value, and
+         // it was set by cascading it to its parent.
+         if (absoluteExpiration.HasValue)
+         {
+            if (!entry.AbsoluteExpiration.HasValue || absoluteExpiration.Value < entry.AbsoluteExpiration.Value)
+            {
+               entry.AbsoluteExpiration = absoluteExpiration;
+            }
+         }
+
+         // Initialize the last access timestamp at the time the entry is added
+         entry.LastAccessed = utcNow;
+
+         if (_entries.TryGetValue(entry.Key, out CacheEntry priorEntry))
+         {
+            priorEntry.SetExpired(EvictionReason.Replaced);
+         }
+
+         bool exceedsCapacity = UpdateCacheSizeExceedsCapacity(entry);
+
+         if (!entry.CheckExpired(utcNow) && !exceedsCapacity)
+         {
+            bool entryAdded = false;
+
+            if (priorEntry == null)
+            {
+               // Try to add the new entry if no previous entries exist.
+               entryAdded = _entries.TryAdd(entry.Key, entry);
+            }
+            else
+            {
+               // Try to update with the new entry if a previous entries exist.
+               entryAdded = _entries.TryUpdate(entry.Key, entry, priorEntry);
+
+               if (entryAdded)
+               {
+                  if (_options.SizeLimit.HasValue)
+                  {
+                     // The prior entry was removed, decrease the by the prior entry's size
+                     Interlocked.Add(ref _cacheSize, -priorEntry.Size.Value);
+                  }
+               }
+               else
+               {
+                  // The update will fail if the previous entry was removed after retrival.
+                  // Adding the new entry will succeed only if no entry has been added since.
+                  // This guarantees removing an old entry does not prevent adding a new entry.
+                  entryAdded = _entries.TryAdd(entry.Key, entry);
+               }
+            }
+
+            if (entryAdded)
+            {
+               entry.AttachTokens();
+            }
+            else
+            {
+               if (_options.SizeLimit.HasValue)
+               {
+                  // Entry could not be added, reset cache size
+                  Interlocked.Add(ref _cacheSize, -entry.Size.Value);
+               }
+               entry.SetExpired(EvictionReason.Replaced);
+               entry.InvokeEvictionCallbacks();
+            }
+
+            if (priorEntry != null)
+            {
+               priorEntry.InvokeEvictionCallbacks();
+            }
+         }
+         else
+         {
+            if (exceedsCapacity)
+            {
+               // The entry was not added due to overcapacity
+               entry.SetExpired(EvictionReason.Capacity);
+
+               TriggerOvercapacityCompaction();
+            }
+            else
+            {
+               if (_options.SizeLimit.HasValue)
+               {
+                  // Entry could not be added due to being expired, reset cache size
+                  Interlocked.Add(ref _cacheSize, -entry.Size.Value);
+               }
+            }
+
+            entry.InvokeEvictionCallbacks();
+            if (priorEntry != null)
+            {
+               RemoveEntry(priorEntry);
+            }
+         }
+
+         StartScanForExpiredItemsIfNeeded(utcNow);
+      }
+
+      /// <inheritdoc />
+      public bool TryGetValue(object key, out object result)
+      {
+         ValidateCacheKey(key);
+         CheckDisposed();
+
+         DateTimeOffset utcNow = _options.Clock.UtcNow;
+
+         if (_entries.TryGetValue(key, out CacheEntry entry))
+         {
+            // Check if expired due to expiration tokens, timers, etc. and if so, remove it.
+            // Allow a stale Replaced value to be returned due to concurrent calls to SetExpired during SetEntry.
+            if (!entry.CheckExpired(utcNow) || entry.EvictionReason == EvictionReason.Replaced)
+            {
+               entry.LastAccessed = utcNow;
+               result = entry.Value;
+
+               if (TrackLinkedCacheEntries && entry.CanPropagateOptions())
+               {
+                  // When this entry is retrieved in the scope of creating another entry,
+                  // that entry needs a copy of these expiration tokens.
+                  entry.PropagateOptions(CacheEntryHelper.Current);
+               }
+
+               StartScanForExpiredItemsIfNeeded(utcNow);
+
+               return true;
+            }
+            else
+            {
+               // TODO: For efficiency queue this up for batch removal
+               RemoveEntry(entry);
+            }
+         }
+
+         StartScanForExpiredItemsIfNeeded(utcNow);
+
+         result = null;
+         return false;
+      }
+
+      /// <inheritdoc />
+      public void Remove(object key)
+      {
+         ValidateCacheKey(key);
+
+         CheckDisposed();
+         if (_entries.TryRemove(key, out CacheEntry entry))
+         {
+            if (_options.SizeLimit.HasValue)
+            {
+               Interlocked.Add(ref _cacheSize, -entry.Size.Value);
+            }
+
+            entry.SetExpired(EvictionReason.Removed);
+            entry.InvokeEvictionCallbacks();
+         }
+
+         StartScanForExpiredItemsIfNeeded(_options.Clock.UtcNow);
+      }
+
+      private void RemoveEntry(CacheEntry entry)
+      {
+         if (EntriesCollection.Remove(new KeyValuePair<object, CacheEntry>(entry.Key, entry)))
+         {
+            if (_options.SizeLimit.HasValue)
+            {
+               Interlocked.Add(ref _cacheSize, -entry.Size.Value);
+            }
+            entry.InvokeEvictionCallbacks();
+         }
+      }
+
+      internal void EntryExpired(CacheEntry entry)
+      {
+         // TODO: For efficiency consider processing these expirations in batches.
+         RemoveEntry(entry);
+         StartScanForExpiredItemsIfNeeded(_options.Clock.UtcNow);
+      }
+
+      // Called by multiple actions to see how long it's been since we last checked for expired items.
+      // If sufficient time has elapsed then a scan is initiated on a background task.
+      [MethodImpl(MethodImplOptions.AggressiveInlining)]
+      private void StartScanForExpiredItemsIfNeeded(DateTimeOffset utcNow)
+      {
+         if (_options.ExpirationScanFrequency < utcNow - _lastExpirationScan)
+         {
+            ScheduleTask(utcNow);
+         }
+
+         void ScheduleTask(DateTimeOffset utcNow)
+         {
+            _lastExpirationScan = utcNow;
+            Task.Factory.StartNew(state => ScanForExpiredItems((MemoryCache)state), this,
+                CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
+         }
+      }
+
+      private static void ScanForExpiredItems(MemoryCache cache)
+      {
+         DateTimeOffset now = cache._lastExpirationScan = cache._options.Clock.UtcNow;
+
+         foreach (KeyValuePair<object, CacheEntry> item in cache._entries)
+         {
+            CacheEntry entry = item.Value;
+
+            if (entry.CheckExpired(now))
+            {
+               cache.RemoveEntry(entry);
+            }
+         }
+      }
+
+      private bool UpdateCacheSizeExceedsCapacity(CacheEntry entry)
+      {
+         if (!_options.SizeLimit.HasValue)
+         {
+            return false;
+         }
+
+         long newSize = 0L;
+         for (int i = 0; i < 100; i++)
+         {
+            long sizeRead = Interlocked.Read(ref _cacheSize);
+            newSize = sizeRead + entry.Size.Value;
+
+            if (newSize < 0 || newSize > _options.SizeLimit)
+            {
+               // Overflow occurred, return true without updating the cache size
+               return true;
+            }
+
+            if (sizeRead == Interlocked.CompareExchange(ref _cacheSize, newSize, sizeRead))
+            {
+               return false;
+            }
+         }
+
+         return true;
+      }
+
+      private void TriggerOvercapacityCompaction()
+      {
+         _logger.LogDebug("Overcapacity compaction triggered");
+
+         // Spawn background thread for compaction
+         ThreadPool.QueueUserWorkItem(s => OvercapacityCompaction((MemoryCache)s), this);
+      }
+
+      private static void OvercapacityCompaction(MemoryCache cache)
+      {
+         long currentSize = Interlocked.Read(ref cache._cacheSize);
+
+         cache._logger.LogDebug($"Overcapacity compaction executing. Current size {currentSize}");
+
+         double? lowWatermark = cache._options.SizeLimit * (1 - cache._options.CompactionPercentage);
+         if (currentSize > lowWatermark)
+         {
+            cache.Compact(currentSize - (long)lowWatermark, entry => entry.Size.Value);
+         }
+
+         cache._logger.LogDebug($"Overcapacity compaction executed. New size {Interlocked.Read(ref cache._cacheSize)}");
+      }
+
+      /// Remove at least the given percentage (0.10 for 10%) of the total entries (or estimated memory?), according to the following policy:
+      /// 1. Remove all expired items.
+      /// 2. Bucket by CacheItemPriority.
+      /// 3. Least recently used objects.
+      /// ?. Items with the soonest absolute expiration.
+      /// ?. Items with the soonest sliding expiration.
+      /// ?. Larger objects - estimated by object graph size, inaccurate.
+      public void Compact(double percentage)
+      {
+         int removalCountTarget = (int)(_entries.Count * percentage);
+         Compact(removalCountTarget, _ => 1);
+      }
+
+      private void Compact(long removalSizeTarget, Func<CacheEntry, long> computeEntrySize)
+      {
+         var entriesToRemove = new List<CacheEntry>();
+         var lowPriEntries = new List<CacheEntry>();
+         var normalPriEntries = new List<CacheEntry>();
+         var highPriEntries = new List<CacheEntry>();
+         long removedSize = 0;
+
+         // Sort items by expired & priority status
+         DateTimeOffset now = _options.Clock.UtcNow;
+         foreach (KeyValuePair<object, CacheEntry> item in _entries)
+         {
+            CacheEntry entry = item.Value;
+            if (entry.CheckExpired(now))
+            {
+               entriesToRemove.Add(entry);
+               removedSize += computeEntrySize(entry);
+            }
+            else
+            {
+               switch (entry.Priority)
+               {
+                  case CacheItemPriority.Low:
+                     lowPriEntries.Add(entry);
+                     break;
+                  case CacheItemPriority.Normal:
+                     normalPriEntries.Add(entry);
+                     break;
+                  case CacheItemPriority.High:
+                     highPriEntries.Add(entry);
+                     break;
+                  case CacheItemPriority.NeverRemove:
+                     break;
+                  default:
+                     throw new NotSupportedException("Not implemented: " + entry.Priority);
+               }
+            }
+         }
+
+         ExpirePriorityBucket(ref removedSize, removalSizeTarget, computeEntrySize, entriesToRemove, lowPriEntries);
+         ExpirePriorityBucket(ref removedSize, removalSizeTarget, computeEntrySize, entriesToRemove, normalPriEntries);
+         ExpirePriorityBucket(ref removedSize, removalSizeTarget, computeEntrySize, entriesToRemove, highPriEntries);
+
+         foreach (CacheEntry entry in entriesToRemove)
+         {
+            RemoveEntry(entry);
+         }
+
+         // Policy:
+         // 1. Least recently used objects.
+         // ?. Items with the soonest absolute expiration.
+         // ?. Items with the soonest sliding expiration.
+         // ?. Larger objects - estimated by object graph size, inaccurate.
+         static void ExpirePriorityBucket(ref long removedSize, long removalSizeTarget, Func<CacheEntry, long> computeEntrySize, List<CacheEntry> entriesToRemove, List<CacheEntry> priorityEntries)
+         {
+            // Do we meet our quota by just removing expired entries?
+            if (removalSizeTarget <= removedSize)
+            {
+               // No-op, we've met quota
+               return;
+            }
+
+            // Expire enough entries to reach our goal
+            // TODO: Refine policy
+
+            // LRU
+            priorityEntries.Sort((e1, e2) => e1.LastAccessed.CompareTo(e2.LastAccessed));
+            foreach (CacheEntry entry in priorityEntries)
+            {
+               entry.SetExpired(EvictionReason.Capacity);
+               entriesToRemove.Add(entry);
+               removedSize += computeEntrySize(entry);
+
+               if (removalSizeTarget <= removedSize)
+               {
+                  break;
+               }
+            }
+         }
+      }
+
+      public void Dispose()
+      {
+         Dispose(true);
+      }
+
+      protected virtual void Dispose(bool disposing)
+      {
+         if (!_disposed)
+         {
+            if (disposing)
+            {
+               GC.SuppressFinalize(this);
+            }
+
+            _disposed = true;
+         }
+      }
+
+      private void CheckDisposed()
+      {
+         if (_disposed)
+         {
+            Throw();
+         }
+
+         static void Throw() => throw new ObjectDisposedException(typeof(MemoryCache).FullName);
+      }
+
+      private static void ValidateCacheKey(object key)
+      {
+         if (key == null)
+         {
+            Throw();
+         }
+
+         static void Throw() => throw new ArgumentNullException(nameof(key));
+      }
+   }
+}

+ 67 - 0
frameworks/CSharp/appmpower/src/Memory/MemoryCacheOptions.cs

@@ -0,0 +1,67 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using Microsoft.Extensions.Internal;
+using Microsoft.Extensions.Options;
+
+namespace appMpower.Memory
+{
+   public class MemoryCacheOptions : IOptions<MemoryCacheOptions>
+   {
+      private long? _sizeLimit;
+      private double _compactionPercentage = 0.05;
+
+      public ISystemClock Clock { get; set; }
+
+      /// <summary>
+      /// Gets or sets the minimum length of time between successive scans for expired items.
+      /// </summary>
+      public TimeSpan ExpirationScanFrequency { get; set; } = TimeSpan.FromMinutes(1);
+
+      /// <summary>
+      /// Gets or sets the maximum size of the cache.
+      /// </summary>
+      public long? SizeLimit
+      {
+         get => _sizeLimit;
+         set
+         {
+            if (value < 0)
+            {
+               throw new ArgumentOutOfRangeException(nameof(value), value, $"{nameof(value)} must be non-negative.");
+            }
+
+            _sizeLimit = value;
+         }
+      }
+
+      /// <summary>
+      /// Gets or sets the amount to compact the cache by when the maximum size is exceeded.
+      /// </summary>
+      public double CompactionPercentage
+      {
+         get => _compactionPercentage;
+         set
+         {
+            if (value < 0 || value > 1)
+            {
+               throw new ArgumentOutOfRangeException(nameof(value), value, $"{nameof(value)} must be between 0 and 1 inclusive.");
+            }
+
+            _compactionPercentage = value;
+         }
+      }
+
+      /// <summary>
+      /// Gets or sets whether to track linked entries. Disabled by default.
+      /// </summary>
+      /// <remarks>Prior to .NET 7 this feature was always enabled.</remarks>
+      public bool TrackLinkedCacheEntries { get; set; }
+
+      MemoryCacheOptions IOptions<MemoryCacheOptions>.Value
+      {
+         get { return this; }
+      }
+   }
+}

+ 24 - 0
frameworks/CSharp/appmpower/src/Microsoft/CachKey.cs

@@ -0,0 +1,24 @@
+using System;
+
+namespace PlatformBenchmarks
+{
+   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();
+   }
+}

+ 101 - 5
frameworks/CSharp/appmpower/src/RawDb.cs

@@ -2,8 +2,11 @@ using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.Data;
 using System.Data;
 using System.Data.Common;
 using System.Data.Common;
+using System.Linq;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
+using Microsoft.Extensions.Caching.Memory;
 using appMpower.Db;
 using appMpower.Db;
+using PlatformBenchmarks;
 
 
 namespace appMpower
 namespace appMpower
 {
 {
@@ -13,9 +16,17 @@ namespace appMpower
       private static Random _random = new Random();
       private static Random _random = new Random();
       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 appMpower.Memory.MemoryCache _cache = new appMpower.Memory.MemoryCache(
+            new appMpower.Memory.MemoryCacheOptions()
+            {
+               ExpirationScanFrequency = TimeSpan.FromMinutes(60)
+            });
+
       public static async Task<World> LoadSingleQueryRow()
       public static async Task<World> LoadSingleQueryRow()
       {
       {
-         var pooledConnection = await PooledConnections.GetConnection(ConnectionStrings.OdbcConnection);
+         var pooledConnection = await PooledConnections.GetConnection(DataProvider.ConnectionString);
          pooledConnection.Open();
          pooledConnection.Open();
 
 
          var (pooledCommand, _) = CreateReadCommand(pooledConnection);
          var (pooledCommand, _) = CreateReadCommand(pooledConnection);
@@ -31,7 +42,7 @@ namespace appMpower
       {
       {
          var worlds = new World[count];
          var worlds = new World[count];
 
 
-         var pooledConnection = await PooledConnections.GetConnection(ConnectionStrings.OdbcConnection);
+         var pooledConnection = await PooledConnections.GetConnection(DataProvider.ConnectionString);
          pooledConnection.Open();
          pooledConnection.Open();
 
 
          var (pooledCommand, dbDataParameter) = CreateReadCommand(pooledConnection);
          var (pooledCommand, dbDataParameter) = CreateReadCommand(pooledConnection);
@@ -52,7 +63,7 @@ namespace appMpower
       {
       {
          var fortunes = new List<Fortune>();
          var fortunes = new List<Fortune>();
 
 
-         var pooledConnection = await PooledConnections.GetConnection(ConnectionStrings.OdbcConnection);
+         var pooledConnection = await PooledConnections.GetConnection(DataProvider.ConnectionString);
          pooledConnection.Open();
          pooledConnection.Open();
 
 
          var pooledCommand = new PooledCommand("SELECT * FROM fortune", pooledConnection);
          var pooledCommand = new PooledCommand("SELECT * FROM fortune", pooledConnection);
@@ -87,7 +98,7 @@ namespace appMpower
       {
       {
          var worlds = new World[count];
          var worlds = new World[count];
 
 
-         var pooledConnection = await PooledConnections.GetConnection(ConnectionStrings.OdbcConnection);
+         var pooledConnection = await PooledConnections.GetConnection(DataProvider.ConnectionString);
          pooledConnection.Open();
          pooledConnection.Open();
 
 
          var (queryCommand, dbDataParameter) = CreateReadCommand(pooledConnection);
          var (queryCommand, dbDataParameter) = CreateReadCommand(pooledConnection);
@@ -182,7 +193,7 @@ namespace appMpower
             queryString = _queriesMultipleRows[count] = PlatformBenchmarks.StringBuilderCache.GetStringAndRelease(stringBuilder);
             queryString = _queriesMultipleRows[count] = PlatformBenchmarks.StringBuilderCache.GetStringAndRelease(stringBuilder);
          }
          }
 
 
-         var pooledConnection = await PooledConnections.GetConnection(ConnectionStrings.OdbcConnection);
+         var pooledConnection = await PooledConnections.GetConnection(DataProvider.ConnectionString);
          pooledConnection.Open();
          pooledConnection.Open();
 
 
          var pooledCommand = new PooledCommand(queryString, pooledConnection);
          var pooledCommand = new PooledCommand(queryString, pooledConnection);
@@ -231,5 +242,90 @@ namespace appMpower
 
 
          return System.Text.Encoding.Default.GetString(values);
          return System.Text.Encoding.Default.GetString(values);
       }
       }
+
+      public static async Task PopulateCache()
+      {
+         var pooledConnection = await PooledConnections.GetConnection(DataProvider.ConnectionString);
+         pooledConnection.Open();
+
+         var (pooledCommand, dbDataParameter) = CreateReadCommand(pooledConnection);
+
+         using (pooledCommand)
+         {
+            var cacheKeys = _cacheKeys;
+            var cache = _cache;
+
+            for (var i = 1; i < 10001; i++)
+            {
+               dbDataParameter.Value = i;
+               cache.Set<CachedWorld>(cacheKeys[i], await ReadSingleRow(pooledCommand));
+            }
+         }
+
+         pooledCommand.Release();
+         pooledConnection.Release();
+      }
+
+      public static 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 LoadUncachedQueries(id, i, count, result);
+            }
+         }
+
+         return Task.FromResult(result);
+      }
+
+      //static async Task<CachedWorld[]> LoadUncachedQueries(int id, int i, int count, RawDb rawdb, CachedWorld[] result)
+      static async Task<CachedWorld[]> LoadUncachedQueries(int id, int i, int count, CachedWorld[] result)
+      {
+         var pooledConnection = await PooledConnections.GetConnection(DataProvider.ConnectionString);
+         pooledConnection.Open();
+
+         var (pooledCommand, dbDataParameter) = CreateReadCommand(pooledConnection);
+
+         using (pooledCommand)
+         {
+            Func<ICacheEntry, Task<CachedWorld>> create = async (entry) =>
+            {
+               return await ReadSingleRow(pooledCommand);
+            };
+
+            var cacheKeys = _cacheKeys;
+            var key = cacheKeys[id];
+
+            dbDataParameter.Value = id;
+
+            for (; i < result.Length; i++)
+            {
+               result[i] = await _cache.GetOrCreateAsync<CachedWorld>(key, create);
+
+               id = _random.Next(1, 10001);
+               dbDataParameter.Value = id;
+               key = cacheKeys[id];
+            }
+
+            pooledCommand.Release();
+            pooledConnection.Release();
+         }
+
+         return result;
+      }
    }
    }
 }
 }

+ 15 - 7
frameworks/CSharp/appmpower/src/appMpower.csproj

@@ -1,27 +1,35 @@
 <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>
 
 
-    <TrimMode>link</TrimMode>
-
     <RuntimeIdentifier>linux-x64</RuntimeIdentifier>
     <RuntimeIdentifier>linux-x64</RuntimeIdentifier>
     <SuppressTrimAnalysisWarnings>true</SuppressTrimAnalysisWarnings>
     <SuppressTrimAnalysisWarnings>true</SuppressTrimAnalysisWarnings>
     <InvariantGlobalization>true</InvariantGlobalization>
     <InvariantGlobalization>true</InvariantGlobalization>
-    <IlcOptimizationPreference>Speed</IlcOptimizationPreference>
     <IlcDisableReflection>true</IlcDisableReflection>
     <IlcDisableReflection>true</IlcDisableReflection>
-    <IlcGenerateStackTraceData>false</IlcGenerateStackTraceData>
 
 
+    <!--
+    <TrimMode>link</TrimMode>
+    -->
+
+    <!-- Opt out of the "easy mode" of the CoreRT compiler (http://aka.ms/OptimizeCoreRT) -->
+    <TrimmerDefaultAction>link</TrimmerDefaultAction>
+    <IlcOptimizationPreference>Speed</IlcOptimizationPreference>
+    <IlcPgoOptimize>true</IlcPgoOptimize>
+    <IlcTrimMetadata>true</IlcTrimMetadata>
+
+    <!-- This benchmark is marked Stripped, so we might as well do this: -->
     <UseSystemResourceKeys>true</UseSystemResourceKeys>
     <UseSystemResourceKeys>true</UseSystemResourceKeys>
     <EventSourceSupport>false</EventSourceSupport>
     <EventSourceSupport>false</EventSourceSupport>
     <DebuggerSupport>false</DebuggerSupport>
     <DebuggerSupport>false</DebuggerSupport>
+    <IlcGenerateStackTraceData>false</IlcGenerateStackTraceData>
   </PropertyGroup>
   </PropertyGroup>
 
 
   <ItemGroup>
   <ItemGroup>
-    <PackageReference Include="System.Data.Odbc" Version="5.0.0" />
-    <PackageReference Include="Microsoft.DotNet.ILCompiler" Version="6.0.0-*" />
+    <PackageReference Include="System.Data.Odbc" Version="6.0.0" />
+    <PackageReference Include="Microsoft.DotNet.ILCompiler" Version="7.0.0-*" />
   </ItemGroup>
   </ItemGroup>
 
 
   <PropertyGroup>
   <PropertyGroup>

+ 4 - 4
frameworks/CSharp/genhttp/Benchmarks/Benchmarks.csproj

@@ -26,11 +26,11 @@
   </ItemGroup>
   </ItemGroup>
     
     
   <ItemGroup>
   <ItemGroup>
-    <PackageReference Include="GenHTTP.Core" Version="6.0.0" />
-    <PackageReference Include="GenHTTP.Modules.Razor" Version="6.0.0" />
-    <PackageReference Include="GenHTTP.Modules.Webservices" Version="6.0.0" />
+    <PackageReference Include="GenHTTP.Core" Version="6.2.0" />
+    <PackageReference Include="GenHTTP.Modules.Razor" Version="6.2.0" />
+    <PackageReference Include="GenHTTP.Modules.Webservices" Version="6.2.0" />
     <PackageReference Include="Microsoft.EntityFrameworkCore" Version="6.0.0" />
     <PackageReference Include="Microsoft.EntityFrameworkCore" Version="6.0.0" />
-    <PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="6.0.0" />
+    <PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="6.0.1" />
   </ItemGroup>
   </ItemGroup>
   
   
 </Project>
 </Project>

+ 1 - 1
frameworks/CSharp/genhttp/Benchmarks/Model/DatabaseContext.cs

@@ -25,7 +25,7 @@ namespace Benchmarks.Model
         {
         {
             var optionsBuilder = new DbContextOptionsBuilder<DatabaseContext>();
             var optionsBuilder = new DbContextOptionsBuilder<DatabaseContext>();
 
 
-            optionsBuilder.UseNpgsql("Server=tfb-database;Database=hello_world;User Id=benchmarkdbuser;Password=benchmarkdbpass;Maximum Pool Size=64;NoResetOnClose=true;Enlist=false;Max Auto Prepare=3");
+            optionsBuilder.UseNpgsql("Server=tfb-database;Database=hello_world;User Id=benchmarkdbuser;Password=benchmarkdbpass;Maximum Pool Size=18;NoResetOnClose=true;Enlist=false;Max Auto Prepare=4");
 
 
             if (!tracking)
             if (!tracking)
             {
             {

+ 4 - 1
frameworks/CSharp/genhttp/Benchmarks/Tests/FortuneHandler.cs

@@ -13,6 +13,7 @@ using GenHTTP.Modules.IO;
 using GenHTTP.Modules.Razor;
 using GenHTTP.Modules.Razor;
 
 
 using Benchmarks.Model;
 using Benchmarks.Model;
+using System.IO;
 
 
 namespace Benchmarks.Tests
 namespace Benchmarks.Tests
 {
 {
@@ -90,7 +91,9 @@ namespace Benchmarks.Tests
 
 
         public IEnumerable<ContentElement> GetContent(IRequest request) => Enumerable.Empty<ContentElement>();
         public IEnumerable<ContentElement> GetContent(IRequest request) => Enumerable.Empty<ContentElement>();
 
 
-        public async ValueTask<string> RenderAsync(TemplateModel model) => await Template.RenderAsync(model);
+        public ValueTask<string> RenderAsync(TemplateModel model) => Template.RenderAsync(model);
+
+        public ValueTask RenderAsync(TemplateModel model, Stream target) => Template.RenderAsync(model, target);
 
 
         public async ValueTask<IResponse> HandleAsync(IRequest request)
         public async ValueTask<IResponse> HandleAsync(IRequest request)
         {
         {

+ 1 - 1
frameworks/CSharp/genhttp/Benchmarks/Tests/QueryResource.cs

@@ -13,7 +13,7 @@ namespace Benchmarks.Tests
 
 
     public sealed class QueryResource
     public sealed class QueryResource
     {
     {
-        private static Random _Random = new Random();
+        private static readonly Random _Random = new Random();
 
 
         [ResourceMethod(":queries")]
         [ResourceMethod(":queries")]
         public ValueTask<List<World>> GetWorldsFromPath(string queries) => GetWorlds(queries);
         public ValueTask<List<World>> GetWorldsFromPath(string queries) => GetWorlds(queries);

+ 12 - 13
frameworks/CSharp/genhttp/genhttp.dockerfile

@@ -1,19 +1,18 @@
-FROM mcr.microsoft.com/dotnet/sdk:6.0-alpine AS build
-WORKDIR /source
+FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build
+WORKDIR /app
+COPY Benchmarks .
+RUN dotnet publish -c Release -o out
 
 
-# copy csproj and restore as distinct layers
-COPY Benchmarks/*.csproj .
-RUN dotnet restore -r linux-musl-x64
+FROM mcr.microsoft.com/dotnet/runtime:6.0 AS runtime
 
 
-# copy and publish app and libraries
-COPY Benchmarks/ .
-RUN dotnet publish -c release -o /app -r linux-musl-x64 --no-restore --self-contained
+# Full PGO
+ENV DOTNET_TieredPGO 1 
+ENV DOTNET_TC_QuickJitForLoops 1 
+ENV DOTNET_ReadyToRun 0
 
 
-# final stage/image
-FROM mcr.microsoft.com/dotnet/runtime-deps:6.0-alpine
 WORKDIR /app
 WORKDIR /app
-COPY --from=build /app .
+COPY --from=build /app/out ./
 
 
-ENTRYPOINT ["./Benchmarks"]
+EXPOSE 8080
 
 
-EXPOSE 8080
+ENTRYPOINT ["dotnet", "Benchmarks.dll"]

+ 6 - 0
frameworks/FSharp/giraffe/giraffe-newtonsoft.dockerfile

@@ -5,6 +5,12 @@ RUN dotnet publish -c Release -o out
 
 
 FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS runtime
 FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS runtime
 ENV ASPNETCORE_URLS http://+:8080
 ENV ASPNETCORE_URLS http://+:8080
+
+# Full PGO
+ENV DOTNET_TieredPGO 1 
+ENV DOTNET_TC_QuickJitForLoops 1 
+ENV DOTNET_ReadyToRun 0
+
 WORKDIR /app
 WORKDIR /app
 COPY --from=build /app/out ./
 COPY --from=build /app/out ./
 
 

+ 6 - 0
frameworks/FSharp/giraffe/giraffe-utf8json.dockerfile

@@ -5,6 +5,12 @@ RUN dotnet publish -c Release -o out
 
 
 FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS runtime
 FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS runtime
 ENV ASPNETCORE_URLS http://+:8080
 ENV ASPNETCORE_URLS http://+:8080
+
+# Full PGO
+ENV DOTNET_TieredPGO 1 
+ENV DOTNET_TC_QuickJitForLoops 1 
+ENV DOTNET_ReadyToRun 0
+
 WORKDIR /app
 WORKDIR /app
 COPY --from=build /app/out ./
 COPY --from=build /app/out ./
 
 

+ 6 - 0
frameworks/FSharp/giraffe/giraffe.dockerfile

@@ -5,6 +5,12 @@ RUN dotnet publish -c Release -o out
 
 
 FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS runtime
 FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS runtime
 ENV ASPNETCORE_URLS http://+:8080
 ENV ASPNETCORE_URLS http://+:8080
+
+# Full PGO
+ENV DOTNET_TieredPGO 1 
+ENV DOTNET_TC_QuickJitForLoops 1 
+ENV DOTNET_ReadyToRun 0
+
 WORKDIR /app
 WORKDIR /app
 COPY --from=build /app/out ./
 COPY --from=build /app/out ./
 
 

+ 2 - 3
frameworks/Java/helidon/README.md

@@ -3,8 +3,7 @@
 This is the Helidon portion of a [benchmarking test suite](../) comparing a variety of web development platforms.
 This is the Helidon portion of a [benchmarking test suite](../) comparing a variety of web development platforms.
 
 
 There is currently one repository implementation.
 There is currently one repository implementation.
-* [JdbcRepository](src/main/java/io/helidon/benchmark/models/JdbcRepository.java) is using JDBC and an io thread pool of size (2 * cores count) to prevent blocking netty's main event loop. It uses hikaricp to manage the connection pool. It is configured for a maximum of (2 * cores count) concurrent connections. See [About-Pool-Sizing](https://github.com/brettwooldridge/HikariCP/wiki/About-Pool-Sizing) for more information. I am assuming that the DB is running on a computer with the same spec as the one running the app, which seems to be the case based on what is written [here](https://www.techempower.com/benchmarks/#section=environment&hw=ph&test=db&l=fjd9b3)
-
+* [JdbcRepository](src/main/java/io/helidon/benchmark/models/JdbcRepository.java) is using Vertx pg client.
 ### Plaintext Test
 ### Plaintext Test
 
 
 * [Plaintext test source](src/main/java/io/helidon/benchmark/services/PlainTextService.java)
 * [Plaintext test source](src/main/java/io/helidon/benchmark/services/PlainTextService.java)
@@ -32,7 +31,7 @@ There is currently one repository implementation.
 ## Versions
 ## Versions
 
 
 * [Java OpenJDK 11](http://openjdk.java.net/)
 * [Java OpenJDK 11](http://openjdk.java.net/)
-* [Helidon 2.2.1](http://helidon.io/)
+* [Helidon 2.4.0](http://helidon.io/)
 
 
 ## Test URLs
 ## Test URLs
 
 

+ 8 - 1
frameworks/Java/helidon/helidon.dockerfile

@@ -11,4 +11,11 @@ COPY --from=maven /helidon/target/benchmark.jar app.jar
 
 
 EXPOSE 8080
 EXPOSE 8080
 
 
-CMD ["java", "-server", "-XX:-UseBiasedLocking", "-XX:+UseNUMA", "-XX:+UseParallelGC", "-jar", "app.jar"]
+CMD java -server \
+    -XX:-UseBiasedLocking \
+    -XX:+UseNUMA \
+    -XX:+AggressiveOpts \
+    -XX:+UseParallelGC \
+    -Dio.netty.buffer.checkBounds=false \
+    -Dio.netty.buffer.checkAccessible=false \
+    -jar app.jar

+ 30 - 19
frameworks/Java/helidon/pom.xml

@@ -21,7 +21,7 @@
     <parent>
     <parent>
         <groupId>io.helidon.applications</groupId>
         <groupId>io.helidon.applications</groupId>
         <artifactId>helidon-se</artifactId>
         <artifactId>helidon-se</artifactId>
-        <version>2.2.1</version>
+        <version>2.4.0</version>
         <relativePath/>
         <relativePath/>
     </parent>
     </parent>
     <groupId>io.helidon</groupId>
     <groupId>io.helidon</groupId>
@@ -32,19 +32,21 @@
 
 
     <properties>
     <properties>
         <mainClass>io.helidon.benchmark.Main</mainClass>
         <mainClass>io.helidon.benchmark.Main</mainClass>
+        <rocker.version>1.3.0</rocker.version>
+        <vertx-pg-client.version>4.2.0</vertx-pg-client.version>
     </properties>
     </properties>
 
 
     <dependencyManagement>
     <dependencyManagement>
         <dependencies>
         <dependencies>
             <dependency>
             <dependency>
-                <groupId>io.reactivex.rxjava2</groupId>
-                <artifactId>rxjava</artifactId>
-                <version>2.2.8</version>
+                <groupId>io.vertx</groupId>
+                <artifactId>vertx-pg-client</artifactId>
+                <version>${vertx-pg-client.version}</version>
             </dependency>
             </dependency>
             <dependency>
             <dependency>
-                <groupId>com.github.spullara.mustache.java</groupId>
-                <artifactId>compiler</artifactId>
-                <version>0.9.6</version>
+                <groupId>com.fizzed</groupId>
+                <artifactId>rocker-runtime</artifactId>
+                <version>${rocker.version}</version>
             </dependency>
             </dependency>
         </dependencies>
         </dependencies>
     </dependencyManagement>
     </dependencyManagement>
@@ -63,20 +65,12 @@
             <artifactId>helidon-config-yaml</artifactId>
             <artifactId>helidon-config-yaml</artifactId>
         </dependency>
         </dependency>
         <dependency>
         <dependency>
-            <groupId>io.reactivex.rxjava2</groupId>
-            <artifactId>rxjava</artifactId>
+            <groupId>io.vertx</groupId>
+            <artifactId>vertx-pg-client</artifactId>
         </dependency>
         </dependency>
         <dependency>
         <dependency>
-            <groupId>com.zaxxer</groupId>
-            <artifactId>HikariCP</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>com.github.spullara.mustache.java</groupId>
-            <artifactId>compiler</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>org.postgresql</groupId>
-            <artifactId>postgresql</artifactId>
+            <groupId>com.fizzed</groupId>
+            <artifactId>rocker-runtime</artifactId>
         </dependency>
         </dependency>
     </dependencies>
     </dependencies>
 
 
@@ -91,6 +85,23 @@
                     </execution>
                     </execution>
                 </executions>
                 </executions>
             </plugin>
             </plugin>
+            <plugin>
+                <groupId>com.fizzed</groupId>
+                <artifactId>rocker-maven-plugin</artifactId>
+                <version>${rocker.version}</version>
+                <executions>
+                    <execution>
+                        <id>generate-rocker-templates</id>
+                        <phase>generate-sources</phase>
+                        <goals>
+                            <goal>generate</goal>
+                        </goals>
+                        <configuration>
+                            <templateDirectory>src/main/resources</templateDirectory>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
         </plugins>
         </plugins>
     </build>
     </build>
 </project>
 </project>

+ 14 - 38
frameworks/Java/helidon/src/main/java/io/helidon/benchmark/Main.java

@@ -1,5 +1,5 @@
 /*
 /*
- * Copyright (c) 2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 2021 Oracle and/or its affiliates.
  *
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * you may not use this file except in compliance with the License.
@@ -16,11 +16,9 @@
 
 
 package io.helidon.benchmark;
 package io.helidon.benchmark;
 
 
-import com.github.mustachejava.DefaultMustacheFactory;
-import com.github.mustachejava.Mustache;
-import com.github.mustachejava.MustacheFactory;
-import com.zaxxer.hikari.HikariConfig;
-import com.zaxxer.hikari.HikariDataSource;
+import java.io.IOException;
+import java.util.logging.LogManager;
+
 import io.helidon.benchmark.models.DbRepository;
 import io.helidon.benchmark.models.DbRepository;
 import io.helidon.benchmark.models.JdbcRepository;
 import io.helidon.benchmark.models.JdbcRepository;
 import io.helidon.benchmark.services.DbService;
 import io.helidon.benchmark.services.DbService;
@@ -31,47 +29,23 @@ import io.helidon.config.Config;
 import io.helidon.media.jsonp.JsonpSupport;
 import io.helidon.media.jsonp.JsonpSupport;
 import io.helidon.webserver.Routing;
 import io.helidon.webserver.Routing;
 import io.helidon.webserver.WebServer;
 import io.helidon.webserver.WebServer;
-import io.reactivex.Scheduler;
-import io.reactivex.schedulers.Schedulers;
-
-import javax.sql.DataSource;
-import java.io.IOException;
-import java.util.concurrent.Executors;
-import java.util.logging.LogManager;
 
 
 /**
 /**
  * Simple Hello World rest application.
  * Simple Hello World rest application.
  */
  */
 public final class Main {
 public final class Main {
 
 
+    private static final String SERVER_HEADER = "Server";
+    private static final String SERVER_NAME = "Helidon";
+
     /**
     /**
      * Cannot be instantiated.
      * Cannot be instantiated.
      */
      */
-    private Main() { }
-
-    private static Scheduler getScheduler() {
-        return Schedulers.from(Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() * 2));
-    }
-
-    private static DataSource getDataSource(Config config) {
-        HikariConfig hikariConfig = new HikariConfig();
-        hikariConfig.setJdbcUrl(config.get("jdbcUrl").asString().get());
-        hikariConfig.setUsername(config.get("username").asString().get());
-        hikariConfig.setPassword(config.get("password").asString().get());
-        hikariConfig.setMaximumPoolSize(Runtime.getRuntime().availableProcessors() * 2);
-
-        return new HikariDataSource(hikariConfig);
+    private Main() {
     }
     }
 
 
     private static DbRepository getRepository(Config config) {
     private static DbRepository getRepository(Config config) {
-        DataSource dataSource = getDataSource(config.get("dataSource"));
-        Scheduler scheduler = getScheduler();
-        return new JdbcRepository(dataSource, scheduler);
-    }
-
-    private static Mustache getTemplate() {
-        MustacheFactory mf = new DefaultMustacheFactory();
-        return mf.compile("fortunes.mustache");
+        return new JdbcRepository(config);
     }
     }
 
 
     /**
     /**
@@ -84,18 +58,19 @@ public final class Main {
 
 
         return Routing.builder()
         return Routing.builder()
                 .any((req, res) -> {
                 .any((req, res) -> {
-                    res.headers().add("Server", "Helidon");
+                    res.headers().add(SERVER_HEADER, SERVER_NAME);
                     req.next();
                     req.next();
                 })
                 })
                 .register(new JsonService())
                 .register(new JsonService())
                 .register(new PlainTextService())
                 .register(new PlainTextService())
                 .register(new DbService(repository))
                 .register(new DbService(repository))
-                .register(new FortuneService(repository, getTemplate()))
+                .register(new FortuneService(repository))
                 .build();
                 .build();
     }
     }
 
 
     /**
     /**
      * Application main entry point.
      * Application main entry point.
+     *
      * @param args command line arguments.
      * @param args command line arguments.
      * @throws IOException if there are problems reading logging properties
      * @throws IOException if there are problems reading logging properties
      */
      */
@@ -105,10 +80,11 @@ public final class Main {
 
 
     /**
     /**
      * Start the server.
      * Start the server.
+     *
      * @return the created {@link WebServer} instance
      * @return the created {@link WebServer} instance
      * @throws IOException if there are problems reading logging properties
      * @throws IOException if there are problems reading logging properties
      */
      */
-    protected static WebServer startServer() throws IOException {
+    private static WebServer startServer() throws IOException {
 
 
         // load logging configuration
         // load logging configuration
         LogManager.getLogManager().readConfiguration(
         LogManager.getLogManager().readConfiguration(

+ 3 - 2
frameworks/Java/helidon/src/main/java/io/helidon/benchmark/models/DbRepository.java

@@ -1,10 +1,11 @@
 package io.helidon.benchmark.models;
 package io.helidon.benchmark.models;
 
 
-import io.reactivex.Single;
-
 import java.util.List;
 import java.util.List;
 
 
+import io.helidon.common.reactive.Single;
+
 public interface DbRepository {
 public interface DbRepository {
+
     Single<World> getWorld(int id);
     Single<World> getWorld(int id);
 
 
     Single<World> updateWorld(World world);
     Single<World> updateWorld(World world);

+ 54 - 47
frameworks/Java/helidon/src/main/java/io/helidon/benchmark/models/JdbcRepository.java

@@ -1,67 +1,74 @@
 package io.helidon.benchmark.models;
 package io.helidon.benchmark.models;
 
 
-import io.reactivex.Scheduler;
-import io.reactivex.Single;
-
-import javax.sql.DataSource;
-import java.sql.Connection;
-import java.sql.PreparedStatement;
-import java.sql.ResultSet;
 import java.util.ArrayList;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.List;
 
 
+import io.helidon.common.reactive.Single;
+import io.helidon.config.Config;
+
+import io.vertx.core.Vertx;
+import io.vertx.core.VertxOptions;
+import io.vertx.pgclient.PgConnectOptions;
+import io.vertx.pgclient.PgPool;
+import io.vertx.sqlclient.PoolOptions;
+import io.vertx.sqlclient.Row;
+import io.vertx.sqlclient.SqlClient;
+import io.vertx.sqlclient.Tuple;
+
 public class JdbcRepository implements DbRepository {
 public class JdbcRepository implements DbRepository {
-    private final DataSource dataSource;
-    private final Scheduler scheduler;
+    private Vertx vertx;
+    private PgConnectOptions connectOptions;
+    private PoolOptions clientOptions;
+    private PoolOptions poolOptions;
+
+    private final ThreadLocal<SqlClient> client = ThreadLocal.withInitial(() -> PgPool.client(vertx, connectOptions, clientOptions));
+    private final ThreadLocal<SqlClient> pool = ThreadLocal.withInitial(() -> PgPool.pool(vertx, connectOptions, poolOptions));
 
 
-    public JdbcRepository(DataSource dataSource, Scheduler scheduler) {
-        this.dataSource = dataSource;
-        this.scheduler = scheduler;
+    public JdbcRepository(Config config) {
+        vertx = Vertx.vertx(new VertxOptions()
+                .setPreferNativeTransport(true));
+        connectOptions = new PgConnectOptions()
+                .setPort(config.get("port").asInt().orElse(5432))
+                .setCachePreparedStatements(config.get("cache-prepared-statements").asBoolean().orElse(true))
+                .setHost(config.get("host").asString().orElse("tfb-database"))
+                .setDatabase(config.get("db").asString().orElse("hello_world"))
+                .setUser(config.get("username").asString().orElse("benchmarkdbuser"))
+                .setPassword(config.get("password").asString().orElse("benchmarkdbpass"));
+
+        clientOptions = new PoolOptions().setMaxSize(1);
+        poolOptions = new PoolOptions().setMaxSize(4);
     }
     }
 
 
     @Override
     @Override
     public Single<World> getWorld(int id) {
     public Single<World> getWorld(int id) {
-        return Single.fromCallable(() -> {
-            try (Connection connection = dataSource.getConnection()) {
-                    PreparedStatement statement = connection.prepareStatement("SELECT id, randomnumber FROM world WHERE id = ?");
-                    statement.setInt(1, id);
-                    ResultSet rs = statement.executeQuery();
-                    rs.next();
-                    World world = new World(rs.getInt(1), rs.getInt(2));
-                    statement.close();
-                    return world;
-            }
-        }).subscribeOn(this.scheduler);
+        return Single.create(client.get().preparedQuery("SELECT id, randomnumber FROM world WHERE id = $1")
+                .execute(Tuple.of(id))
+                .map(rows -> {
+                    Row r = rows.iterator().next();
+                    return new World(r.getInteger(0), r.getInteger(1));
+                })
+                .toCompletionStage());
     }
     }
 
 
     @Override
     @Override
     public Single<World> updateWorld(World world) {
     public Single<World> updateWorld(World world) {
-        return Single.fromCallable(() -> {
-                    try (Connection connection = dataSource.getConnection()) {
-                        PreparedStatement statement = connection.prepareStatement("UPDATE world SET randomnumber = ? WHERE id = ?");
-                        statement.setInt(1, world.randomNumber);
-                        statement.setInt(2, world.id);
-                        statement.execute();
-                        statement.close();
-                        return world;
-                    }
-        }).subscribeOn(this.scheduler);
+        return Single.create(pool.get().preparedQuery("UPDATE world SET randomnumber = $1 WHERE id = $2")
+                .execute(Tuple.of(world.id, world.id))
+                .toCompletionStage()
+                .thenApply(rows -> world));
     }
     }
 
 
     @Override
     @Override
     public Single<List<Fortune>> getFortunes() {
     public Single<List<Fortune>> getFortunes() {
-        return Single.fromCallable(() -> {
-            try (Connection connection = dataSource.getConnection()) {
-                PreparedStatement statement = connection.prepareStatement("SELECT id, message FROM fortune");
-                ResultSet rs = statement.executeQuery();
-
-                List<Fortune> fortunes = new ArrayList<>();
-                while (rs.next()) {
-                    fortunes.add(new Fortune(rs.getInt(1), rs.getString(2)));
-                }
-                statement.close();
-                return fortunes;
-            }
-        }).subscribeOn(this.scheduler);
+        return Single.create(client.get().preparedQuery("SELECT id, message FROM fortune")
+                .execute()
+                .map(rows -> {
+                    List<Fortune> fortunes = new ArrayList<>(rows.size() + 1);
+                    for (Row r : rows) {
+                        fortunes.add(new Fortune(r.getInteger(0), r.getString(1)));
+                    }
+                    return fortunes;
+                })
+                .toCompletionStage());
     }
     }
-}
+}

+ 3 - 1
frameworks/Java/helidon/src/main/java/io/helidon/benchmark/models/World.java

@@ -7,6 +7,8 @@ import java.util.Collections;
 
 
 public final class World {
 public final class World {
 
 
+    private static final String ID_KEY = "id";
+    private static final String ID_RANDOM_NUMBER = "randomNumber";
     private static final JsonBuilderFactory JSON = Json.createBuilderFactory(Collections.emptyMap());
     private static final JsonBuilderFactory JSON = Json.createBuilderFactory(Collections.emptyMap());
 
 
     public int id;
     public int id;
@@ -18,6 +20,6 @@ public final class World {
     }
     }
 
 
     public JsonObject toJson() {
     public JsonObject toJson() {
-        return JSON.createObjectBuilder().add("id", id).add("randomNumber", randomNumber).build();
+        return JSON.createObjectBuilder().add(ID_KEY, id).add(ID_RANDOM_NUMBER, randomNumber).build();
     }
     }
 }
 }

+ 31 - 43
frameworks/Java/helidon/src/main/java/io/helidon/benchmark/services/DbService.java

@@ -1,39 +1,29 @@
 package io.helidon.benchmark.services;
 package io.helidon.benchmark.services;
 
 
-import java.util.Arrays;
 import java.util.Collections;
 import java.util.Collections;
 import java.util.List;
 import java.util.List;
 import java.util.Optional;
 import java.util.Optional;
 import java.util.concurrent.ThreadLocalRandom;
 import java.util.concurrent.ThreadLocalRandom;
 
 
 import javax.json.Json;
 import javax.json.Json;
-import javax.json.JsonArray;
 import javax.json.JsonArrayBuilder;
 import javax.json.JsonArrayBuilder;
 import javax.json.JsonBuilderFactory;
 import javax.json.JsonBuilderFactory;
-import javax.json.JsonObject;
-import javax.json.JsonWriterFactory;
 
 
 import io.helidon.benchmark.models.DbRepository;
 import io.helidon.benchmark.models.DbRepository;
 import io.helidon.benchmark.models.World;
 import io.helidon.benchmark.models.World;
 import io.helidon.common.http.Parameters;
 import io.helidon.common.http.Parameters;
+import io.helidon.common.reactive.Multi;
 import io.helidon.webserver.Routing;
 import io.helidon.webserver.Routing;
 import io.helidon.webserver.ServerRequest;
 import io.helidon.webserver.ServerRequest;
 import io.helidon.webserver.ServerResponse;
 import io.helidon.webserver.ServerResponse;
 import io.helidon.webserver.Service;
 import io.helidon.webserver.Service;
-import io.reactivex.Flowable;
-import io.reactivex.Single;
 
 
 public class DbService implements Service {
 public class DbService implements Service {
 
 
     private final DbRepository repository;
     private final DbRepository repository;
-    private JsonWriterFactory jsonWriterFactory;
-    private JsonBuilderFactory jsonBuilderFactory;
 
 
     public DbService(DbRepository repository) {
     public DbService(DbRepository repository) {
         this.repository = repository;
         this.repository = repository;
-
-        this.jsonWriterFactory = Json.createWriterFactory(null);
-        this.jsonBuilderFactory = Json.createBuilderFactory(Collections.emptyMap());
     }
     }
 
 
     @Override
     @Override
@@ -44,63 +34,61 @@ public class DbService implements Service {
     }
     }
 
 
     private void db(final ServerRequest request,
     private void db(final ServerRequest request,
-                          final ServerResponse response) {
+                    final ServerResponse response) {
         repository.getWorld(randomWorldNumber())
         repository.getWorld(randomWorldNumber())
-                .map(World::toJson)
-                .subscribe(jsonObject -> response.send(jsonObject));
+                .forSingle(world -> response.send(world.toJson()));
     }
     }
 
 
     private void queries(final ServerRequest request,
     private void queries(final ServerRequest request,
-                           final ServerResponse response) {
-        Flowable<JsonObject>[] worlds = new Flowable[parseQueryCount(request.queryParams())];
-        Arrays.setAll(worlds, i -> repository.getWorld(randomWorldNumber()).map(World::toJson).toFlowable());
+                         final ServerResponse response) {
+
+        Multi.range(0, parseQueryCount(request.queryParams()))
+                .flatMap(i -> repository.getWorld(randomWorldNumber()))
+                .map(World::toJson)
+                .reduce(JSON::createArrayBuilder, JsonArrayBuilder::add)
+                .map(JsonArrayBuilder::build)
+                .onError(response::send)
+                .forSingle(response::send);
 
 
-        marshall(worlds).subscribe(jsonObject -> response.send(jsonObject));
     }
     }
 
 
     private void updates(final ServerRequest request,
     private void updates(final ServerRequest request,
                          final ServerResponse response) {
                          final ServerResponse response) {
-        Flowable<JsonObject>[] worlds = new Flowable[parseQueryCount(request.queryParams())];
-
-        Arrays.setAll(worlds, i -> repository.getWorld(randomWorldNumber()).flatMapPublisher(world -> {
-            world.randomNumber = randomWorldNumber();
-            return repository.updateWorld(world).map(World::toJson).toFlowable();
-        }));
 
 
-        marshall(worlds).subscribe(jsonObject -> response.send(jsonObject));
-    }
-
-    private Single<JsonArray> marshall(Flowable<JsonObject>[] worlds) {
-        return Flowable.mergeArray(worlds)
-                .toList()
-                .map(this::buildArray)
-                .doOnError(Throwable::printStackTrace);
+        Multi.range(0, parseQueryCount(request.queryParams()))
+                .flatMap(i -> repository.getWorld(randomWorldNumber()), 1, false, 1)
+                .flatMap(world -> {
+                    world.randomNumber = randomWorldNumber();
+                    return repository.updateWorld(world);
+                }, 1, false, 1)
+                .map(World::toJson)
+                .reduce(JSON::createArrayBuilder, JsonArrayBuilder::add)
+                .map(JsonArrayBuilder::build)
+                .onError(response::send)
+                .forSingle(response::send);
     }
     }
 
 
-    private JsonArray buildArray(List<JsonObject> jsonObjects) {
-        return jsonObjects.stream().reduce(
-                jsonBuilderFactory.createArrayBuilder(),
-                JsonArrayBuilder::add,
-                JsonArrayBuilder::addAll)
-                .build();
-    }
+    private static final JsonBuilderFactory JSON = Json.createBuilderFactory(Collections.emptyMap());
 
 
     private int randomWorldNumber() {
     private int randomWorldNumber() {
         return 1 + ThreadLocalRandom.current().nextInt(10000);
         return 1 + ThreadLocalRandom.current().nextInt(10000);
     }
     }
 
 
     private int parseQueryCount(Parameters parameters) {
     private int parseQueryCount(Parameters parameters) {
-        Optional<String> textValue = parameters.first("queries");
+        List<String> values = parameters.all("queries");
 
 
-        if (textValue.isEmpty()) {
+        if (values.isEmpty()) {
             return 1;
             return 1;
         }
         }
+
+        String first = values.get(0);
+
         int parsedValue;
         int parsedValue;
         try {
         try {
-            parsedValue = Integer.parseInt(textValue.get());
+            parsedValue = Integer.parseInt(first, 10);
         } catch (NumberFormatException e) {
         } catch (NumberFormatException e) {
             return 1;
             return 1;
         }
         }
         return Math.min(500, Math.max(1, parsedValue));
         return Math.min(500, Math.max(1, parsedValue));
     }
     }
-}
+}

+ 21 - 26
frameworks/Java/helidon/src/main/java/io/helidon/benchmark/services/FortuneService.java

@@ -1,51 +1,46 @@
 package io.helidon.benchmark.services;
 package io.helidon.benchmark.services;
 
 
-import static java.util.Comparator.comparing;
-
-import java.io.ByteArrayOutputStream;
-import java.io.OutputStreamWriter;
 import java.nio.charset.StandardCharsets;
 import java.nio.charset.StandardCharsets;
-import java.util.Collections;
-
-import com.github.mustachejava.Mustache;
+import java.util.Comparator;
 
 
 import io.helidon.benchmark.models.DbRepository;
 import io.helidon.benchmark.models.DbRepository;
 import io.helidon.benchmark.models.Fortune;
 import io.helidon.benchmark.models.Fortune;
 import io.helidon.common.http.MediaType;
 import io.helidon.common.http.MediaType;
+import io.helidon.webserver.Handler;
 import io.helidon.webserver.Routing;
 import io.helidon.webserver.Routing;
 import io.helidon.webserver.ServerRequest;
 import io.helidon.webserver.ServerRequest;
 import io.helidon.webserver.ServerResponse;
 import io.helidon.webserver.ServerResponse;
 import io.helidon.webserver.Service;
 import io.helidon.webserver.Service;
 
 
-public class FortuneService implements Service {
+import com.fizzed.rocker.runtime.ArrayOfByteArraysOutput;
+import views.fortunes;
+
+public class FortuneService implements Service, Handler {
+
+    private static final Fortune ADDITIONAL_FORTUNE = new Fortune(0, "Additional fortune added at request time.");
 
 
     private final DbRepository repository;
     private final DbRepository repository;
-    private final Mustache template;
 
 
-    public FortuneService(DbRepository repository, Mustache template) {
+    public FortuneService(DbRepository repository) {
         this.repository = repository;
         this.repository = repository;
-        this.template = template;
     }
     }
 
 
     @Override
     @Override
     public void update(Routing.Rules rules) {
     public void update(Routing.Rules rules) {
-        rules.get("/fortunes", this::fortunes);
+        rules.get("/fortunes", this);
     }
     }
 
 
-    private void fortunes(ServerRequest request, ServerResponse response) {
+    @Override
+    public void accept(ServerRequest req, ServerResponse res) {
+        res.headers().contentType(MediaType.TEXT_HTML.withCharset(StandardCharsets.UTF_8.name()));
         repository.getFortunes()
         repository.getFortunes()
-                .subscribe(fortunes -> {
-                    fortunes.add(new Fortune(0, "Additional fortune added at request time."));
-                    fortunes.sort(comparing(fortune -> fortune.message));
-
-                    ByteArrayOutputStream baos = new ByteArrayOutputStream();
-                    OutputStreamWriter writer = new OutputStreamWriter(baos, StandardCharsets.UTF_8.name());
-
-                    template.execute(writer, Collections.singletonMap("fortunes", fortunes));
-                    writer.flush();
-
-                    response.headers().contentType(MediaType.TEXT_HTML.withCharset(StandardCharsets.UTF_8.name()));
-                    response.send(baos.toByteArray());
+                .forSingle(fortuneList -> {
+                    fortuneList.add(ADDITIONAL_FORTUNE);
+                    fortuneList.sort(Comparator.comparing(Fortune::getMessage));
+                    res.headers().contentType(MediaType.TEXT_HTML.withCharset(StandardCharsets.UTF_8.name()));
+                    res.send(fortunes.template(fortuneList)
+                            .render(ArrayOfByteArraysOutput.FACTORY)
+                            .toByteArray());
                 });
                 });
     }
     }
-}
+}

+ 20 - 8
frameworks/Java/helidon/src/main/java/io/helidon/benchmark/services/JsonService.java

@@ -1,24 +1,36 @@
 package io.helidon.benchmark.services;
 package io.helidon.benchmark.services;
 
 
 import java.util.Collections;
 import java.util.Collections;
+import java.util.Map;
 
 
 import javax.json.Json;
 import javax.json.Json;
 import javax.json.JsonBuilderFactory;
 import javax.json.JsonBuilderFactory;
 
 
+import io.helidon.webserver.Handler;
 import io.helidon.webserver.Routing;
 import io.helidon.webserver.Routing;
+import io.helidon.webserver.ServerRequest;
+import io.helidon.webserver.ServerResponse;
 import io.helidon.webserver.Service;
 import io.helidon.webserver.Service;
 
 
-public class JsonService implements Service {
+public class JsonService implements Service, Handler {
 
 
-    private JsonBuilderFactory jsonBuilderFactory;
+    private static final String ATTR_NAME = "message";
+    private static final String ATTR_VALUE = "Hello, World!";
+    private static final Map<String, Object> ATTR_MAP = Map.of(ATTR_NAME, ATTR_VALUE);
 
 
-     public JsonService() {
-         this.jsonBuilderFactory = Json.createBuilderFactory(Collections.emptyMap());
-     }
+    private final JsonBuilderFactory jsonBuilderFactory;
+
+    public JsonService() {
+        this.jsonBuilderFactory = Json.createBuilderFactory(Collections.emptyMap());
+    }
 
 
     @Override
     @Override
     public void update(Routing.Rules rules) {
     public void update(Routing.Rules rules) {
-        rules.get("/json",
-                (req, res) -> res.send(jsonBuilderFactory.createObjectBuilder(Collections.singletonMap("message", "Hello, World!")).build()));
+        rules.get("/json", this);
+    }
+
+    @Override
+    public void accept(ServerRequest req, ServerResponse res) {
+        res.send(jsonBuilderFactory.createObjectBuilder(ATTR_MAP).build());
     }
     }
-}
+}

+ 18 - 4
frameworks/Java/helidon/src/main/java/io/helidon/benchmark/services/PlainTextService.java

@@ -1,13 +1,27 @@
 package io.helidon.benchmark.services;
 package io.helidon.benchmark.services;
 
 
+import java.nio.charset.StandardCharsets;
+
+import io.helidon.common.http.Http;
+import io.helidon.common.http.MediaType;
+import io.helidon.webserver.Handler;
 import io.helidon.webserver.Routing;
 import io.helidon.webserver.Routing;
+import io.helidon.webserver.ServerRequest;
+import io.helidon.webserver.ServerResponse;
 import io.helidon.webserver.Service;
 import io.helidon.webserver.Service;
 
 
-public class PlainTextService implements Service {
+public class PlainTextService implements Service, Handler {
+
+    private static final byte[] MESSAGE = "Hello, World!".getBytes(StandardCharsets.UTF_8);
+    private static final String MEDIA_TYPE = MediaType.TEXT_PLAIN.toString();
 
 
     @Override
     @Override
     public void update(Routing.Rules rules) {
     public void update(Routing.Rules rules) {
-        rules.get("/plaintext",
-                (req, res) -> res.send("Hello, World!"));
+        rules.get("/plaintext", this);
+    }
+
+    @Override
+    public void accept(ServerRequest req, ServerResponse res) {
+        res.addHeader(Http.Header.CONTENT_TYPE, MEDIA_TYPE).send(MESSAGE);
     }
     }
-}
+}

+ 4 - 4
frameworks/Java/helidon/src/main/resources/application.yaml

@@ -2,7 +2,7 @@ server:
   port: 8080
   port: 8080
   host: 0.0.0.0
   host: 0.0.0.0
 
 
-dataSource:
-  jdbcUrl: "jdbc:postgresql://tfb-database:5432/hello_world"
-  username: benchmarkdbuser
-  password: benchmarkdbpass
+host: "tfb-database"
+db: "hello_world"
+username: benchmarkdbuser
+password: benchmarkdbpass

+ 0 - 20
frameworks/Java/helidon/src/main/resources/fortunes.mustache

@@ -1,20 +0,0 @@
-<!DOCTYPE html>
-<html>
-<head>
-    <title>Fortunes</title>
-</head>
-<body>
-<table>
-    <tr>
-        <th>id</th>
-        <th>message</th>
-    </tr>
-    {{#fortunes}}
-        <tr>
-            <td>{{id}}</td>
-            <td>{{message}}</td>
-        </tr>
-    {{/fortunes}}
-</table>
-</body>
-</html>

+ 24 - 0
frameworks/Java/helidon/src/main/resources/views/fortunes.rocker.html

@@ -0,0 +1,24 @@
+@import io.helidon.benchmark.models.Fortune
+@import java.util.List
+@args (List<Fortune> fortunes)
+
+<!DOCTYPE html>
+<html>
+<head>
+<title>Fortunes</title>
+</head>
+<body>
+<table>
+    <tr>
+        <th>id</th>
+        <th>message</th>
+    </tr>
+    @for (f : fortunes) {
+    <tr>
+        <td>@f.getId()</td>
+        <td>@f.getMessage()</td>
+    </tr>
+    }
+</table>
+</body>
+</html>

+ 1 - 1
frameworks/JavaScript/express/README.md

@@ -12,7 +12,7 @@ This is the Express portion of a [benchmarking test suite](../) comparing a vari
 
 
 ## Infrastructure Software Versions
 ## Infrastructure Software Versions
 The tests were run with:
 The tests were run with:
-* [Node.js v16.9.1](http://nodejs.org/)
+* [Node.js v16.13.0](http://nodejs.org/)
 * [Express 4.17.1](http://expressjs.com/)
 * [Express 4.17.1](http://expressjs.com/)
 
 
 ## Resources
 ## Resources

+ 1 - 1
frameworks/JavaScript/express/express-mongodb.dockerfile

@@ -1,4 +1,4 @@
-FROM node:16.9.1-slim
+FROM node:16.13.0-slim
 
 
 COPY ./ ./
 COPY ./ ./
 
 

+ 1 - 1
frameworks/JavaScript/express/express-mysql.dockerfile

@@ -1,4 +1,4 @@
-FROM node:16.9.1-slim
+FROM node:16.13.0-slim
 
 
 COPY ./ ./
 COPY ./ ./
 
 

+ 1 - 1
frameworks/JavaScript/express/express-postgres.dockerfile

@@ -1,4 +1,4 @@
-FROM node:16.9.1-slim
+FROM node:16.13.0-slim
 
 
 COPY ./ ./
 COPY ./ ./
 
 

+ 1 - 1
frameworks/JavaScript/express/express.dockerfile

@@ -1,4 +1,4 @@
-FROM node:16.9.1-slim
+FROM node:16.13.0-slim
 
 
 COPY ./ ./
 COPY ./ ./
 
 

+ 1 - 1
frameworks/JavaScript/hapi/hapi-mysql.dockerfile

@@ -1,4 +1,4 @@
-FROM node:16.9.1-slim
+FROM node:16.13.0-slim
 
 
 COPY ./ ./
 COPY ./ ./
 
 

+ 1 - 1
frameworks/JavaScript/hapi/hapi-nginx.dockerfile

@@ -1,4 +1,4 @@
-FROM node:16.9.1-slim
+FROM node:16.13.0-slim
 
 
 RUN apt-get update
 RUN apt-get update
 RUN apt-get install nginx -y
 RUN apt-get install nginx -y

+ 1 - 1
frameworks/JavaScript/hapi/hapi-postgres.dockerfile

@@ -1,4 +1,4 @@
-FROM node:16.9.1-slim
+FROM node:16.13.0-slim
 
 
 COPY ./ ./
 COPY ./ ./
 
 

+ 1 - 1
frameworks/JavaScript/hapi/hapi.dockerfile

@@ -1,4 +1,4 @@
-FROM node:16.9.1-slim
+FROM node:16.13.0-slim
 
 
 COPY ./ ./
 COPY ./ ./
 
 

+ 1 - 1
frameworks/JavaScript/nodejs/README.md

@@ -16,7 +16,7 @@ The server is currently in Alpha state, but aims to be a drop-in replacement for
 
 
 ## Infrastructure Software Versions
 ## Infrastructure Software Versions
 The tests were run with:
 The tests were run with:
-* [Node.js v16.9.1](http://nodejs.org/)
+* [Node.js v16.13.0](http://nodejs.org/)
 
 
 * [Node MySQL 2.16.0](https://github.com/felixge/node-mysql/)
 * [Node MySQL 2.16.0](https://github.com/felixge/node-mysql/)
 * [Sequelize 5.15.1](https://github.com/sequelize/sequelize)
 * [Sequelize 5.15.1](https://github.com/sequelize/sequelize)

+ 1 - 1
frameworks/JavaScript/nodejs/nodejs.dockerfile

@@ -1,4 +1,4 @@
-FROM node:16.9.1-slim
+FROM node:16.13.0-slim
 
 
 ARG TFB_TEST_NAME
 ARG TFB_TEST_NAME
 
 

+ 29 - 4
frameworks/OCaml/httpaf/httpaf.dockerfile

@@ -1,17 +1,42 @@
 # -*- mode: dockerfile -*-
 # -*- mode: dockerfile -*-
 
 
-FROM ocurrent/opam:alpine-3.12-ocaml-4.11
+FROM alpine:3.15
 
 
+# https://caml.inria.fr/pub/docs/manual-ocaml/libref/Gc.html
+# https://linux.die.net/man/1/ocamlrun
+# https://blog.janestreet.com/memory-allocator-showdown/
+ENV OCAMLRUNPARAM a=2,o=240
+
+RUN apk add --no-cache \
+    bash\
+    bubblewrap\
+    coreutils\
+    gcc\
+    git\
+    libev-dev\
+    libffi-dev\
+    linux-headers\
+    m4\
+    make\
+    musl-dev\
+    opam\
+    postgresql-dev
+
+RUN opam init\
+    --disable-sandboxing\
+    --auto-setup\
+    --compiler ocaml-base-compiler.4.13.1
+
+RUN opam install -y opam-depext
 RUN \
 RUN \
-  opam depext dune conf-libev httpaf httpaf-lwt-unix lwt yojson && \
-  opam install dune conf-libev httpaf httpaf-lwt-unix lwt yojson
+  opam depext -y dune conf-libev httpaf httpaf-lwt-unix lwt yojson && \
+  opam install -y dune conf-libev httpaf httpaf-lwt-unix lwt yojson
 
 
 COPY . /app
 COPY . /app
 
 
 WORKDIR /app
 WORKDIR /app
 
 
 RUN \
 RUN \
-  sudo chown -R opam: . && \
   eval $(opam env) && \
   eval $(opam env) && \
   dune build --release httpaf_unix.exe
   dune build --release httpaf_unix.exe
 
 

+ 4 - 4
frameworks/PHP/comet/comet-mysql.dockerfile

@@ -5,14 +5,14 @@ ARG DEBIAN_FRONTEND=noninteractive
 RUN apt-get update -yqq && apt-get install -yqq software-properties-common > /dev/null
 RUN apt-get update -yqq && apt-get install -yqq software-properties-common > /dev/null
 RUN LC_ALL=C.UTF-8 add-apt-repository ppa:ondrej/php
 RUN LC_ALL=C.UTF-8 add-apt-repository ppa:ondrej/php
 RUN apt-get update -yqq > /dev/null && \
 RUN apt-get update -yqq > /dev/null && \
-    apt-get install -yqq php8.0-cli php8.0-xml php8.0-mysql php8.0-mbstring > /dev/null
+    apt-get install -yqq php8.1-cli php8.1-xml php8.1-mysql php8.1-mbstring > /dev/null
 
 
 COPY --from=composer:latest /usr/bin/composer /usr/local/bin/composer
 COPY --from=composer:latest /usr/bin/composer /usr/local/bin/composer
 
 
-RUN apt-get install -y php-pear php-dev libevent-dev git > /dev/null
-RUN pecl install event-3.0.5 > /dev/null && echo "extension=event.so" > /etc/php/8.0/cli/conf.d/event.ini
+RUN apt-get install -y php-pear php8.1-dev libevent-dev git > /dev/null
+RUN pecl install event-3.0.6 > /dev/null && echo "extension=event.so" > /etc/php/8.1/cli/conf.d/event.ini
 
 
-COPY php.ini /etc/php/8.0/cli/php.ini
+COPY php.ini /etc/php/8.1/cli/php.ini
 
 
 ADD ./ /comet
 ADD ./ /comet
 WORKDIR /comet
 WORKDIR /comet

+ 4 - 4
frameworks/PHP/comet/comet.dockerfile

@@ -5,14 +5,14 @@ ARG DEBIAN_FRONTEND=noninteractive
 RUN apt-get update -yqq && apt-get install -yqq software-properties-common > /dev/null
 RUN apt-get update -yqq && apt-get install -yqq software-properties-common > /dev/null
 RUN LC_ALL=C.UTF-8 add-apt-repository ppa:ondrej/php
 RUN LC_ALL=C.UTF-8 add-apt-repository ppa:ondrej/php
 RUN apt-get update -yqq > /dev/null && \
 RUN apt-get update -yqq > /dev/null && \
-    apt-get install -yqq php8.0-cli php8.0-pgsql php8.0-xml php8.0-mbstring > /dev/null
+    apt-get install -yqq php8.1-cli php8.1-pgsql php8.1-xml php8.1-mbstring > /dev/null
 
 
 COPY --from=composer:latest /usr/bin/composer /usr/local/bin/composer
 COPY --from=composer:latest /usr/bin/composer /usr/local/bin/composer
 
 
-RUN apt-get install -y php-pear php-dev libevent-dev git > /dev/null
-RUN pecl install event-3.0.5 > /dev/null && echo "extension=event.so" > /etc/php/8.0/cli/conf.d/event.ini
+RUN apt-get install -y php-pear php8.1-dev libevent-dev git > /dev/null
+RUN pecl install event-3.0.6 > /dev/null && echo "extension=event.so" > /etc/php/8.1/cli/conf.d/event.ini
 
 
-COPY php.ini /etc/php/8.0/cli/php.ini
+COPY php.ini /etc/php/8.1/cli/php.ini
 
 
 ADD ./ /comet
 ADD ./ /comet
 WORKDIR /comet
 WORKDIR /comet

+ 4 - 4
frameworks/PHP/kumbiaphp/kumbiaphp-workerman-mysql.dockerfile

@@ -5,14 +5,14 @@ ARG DEBIAN_FRONTEND=noninteractive
 RUN apt-get update -yqq && apt-get install -yqq software-properties-common > /dev/null
 RUN apt-get update -yqq && apt-get install -yqq software-properties-common > /dev/null
 RUN LC_ALL=C.UTF-8 add-apt-repository ppa:ondrej/php
 RUN LC_ALL=C.UTF-8 add-apt-repository ppa:ondrej/php
 RUN apt-get update -yqq > /dev/null && \
 RUN apt-get update -yqq > /dev/null && \
-    apt-get install -yqq git php8.0-cli php8.0-mysql php8.0-xml > /dev/null
+    apt-get install -yqq git php8.1-cli php8.1-mysql php8.1-xml > /dev/null
 
 
 COPY --from=composer:latest /usr/bin/composer /usr/local/bin/composer
 COPY --from=composer:latest /usr/bin/composer /usr/local/bin/composer
 
 
-RUN apt-get install -y php-pear php8.0-dev libevent-dev > /dev/null
-RUN pecl install event-3.0.5 > /dev/null && echo "extension=event.so" > /etc/php/8.0/cli/conf.d/event.ini
+RUN apt-get install -y php-pear php8.1-dev libevent-dev > /dev/null
+RUN pecl install event-3.0.6 > /dev/null && echo "extension=event.so" > /etc/php/8.1/cli/conf.d/event.ini
 
 
-COPY deploy/conf/cliphp.ini /etc/php/8.0/cli/php.ini
+COPY deploy/conf/cliphp.ini /etc/php/8.1/cli/php.ini
 
 
 ADD ./ /kumbiaphp
 ADD ./ /kumbiaphp
 WORKDIR /kumbiaphp
 WORKDIR /kumbiaphp

+ 3 - 3
frameworks/PHP/php-ngx/php-ngx-async.dockerfile

@@ -7,13 +7,13 @@ RUN LC_ALL=C.UTF-8 add-apt-repository ppa:ondrej/php > /dev/null
 RUN apt-get update -yqq > /dev/null && \
 RUN apt-get update -yqq > /dev/null && \
     apt-get install -yqq wget git unzip libxml2-dev cmake make systemtap-sdt-dev \
     apt-get install -yqq wget git unzip libxml2-dev cmake make systemtap-sdt-dev \
                     zlib1g-dev libpcre3-dev libargon2-0-dev libsodium-dev \
                     zlib1g-dev libpcre3-dev libargon2-0-dev libsodium-dev \
-                    php8.0-cli php8.0-dev libphp8.0-embed php8.0-mysql nginx > /dev/null
+                    php8.1-cli php8.1-dev libphp8.1-embed php8.1-mysql nginx > /dev/null
 
 
 ADD ./ ./
 ADD ./ ./
 
 
-ENV NGINX_VERSION 1.21.3
+ENV NGINX_VERSION 1.21.4
 
 
-RUN git clone -b v0.0.25 --single-branch --depth 1 https://github.com/rryqszq4/ngx_php7.git > /dev/null
+RUN git clone -b v0.0.26 --single-branch --depth 1 https://github.com/rryqszq4/ngx_php7.git > /dev/null
 
 
 RUN wget -q http://nginx.org/download/nginx-${NGINX_VERSION}.tar.gz && \
 RUN wget -q http://nginx.org/download/nginx-${NGINX_VERSION}.tar.gz && \
     tar -zxf nginx-${NGINX_VERSION}.tar.gz && \
     tar -zxf nginx-${NGINX_VERSION}.tar.gz && \

+ 3 - 3
frameworks/PHP/php-ngx/php-ngx-mysql.dockerfile

@@ -7,13 +7,13 @@ RUN LC_ALL=C.UTF-8 add-apt-repository ppa:ondrej/php > /dev/null
 RUN apt-get update -yqq > /dev/null && \
 RUN apt-get update -yqq > /dev/null && \
     apt-get install -yqq wget git unzip libxml2-dev cmake make systemtap-sdt-dev \
     apt-get install -yqq wget git unzip libxml2-dev cmake make systemtap-sdt-dev \
                     zlib1g-dev libpcre3-dev libargon2-0-dev libsodium-dev \
                     zlib1g-dev libpcre3-dev libargon2-0-dev libsodium-dev \
-                    php8.0-cli php8.0-dev libphp8.0-embed php8.0-mysql nginx > /dev/null
+                    php8.1-cli php8.1-dev libphp8.1-embed php8.1-mysql nginx > /dev/null
 
 
 ADD ./ ./
 ADD ./ ./
 
 
-ENV NGINX_VERSION 1.21.3
+ENV NGINX_VERSION 1.21.4
 
 
-RUN git clone -b v0.0.25 --single-branch --depth 1 https://github.com/rryqszq4/ngx_php7.git > /dev/null
+RUN git clone -b v0.0.26 --single-branch --depth 1 https://github.com/rryqszq4/ngx_php7.git > /dev/null
 
 
 RUN wget -q http://nginx.org/download/nginx-${NGINX_VERSION}.tar.gz && \
 RUN wget -q http://nginx.org/download/nginx-${NGINX_VERSION}.tar.gz && \
     tar -zxf nginx-${NGINX_VERSION}.tar.gz && \
     tar -zxf nginx-${NGINX_VERSION}.tar.gz && \

+ 3 - 3
frameworks/PHP/php-ngx/php-ngx-pgsql.dockerfile

@@ -7,13 +7,13 @@ RUN LC_ALL=C.UTF-8 add-apt-repository ppa:ondrej/php > /dev/null
 RUN apt-get update -yqq > /dev/null && \
 RUN apt-get update -yqq > /dev/null && \
     apt-get install -yqq wget git unzip libxml2-dev cmake make systemtap-sdt-dev \
     apt-get install -yqq wget git unzip libxml2-dev cmake make systemtap-sdt-dev \
                     zlib1g-dev libpcre3-dev libargon2-0-dev libsodium-dev \
                     zlib1g-dev libpcre3-dev libargon2-0-dev libsodium-dev \
-                    php8.0-cli php8.0-dev libphp8.0-embed php8.0-pgsql nginx > /dev/null
+                    php8.1-cli php8.1-dev libphp8.1-embed php8.1-pgsql nginx > /dev/null
 
 
 ADD ./ ./
 ADD ./ ./
 
 
-ENV NGINX_VERSION 1.21.3
+ENV NGINX_VERSION 1.21.4
 
 
-RUN git clone -b v0.0.25 --single-branch --depth 1 https://github.com/rryqszq4/ngx_php7.git > /dev/null
+RUN git clone -b v0.0.26 --single-branch --depth 1 https://github.com/rryqszq4/ngx_php7.git > /dev/null
 
 
 RUN wget -q http://nginx.org/download/nginx-${NGINX_VERSION}.tar.gz && \
 RUN wget -q http://nginx.org/download/nginx-${NGINX_VERSION}.tar.gz && \
     tar -zxf nginx-${NGINX_VERSION}.tar.gz && \
     tar -zxf nginx-${NGINX_VERSION}.tar.gz && \

+ 3 - 3
frameworks/PHP/php-ngx/php-ngx.dockerfile

@@ -7,12 +7,12 @@ RUN LC_ALL=C.UTF-8 add-apt-repository ppa:ondrej/php > /dev/null
 RUN apt-get update -yqq > /dev/null && \
 RUN apt-get update -yqq > /dev/null && \
     apt-get install -yqq wget git unzip libxml2-dev cmake make systemtap-sdt-dev \
     apt-get install -yqq wget git unzip libxml2-dev cmake make systemtap-sdt-dev \
                     zlib1g-dev libpcre3-dev libargon2-0-dev libsodium-dev \
                     zlib1g-dev libpcre3-dev libargon2-0-dev libsodium-dev \
-                    php8.0-cli php8.0-dev libphp8.0-embed php8.0-mysql nginx > /dev/null
+                    php8.1-cli php8.1-dev libphp8.1-embed php8.1-mysql nginx > /dev/null
 ADD ./ ./
 ADD ./ ./
 
 
-ENV NGINX_VERSION 1.21.3
+ENV NGINX_VERSION 1.21.4
 
 
-RUN git clone -b v0.0.25 --single-branch --depth 1 https://github.com/rryqszq4/ngx_php7.git > /dev/null
+RUN git clone -b v0.0.26 --single-branch --depth 1 https://github.com/rryqszq4/ngx_php7.git > /dev/null
 
 
 RUN wget -q http://nginx.org/download/nginx-${NGINX_VERSION}.tar.gz && \
 RUN wget -q http://nginx.org/download/nginx-${NGINX_VERSION}.tar.gz && \
     tar -zxf nginx-${NGINX_VERSION}.tar.gz && \
     tar -zxf nginx-${NGINX_VERSION}.tar.gz && \

+ 4 - 4
frameworks/PHP/webman/webman.dockerfile

@@ -5,14 +5,14 @@ ARG DEBIAN_FRONTEND=noninteractive
 RUN apt-get update -yqq && apt-get install -yqq software-properties-common > /dev/null
 RUN apt-get update -yqq && apt-get install -yqq software-properties-common > /dev/null
 RUN LC_ALL=C.UTF-8 add-apt-repository ppa:ondrej/php
 RUN LC_ALL=C.UTF-8 add-apt-repository ppa:ondrej/php
 RUN apt-get update -yqq > /dev/null && \
 RUN apt-get update -yqq > /dev/null && \
-    apt-get install -yqq php8.0-cli php8.0-pgsql php8.0-xml > /dev/null
+    apt-get install -yqq php8.1-cli php8.1-pgsql php8.1-xml > /dev/null
 
 
 COPY --from=composer:latest /usr/bin/composer /usr/local/bin/composer
 COPY --from=composer:latest /usr/bin/composer /usr/local/bin/composer
 
 
-RUN apt-get install -y php-pear php8.0-dev libevent-dev git > /dev/null
-RUN pecl install event-3.0.5 > /dev/null && echo "extension=event.so" > /etc/php/8.0/cli/conf.d/event.ini
+RUN apt-get update -yqq && apt-get install -y php-pear php8.1-dev libevent-dev git > /dev/null
+RUN pecl install event-3.0.6 > /dev/null && echo "extension=event.so" > /etc/php/8.1/cli/conf.d/event.ini
 
 
-COPY php.ini /etc/php/8.0/cli/php.ini
+COPY php.ini /etc/php/8.1/cli/php.ini
 
 
 ADD ./ /webman
 ADD ./ /webman
 WORKDIR /webman
 WORKDIR /webman

+ 267 - 1
frameworks/Rust/axum/Cargo.lock

@@ -56,6 +56,111 @@ dependencies = [
  "winapi",
  "winapi",
 ]
 ]
 
 
+[[package]]
+name = "async-channel"
+version = "1.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2114d64672151c0c5eaa5e131ec84a74f06e1e559830dabba01ca30605d66319"
+dependencies = [
+ "concurrent-queue",
+ "event-listener",
+ "futures-core",
+]
+
+[[package]]
+name = "async-executor"
+version = "1.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "871f9bb5e0a22eeb7e8cf16641feb87c9dc67032ccf8ff49e772eb9941d3a965"
+dependencies = [
+ "async-task",
+ "concurrent-queue",
+ "fastrand",
+ "futures-lite",
+ "once_cell",
+ "slab",
+]
+
+[[package]]
+name = "async-global-executor"
+version = "2.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9586ec52317f36de58453159d48351bc244bc24ced3effc1fce22f3d48664af6"
+dependencies = [
+ "async-channel",
+ "async-executor",
+ "async-io",
+ "async-mutex",
+ "blocking",
+ "futures-lite",
+ "num_cpus",
+ "once_cell",
+]
+
+[[package]]
+name = "async-io"
+version = "1.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a811e6a479f2439f0c04038796b5cfb3d2ad56c230e0f2d3f7b04d68cfee607b"
+dependencies = [
+ "concurrent-queue",
+ "futures-lite",
+ "libc",
+ "log",
+ "once_cell",
+ "parking",
+ "polling",
+ "slab",
+ "socket2 0.4.2",
+ "waker-fn",
+ "winapi",
+]
+
+[[package]]
+name = "async-lock"
+version = "2.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e6a8ea61bf9947a1007c5cada31e647dbc77b103c679858150003ba697ea798b"
+dependencies = [
+ "event-listener",
+]
+
+[[package]]
+name = "async-mutex"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "479db852db25d9dbf6204e6cb6253698f175c15726470f78af0d918e99d6156e"
+dependencies = [
+ "event-listener",
+]
+
+[[package]]
+name = "async-std"
+version = "1.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f8056f1455169ab86dd47b47391e4ab0cbd25410a70e9fe675544f49bafaf952"
+dependencies = [
+ "async-channel",
+ "async-global-executor",
+ "async-io",
+ "async-lock",
+ "crossbeam-utils",
+ "futures-channel",
+ "futures-core",
+ "futures-io",
+ "futures-lite",
+ "gloo-timers",
+ "kv-log-macro",
+ "log",
+ "memchr",
+ "num_cpus",
+ "once_cell",
+ "pin-project-lite",
+ "pin-utils",
+ "slab",
+ "wasm-bindgen-futures",
+]
+
 [[package]]
 [[package]]
 name = "async-stream"
 name = "async-stream"
 version = "0.3.2"
 version = "0.3.2"
@@ -77,6 +182,12 @@ dependencies = [
  "syn",
  "syn",
 ]
 ]
 
 
+[[package]]
+name = "async-task"
+version = "4.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e91831deabf0d6d7ec49552e489aed63b7456a7a3c46cff62adad428110b0af0"
+
 [[package]]
 [[package]]
 name = "async-trait"
 name = "async-trait"
 version = "0.1.51"
 version = "0.1.51"
@@ -97,6 +208,12 @@ dependencies = [
  "num-traits",
  "num-traits",
 ]
 ]
 
 
+[[package]]
+name = "atomic-waker"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "065374052e7df7ee4047b1160cca5e1467a12351a40b3da123c870ba0b8eda2a"
+
 [[package]]
 [[package]]
 name = "atty"
 name = "atty"
 version = "0.2.14"
 version = "0.2.14"
@@ -116,8 +233,9 @@ checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
 
 
 [[package]]
 [[package]]
 name = "axum"
 name = "axum"
-version = "0.1.1"
+version = "0.1.2"
 dependencies = [
 dependencies = [
+ "async-std",
  "async-stream",
  "async-stream",
  "async-trait",
  "async-trait",
  "axum 0.3.2",
  "axum 0.3.2",
@@ -267,6 +385,20 @@ dependencies = [
  "generic-array",
  "generic-array",
 ]
 ]
 
 
+[[package]]
+name = "blocking"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "046e47d4b2d391b1f6f8b407b1deb8dee56c1852ccd868becf2710f601b5f427"
+dependencies = [
+ "async-channel",
+ "async-task",
+ "atomic-waker",
+ "fastrand",
+ "futures-lite",
+ "once_cell",
+]
+
 [[package]]
 [[package]]
 name = "bson"
 name = "bson"
 version = "2.0.1"
 version = "2.0.1"
@@ -341,6 +473,12 @@ version = "1.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8"
 checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8"
 
 
+[[package]]
+name = "cache-padded"
+version = "1.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "631ae5198c9be5e753e5cc215e1bd73c2b466a3565173db433f52bb9d3e66dba"
+
 [[package]]
 [[package]]
 name = "cc"
 name = "cc"
 version = "1.0.71"
 version = "1.0.71"
@@ -378,6 +516,15 @@ dependencies = [
  "winapi",
  "winapi",
 ]
 ]
 
 
+[[package]]
+name = "concurrent-queue"
+version = "1.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "30ed07550be01594c6026cff2a1d7fe9c8f683caa798e12b68694ac9e88286a3"
+dependencies = [
+ "cache-padded",
+]
+
 [[package]]
 [[package]]
 name = "console"
 name = "console"
 version = "0.14.1"
 version = "0.14.1"
@@ -497,6 +644,16 @@ dependencies = [
  "subtle",
  "subtle",
 ]
 ]
 
 
+[[package]]
+name = "ctor"
+version = "0.1.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ccc0a48a9b826acdf4028595adc9db92caea352f7af011a3034acd172a52a0aa"
+dependencies = [
+ "quote",
+ "syn",
+]
+
 [[package]]
 [[package]]
 name = "darling"
 name = "darling"
 version = "0.13.0"
 version = "0.13.0"
@@ -700,6 +857,12 @@ dependencies = [
  "version_check 0.9.3",
  "version_check 0.9.3",
 ]
 ]
 
 
+[[package]]
+name = "event-listener"
+version = "2.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f7531096570974c3a9dcf9e4b8e1cede1ec26cf5046219fb3b9d897503b9be59"
+
 [[package]]
 [[package]]
 name = "fallible-iterator"
 name = "fallible-iterator"
 version = "0.2.0"
 version = "0.2.0"
@@ -716,6 +879,15 @@ dependencies = [
  "regex",
  "regex",
 ]
 ]
 
 
+[[package]]
+name = "fastrand"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b394ed3d285a429378d3b384b9eb1285267e7df4b166df24b7a6939a04dc392e"
+dependencies = [
+ "instant",
+]
+
 [[package]]
 [[package]]
 name = "flate2"
 name = "flate2"
 version = "1.0.22"
 version = "1.0.22"
@@ -818,6 +990,21 @@ version = "0.3.17"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "522de2a0fe3e380f1bc577ba0474108faf3f6b18321dbf60b3b9c39a75073377"
 checksum = "522de2a0fe3e380f1bc577ba0474108faf3f6b18321dbf60b3b9c39a75073377"
 
 
+[[package]]
+name = "futures-lite"
+version = "1.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7694489acd39452c77daa48516b894c153f192c3578d5a839b62c58099fcbf48"
+dependencies = [
+ "fastrand",
+ "futures-core",
+ "futures-io",
+ "memchr",
+ "parking",
+ "pin-project-lite",
+ "waker-fn",
+]
+
 [[package]]
 [[package]]
 name = "futures-macro"
 name = "futures-macro"
 version = "0.3.17"
 version = "0.3.17"
@@ -920,6 +1107,19 @@ dependencies = [
  "regex",
  "regex",
 ]
 ]
 
 
+[[package]]
+name = "gloo-timers"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "47204a46aaff920a1ea58b11d03dec6f704287d27561724a4631e450654a891f"
+dependencies = [
+ "futures-channel",
+ "futures-core",
+ "js-sys",
+ "wasm-bindgen",
+ "web-sys",
+]
+
 [[package]]
 [[package]]
 name = "grep-cli"
 name = "grep-cli"
 version = "0.1.6"
 version = "0.1.6"
@@ -1144,6 +1344,15 @@ dependencies = [
  "wasm-bindgen",
  "wasm-bindgen",
 ]
 ]
 
 
+[[package]]
+name = "kv-log-macro"
+version = "1.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0de8b303297635ad57c9f5059fd9cee7a47f8e8daa09df0fcd07dd39fb22977f"
+dependencies = [
+ "log",
+]
+
 [[package]]
 [[package]]
 name = "lazy_static"
 name = "lazy_static"
 version = "1.4.0"
 version = "1.4.0"
@@ -1193,6 +1402,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
 checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
 dependencies = [
 dependencies = [
  "cfg-if",
  "cfg-if",
+ "value-bag",
 ]
 ]
 
 
 [[package]]
 [[package]]
@@ -1462,6 +1672,12 @@ dependencies = [
  "winapi",
  "winapi",
 ]
 ]
 
 
+[[package]]
+name = "parking"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "427c3892f9e783d91cc128285287e70a59e206ca452770ece88a76f7a3eddd72"
+
 [[package]]
 [[package]]
 name = "parking_lot"
 name = "parking_lot"
 version = "0.11.2"
 version = "0.11.2"
@@ -1590,6 +1806,19 @@ dependencies = [
  "xml-rs",
  "xml-rs",
 ]
 ]
 
 
+[[package]]
+name = "polling"
+version = "2.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "685404d509889fade3e86fe3a5803bca2ec09b0c0778d5ada6ec8bf7a8de5259"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "log",
+ "wepoll-ffi",
+ "winapi",
+]
+
 [[package]]
 [[package]]
 name = "postgres-protocol"
 name = "postgres-protocol"
 version = "0.6.2"
 version = "0.6.2"
@@ -2737,6 +2966,16 @@ dependencies = [
  "v_escape",
  "v_escape",
 ]
 ]
 
 
+[[package]]
+name = "value-bag"
+version = "1.0.0-alpha.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "79923f7731dc61ebfba3633098bf3ac533bbd35ccd8c57e7088d9a5eebe0263f"
+dependencies = [
+ "ctor",
+ "version_check 0.9.3",
+]
+
 [[package]]
 [[package]]
 name = "vcpkg"
 name = "vcpkg"
 version = "0.2.15"
 version = "0.2.15"
@@ -2755,6 +2994,12 @@ version = "0.9.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe"
 checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe"
 
 
+[[package]]
+name = "waker-fn"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca"
+
 [[package]]
 [[package]]
 name = "walkdir"
 name = "walkdir"
 version = "2.3.2"
 version = "2.3.2"
@@ -2807,6 +3052,18 @@ dependencies = [
  "wasm-bindgen-shared",
  "wasm-bindgen-shared",
 ]
 ]
 
 
+[[package]]
+name = "wasm-bindgen-futures"
+version = "0.4.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e8d7523cb1f2a4c96c1317ca690031b714a51cc14e05f712446691f413f5d39"
+dependencies = [
+ "cfg-if",
+ "js-sys",
+ "wasm-bindgen",
+ "web-sys",
+]
+
 [[package]]
 [[package]]
 name = "wasm-bindgen-macro"
 name = "wasm-bindgen-macro"
 version = "0.2.78"
 version = "0.2.78"
@@ -2865,6 +3122,15 @@ dependencies = [
  "webpki",
  "webpki",
 ]
 ]
 
 
+[[package]]
+name = "wepoll-ffi"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d743fdedc5c64377b5fc2bc036b01c7fd642205a0d96356034ae3404d49eb7fb"
+dependencies = [
+ "cc",
+]
+
 [[package]]
 [[package]]
 name = "whoami"
 name = "whoami"
 version = "1.2.0"
 version = "1.2.0"

+ 2 - 1
frameworks/Rust/axum/Cargo.toml

@@ -1,6 +1,6 @@
 [package]
 [package]
 name = "axum"
 name = "axum"
-version = "0.1.1"
+version = "0.1.2"
 authors = ["Dragos Varovici <[email protected]>"]
 authors = ["Dragos Varovici <[email protected]>"]
 edition = "2018"
 edition = "2018"
 
 
@@ -26,6 +26,7 @@ rand = { version = "^0.8", features = ["small_rng"]}
 yarte = { version = "^0.15" }
 yarte = { version = "^0.15" }
 async-stream = { version = "^0.3" }
 async-stream = { version = "^0.3" }
 async-trait = { version = "0.1" }
 async-trait = { version = "0.1" }
+async-std = "1.10"
 futures = { version = "^0.3" }
 futures = { version = "^0.3" }
 futures-util = { version = "^0.3" }
 futures-util = { version = "^0.3" }
 dotenv = { version = "^0.15" }
 dotenv = { version = "^0.15" }

+ 4 - 1
frameworks/Rust/axum/axum-bb8.dockerfile

@@ -11,10 +11,13 @@ COPY ./src ./src
 COPY ./templates ./templates
 COPY ./templates ./templates
 COPY ./Cargo.toml ./Cargo.toml
 COPY ./Cargo.toml ./Cargo.toml
 COPY ./Cargo.lock ./Cargo.lock
 COPY ./Cargo.lock ./Cargo.lock
+COPY ./run.sh ./run.sh
+RUN chmod +x ./run.sh
 
 
 ENV RUSTFLAGS "-C target-cpu=native"
 ENV RUSTFLAGS "-C target-cpu=native"
 RUN cargo build --release
 RUN cargo build --release
+RUN cp ./target/release/axum-bb8 ./target/release/axum-techempower
 
 
 EXPOSE 8000
 EXPOSE 8000
 
 
-CMD ["./target/release/axum-bb8"]
+CMD ["./run.sh"]

+ 4 - 1
frameworks/Rust/axum/axum-mongo.dockerfile

@@ -11,10 +11,13 @@ COPY ./src ./src
 COPY ./templates ./templates
 COPY ./templates ./templates
 COPY ./Cargo.toml ./Cargo.toml
 COPY ./Cargo.toml ./Cargo.toml
 COPY ./Cargo.lock ./Cargo.lock
 COPY ./Cargo.lock ./Cargo.lock
+COPY ./run.sh ./run.sh
+RUN chmod +x ./run.sh
 
 
 ENV RUSTFLAGS "-C target-cpu=native"
 ENV RUSTFLAGS "-C target-cpu=native"
 RUN cargo build --release
 RUN cargo build --release
+RUN cp ./target/release/axum-mongo ./target/release/axum-techempower
 
 
 EXPOSE 8000
 EXPOSE 8000
 
 
-CMD ["./target/release/axum-mongo"]
+CMD ["./run.sh"]

+ 5 - 1
frameworks/Rust/axum/axum-sqlx.dockerfile

@@ -11,10 +11,14 @@ COPY ./src ./src
 COPY ./templates ./templates
 COPY ./templates ./templates
 COPY ./Cargo.toml ./Cargo.toml
 COPY ./Cargo.toml ./Cargo.toml
 COPY ./Cargo.lock ./Cargo.lock
 COPY ./Cargo.lock ./Cargo.lock
+COPY ./run.sh ./run.sh
+RUN chmod +x ./run.sh
 
 
 ENV RUSTFLAGS "-C target-cpu=native"
 ENV RUSTFLAGS "-C target-cpu=native"
 RUN cargo build --release
 RUN cargo build --release
+RUN cp ./target/release/axum-sqlx ./target/release/axum-techempower
+
 
 
 EXPOSE 8000
 EXPOSE 8000
 
 
-CMD ["./target/release/axum-sqlx"]
+CMD ["./run.sh"]

+ 4 - 1
frameworks/Rust/axum/axum.dockerfile

@@ -9,10 +9,13 @@ COPY ./src ./src
 COPY ./templates ./templates
 COPY ./templates ./templates
 COPY ./Cargo.toml ./Cargo.toml
 COPY ./Cargo.toml ./Cargo.toml
 COPY ./Cargo.lock ./Cargo.lock
 COPY ./Cargo.lock ./Cargo.lock
+COPY ./run.sh ./run.sh
+RUN chmod +x ./run.sh
 
 
 ENV RUSTFLAGS "-C target-cpu=native"
 ENV RUSTFLAGS "-C target-cpu=native"
 RUN cargo build --release
 RUN cargo build --release
+RUN cp ./target/release/axum ./target/release/axum-techempower
 
 
 EXPOSE 8000
 EXPOSE 8000
 
 
-CMD ["./target/release/axum"]
+CMD ["./run.sh"]

+ 3 - 0
frameworks/Rust/axum/run.sh

@@ -0,0 +1,3 @@
+#!/bin/bash
+
+./target/release/axum-techempower

+ 1 - 0
frameworks/Rust/axum/src/common.rs

@@ -0,0 +1 @@
+pub const POOL_SIZE: u32 = 56;

+ 0 - 17
frameworks/Rust/axum/src/common_handlers.rs

@@ -1,17 +0,0 @@
-use axum::http::StatusCode;
-use axum::Json;
-use axum::response::IntoResponse;
-
-use crate::models_common::{Message};
-
-pub async fn plaintext() -> &'static str {
-    "Hello, World!"
-}
-
-pub async fn json() -> impl IntoResponse {
-    let message = Message {
-        message: "Hello, World!",
-    };
-
-    (StatusCode::OK, Json(message))
-}

+ 2 - 1
frameworks/Rust/axum/src/database_bb8.rs

@@ -3,6 +3,7 @@ use axum::http::StatusCode;
 use bb8::{Pool, PooledConnection};
 use bb8::{Pool, PooledConnection};
 use bb8_postgres::PostgresConnectionManager;
 use bb8_postgres::PostgresConnectionManager;
 use bb8_postgres::tokio_postgres::NoTls;
 use bb8_postgres::tokio_postgres::NoTls;
+use crate::common::POOL_SIZE;
 use crate::utils::internal_error;
 use crate::utils::internal_error;
 
 
 pub type ConnectionManager = PostgresConnectionManager<NoTls>;
 pub type ConnectionManager = PostgresConnectionManager<NoTls>;
@@ -12,7 +13,7 @@ pub type Connection = PooledConnection<'static, ConnectionManager>;
 pub async fn create_bb8_pool(database_url: String) -> ConnectionPool {
 pub async fn create_bb8_pool(database_url: String) -> ConnectionPool {
     let manager = PostgresConnectionManager::new_from_stringlike(database_url, NoTls).unwrap();
     let manager = PostgresConnectionManager::new_from_stringlike(database_url, NoTls).unwrap();
 
 
-    Pool::builder().build(manager).await.unwrap()
+    Pool::builder().max_size(POOL_SIZE).build(manager).await.unwrap()
 }
 }
 
 
 pub struct DatabaseConnection(pub Connection);
 pub struct DatabaseConnection(pub Connection);

+ 2 - 1
frameworks/Rust/axum/src/database_sqlx.rs

@@ -4,10 +4,11 @@ use axum::http::StatusCode;
 use sqlx::{PgPool, Postgres};
 use sqlx::{PgPool, Postgres};
 use sqlx::pool::PoolConnection;
 use sqlx::pool::PoolConnection;
 use sqlx::postgres::PgPoolOptions;
 use sqlx::postgres::PgPoolOptions;
+use crate::common::POOL_SIZE;
 use crate::utils::internal_error;
 use crate::utils::internal_error;
 
 
 pub async fn create_pool(database_url: String) -> PgPool {
 pub async fn create_pool(database_url: String) -> PgPool {
-    PgPoolOptions::new().max_connections(100).connect(&*database_url).await.unwrap()
+    PgPoolOptions::new().max_connections(POOL_SIZE).min_connections(56).connect(&*database_url).await.unwrap()
 }
 }
 
 
 pub struct DatabaseConnection(pub PoolConnection<Postgres>);
 pub struct DatabaseConnection(pub PoolConnection<Postgres>);

+ 20 - 6
frameworks/Rust/axum/src/main.rs

@@ -4,30 +4,44 @@ extern crate async_trait;
 extern crate tokio_pg_mapper_derive;
 extern crate tokio_pg_mapper_derive;
 extern crate tokio_pg_mapper;
 extern crate tokio_pg_mapper;
 
 
-mod common_handlers;
 mod models_common;
 mod models_common;
+mod server;
+mod common;
 
 
+use models_common::{Message};
+
+use axum::http::StatusCode;
+use axum::Json;
 use dotenv::dotenv;
 use dotenv::dotenv;
-use std::net::{Ipv4Addr, SocketAddr};
 use axum::{Router, routing::get};
 use axum::{Router, routing::get};
 use axum::http::{header, HeaderValue};
 use axum::http::{header, HeaderValue};
+use axum::response::IntoResponse;
 use tower_http::set_header::SetResponseHeaderLayer;
 use tower_http::set_header::SetResponseHeaderLayer;
 use hyper::Body;
 use hyper::Body;
 
 
-use common_handlers::{json, plaintext};
+pub async fn plaintext() -> &'static str {
+    "Hello, World!"
+}
+
+pub async fn json() -> impl IntoResponse {
+    let message = Message {
+        message: "Hello, World!",
+    };
+
+    (StatusCode::OK, Json(message))
+}
 
 
 #[tokio::main]
 #[tokio::main]
 async fn main() {
 async fn main() {
     dotenv().ok();
     dotenv().ok();
 
 
-    let addr = SocketAddr::from((Ipv4Addr::UNSPECIFIED, 8000));
-
     let app =  Router::new()
     let app =  Router::new()
         .route("/plaintext", get(plaintext))
         .route("/plaintext", get(plaintext))
         .route("/json", get(json))
         .route("/json", get(json))
         .layer(SetResponseHeaderLayer::<_, Body>::if_not_present(header::SERVER, HeaderValue::from_static("Axum")));
         .layer(SetResponseHeaderLayer::<_, Body>::if_not_present(header::SERVER, HeaderValue::from_static("Axum")));
 
 
-    axum::Server::bind(&addr)
+    server::builder()
+        .http1_pipeline_flush(true)
         .serve(app.into_make_service())
         .serve(app.into_make_service())
         .await
         .await
         .unwrap();
         .unwrap();

+ 3 - 8
frameworks/Rust/axum/src/main_bb8.rs

@@ -3,14 +3,14 @@ extern crate dotenv;
 #[macro_use]
 #[macro_use]
 extern crate async_trait;
 extern crate async_trait;
 
 
-mod common_handlers;
 mod models_common;
 mod models_common;
 mod models_bb8;
 mod models_bb8;
 mod database_bb8;
 mod database_bb8;
 mod utils;
 mod utils;
+mod server;
+mod common;
 
 
 use dotenv::dotenv;
 use dotenv::dotenv;
-use std::net::{Ipv4Addr, SocketAddr};
 use std::env;
 use std::env;
 use crate::database_bb8::{Connection, create_bb8_pool, DatabaseConnection};
 use crate::database_bb8::{Connection, create_bb8_pool, DatabaseConnection};
 use axum::{
 use axum::{
@@ -30,7 +30,6 @@ use tokio_pg_mapper::FromTokioPostgresRow;
 use yarte::Template;
 use yarte::Template;
 
 
 use models_bb8::{World, Fortune};
 use models_bb8::{World, Fortune};
-use common_handlers::{json, plaintext};
 use utils::{Params, parse_params, random_number};
 use utils::{Params, parse_params, random_number};
 use crate::utils::Utf8Html;
 use crate::utils::Utf8Html;
 
 
@@ -142,14 +141,10 @@ async fn main() {
     let database_url = env::var("AXUM_TECHEMPOWER_DATABASE_URL").ok()
     let database_url = env::var("AXUM_TECHEMPOWER_DATABASE_URL").ok()
         .expect("AXUM_TECHEMPOWER_DATABASE_URL environment variable was not set");
         .expect("AXUM_TECHEMPOWER_DATABASE_URL environment variable was not set");
 
 
-    let addr = SocketAddr::from((Ipv4Addr::UNSPECIFIED, 8000));
-
     // setup connection pool
     // setup connection pool
     let pool = create_bb8_pool(database_url).await;
     let pool = create_bb8_pool(database_url).await;
 
 
     let router = Router::new()
     let router = Router::new()
-        .route("/plaintext", get(plaintext))
-        .route("/json", get(json))
         .route("/fortunes", get(fortunes))
         .route("/fortunes", get(fortunes))
         .route("/db", get(db))
         .route("/db", get(db))
         .route("/queries", get(queries))
         .route("/queries", get(queries))
@@ -157,7 +152,7 @@ async fn main() {
         .layer(AddExtensionLayer::new(pool))
         .layer(AddExtensionLayer::new(pool))
         .layer(SetResponseHeaderLayer::<_, Body>::if_not_present(header::SERVER, HeaderValue::from_static("Axum")));
         .layer(SetResponseHeaderLayer::<_, Body>::if_not_present(header::SERVER, HeaderValue::from_static("Axum")));
 
 
-    axum::Server::bind(&addr)
+    server::builder()
         .serve(router.into_make_service())
         .serve(router.into_make_service())
         .await
         .await
         .unwrap();
         .unwrap();

+ 9 - 11
frameworks/Rust/axum/src/main_mongo.rs

@@ -3,15 +3,16 @@ extern crate dotenv;
 #[macro_use]
 #[macro_use]
 extern crate async_trait;
 extern crate async_trait;
 
 
-mod common_handlers;
 mod models_common;
 mod models_common;
 mod models_mongo;
 mod models_mongo;
 mod database_mongo;
 mod database_mongo;
 mod utils;
 mod utils;
+mod server;
+mod common;
 
 
 use dotenv::dotenv;
 use dotenv::dotenv;
-use std::net::{Ipv4Addr, SocketAddr};
 use std::env;
 use std::env;
+use std::time::Duration;
 use axum::{
 use axum::{
     extract::{Query},
     extract::{Query},
     http::StatusCode,
     http::StatusCode,
@@ -30,10 +31,9 @@ use mongodb::{bson::doc, Client, Database};
 use mongodb::options::ClientOptions;
 use mongodb::options::ClientOptions;
 
 
 use models_mongo::{World, Fortune};
 use models_mongo::{World, Fortune};
-use common_handlers::{json, plaintext};
 use utils::{Params, parse_params, random_number, Utf8Html};
 use utils::{Params, parse_params, random_number, Utf8Html};
-use crate::database_mongo::DatabaseConnection;
-use crate::models_mongo::FortuneInfo;
+use database_mongo::DatabaseConnection;
+use models_mongo::FortuneInfo;
 
 
 async fn db(DatabaseConnection(mut db): DatabaseConnection) -> impl IntoResponse {
 async fn db(DatabaseConnection(mut db): DatabaseConnection) -> impl IntoResponse {
     let mut rng = SmallRng::from_entropy();
     let mut rng = SmallRng::from_entropy();
@@ -107,24 +107,22 @@ async fn main() {
     let database_url = env::var("AXUM_TECHEMPOWER_MONGODB_URL").ok()
     let database_url = env::var("AXUM_TECHEMPOWER_MONGODB_URL").ok()
         .expect("AXUM_TECHEMPOWER_MONGODB_URL environment variable was not set");
         .expect("AXUM_TECHEMPOWER_MONGODB_URL environment variable was not set");
 
 
-    let addr = SocketAddr::from((Ipv4Addr::UNSPECIFIED, 8000));
-
     // setup connection pool
     // setup connection pool
     let mut client_options = ClientOptions::parse(database_url).await.unwrap();
     let mut client_options = ClientOptions::parse(database_url).await.unwrap();
-    client_options.max_pool_size = Some(100);
+    client_options.max_pool_size = Some(common::POOL_SIZE);
+    client_options.min_pool_size = Some(common::POOL_SIZE);
+    client_options.connect_timeout = Some(Duration::from_millis(200));
 
 
     let client = Client::with_options(client_options).unwrap();
     let client = Client::with_options(client_options).unwrap();
 
 
     let app = Router::new()
     let app = Router::new()
-        .route("/plaintext", get(plaintext))
-        .route("/json", get(json))
         .route("/fortunes", get(fortunes))
         .route("/fortunes", get(fortunes))
         .route("/db", get(db))
         .route("/db", get(db))
         .route("/queries", get(queries))
         .route("/queries", get(queries))
         .layer(AddExtensionLayer::new(client))
         .layer(AddExtensionLayer::new(client))
         .layer(SetResponseHeaderLayer::<_, Body>::if_not_present(header::SERVER, HeaderValue::from_static("Axum")));
         .layer(SetResponseHeaderLayer::<_, Body>::if_not_present(header::SERVER, HeaderValue::from_static("Axum")));
 
 
-    axum::Server::bind(&addr)
+    server::builder()
         .serve(app.into_make_service())
         .serve(app.into_make_service())
         .await
         .await
         .unwrap();
         .unwrap();

+ 3 - 8
frameworks/Rust/axum/src/main_sqlx.rs

@@ -3,14 +3,14 @@ extern crate dotenv;
 #[macro_use]
 #[macro_use]
 extern crate async_trait;
 extern crate async_trait;
 
 
-mod common_handlers;
 mod models_common;
 mod models_common;
 mod models_sqlx;
 mod models_sqlx;
 mod database_sqlx;
 mod database_sqlx;
 mod utils;
 mod utils;
+mod server;
+mod common;
 
 
 use dotenv::dotenv;
 use dotenv::dotenv;
-use std::net::{Ipv4Addr, SocketAddr};
 use std::env;
 use std::env;
 use crate::database_sqlx::{DatabaseConnection};
 use crate::database_sqlx::{DatabaseConnection};
 use axum::{
 use axum::{
@@ -30,7 +30,6 @@ use yarte::Template;
 
 
 use models_sqlx::{World, Fortune};
 use models_sqlx::{World, Fortune};
 use database_sqlx::create_pool;
 use database_sqlx::create_pool;
-use common_handlers::{json, plaintext};
 use utils::{Params, parse_params, random_number, Utf8Html};
 use utils::{Params, parse_params, random_number, Utf8Html};
 
 
 async fn db(DatabaseConnection(mut conn): DatabaseConnection) -> impl IntoResponse {
 async fn db(DatabaseConnection(mut conn): DatabaseConnection) -> impl IntoResponse {
@@ -115,14 +114,12 @@ async fn main() {
     let database_url = env::var("AXUM_TECHEMPOWER_DATABASE_URL").ok()
     let database_url = env::var("AXUM_TECHEMPOWER_DATABASE_URL").ok()
         .expect("AXUM_TECHEMPOWER_DATABASE_URL environment variable was not set");
         .expect("AXUM_TECHEMPOWER_DATABASE_URL environment variable was not set");
 
 
-    let addr = SocketAddr::from((Ipv4Addr::UNSPECIFIED, 8000));
-
     // setup connection pool
     // setup connection pool
     let pool = create_pool(database_url).await;
     let pool = create_pool(database_url).await;
 
 
     let app = router(pool).await;
     let app = router(pool).await;
 
 
-    axum::Server::bind(&addr)
+    server::builder()
         .serve(app.into_make_service())
         .serve(app.into_make_service())
         .await
         .await
         .unwrap();
         .unwrap();
@@ -130,8 +127,6 @@ async fn main() {
 
 
 async fn router(pool: PgPool) -> Router {
 async fn router(pool: PgPool) -> Router {
     Router::new()
     Router::new()
-        .route("/plaintext", get(plaintext))
-        .route("/json", get(json))
         .route("/fortunes", get(fortunes))
         .route("/fortunes", get(fortunes))
         .route("/db", get(db))
         .route("/db", get(db))
         .route("/queries", get(queries))
         .route("/queries", get(queries))

+ 35 - 0
frameworks/Rust/axum/src/server.rs

@@ -0,0 +1,35 @@
+
+use std::io;
+use std::net::{Ipv4Addr, SocketAddr};
+
+use hyper::server::conn::AddrIncoming;
+use tokio::net::{TcpListener, TcpSocket};
+use crate::common::POOL_SIZE;
+
+pub fn builder() -> hyper::server::Builder<AddrIncoming> {
+    let addr = SocketAddr::from((Ipv4Addr::UNSPECIFIED, 8000));
+    let listener = reuse_listener(addr).expect("couldn't bind to addr");
+    let incoming = AddrIncoming::from_listener(listener).unwrap();
+
+    println!("Started axum server at 8000 with pool size {pool_size}", pool_size=POOL_SIZE);
+
+    axum::Server::builder(incoming).http1_only(true).tcp_nodelay(true)
+}
+
+fn reuse_listener(addr: SocketAddr) -> io::Result<TcpListener> {
+    let socket = match addr {
+        SocketAddr::V4(_) => TcpSocket::new_v4()?,
+        SocketAddr::V6(_) => TcpSocket::new_v6()?,
+    };
+
+    #[cfg(unix)]
+        {
+            if let Err(e) = socket.set_reuseport(true) {
+                eprintln!("error setting SO_REUSEPORT: {}", e);
+            }
+        }
+
+    socket.set_reuseaddr(true)?;
+    socket.bind(addr)?;
+    socket.listen(1024)
+}

+ 105 - 110
frameworks/Rust/xitca-web/Cargo.lock

@@ -4,15 +4,9 @@ version = 3
 
 
 [[package]]
 [[package]]
 name = "ahash"
 name = "ahash"
-version = "0.3.8"
+version = "0.7.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e8fd72866655d1904d6b0997d0b07ba561047d070fbe29de039031c641b61217"
-
-[[package]]
-name = "ahash"
-version = "0.7.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "43bb833f0bf979d8475d38fbf09ed3b8a55e1885fe93ad3f93239fc6a4f17b98"
+checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47"
 dependencies = [
 dependencies = [
  "const-random",
  "const-random",
  "getrandom",
  "getrandom",
@@ -81,9 +75,9 @@ checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8"
 
 
 [[package]]
 [[package]]
 name = "cc"
 name = "cc"
-version = "1.0.70"
+version = "1.0.72"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d26a6ce4b6a484fa3edb70f7efa6fc430fd2b87285fe8b84304fd0936faa0dc0"
+checksum = "22a9137b95ea06864e018375b72adfb7db6e6f68cfc8df5a04d00288050485ee"
 
 
 [[package]]
 [[package]]
 name = "cfg-if"
 name = "cfg-if"
@@ -142,9 +136,9 @@ checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7"
 
 
 [[package]]
 [[package]]
 name = "crypto-mac"
 name = "crypto-mac"
-version = "0.10.1"
+version = "0.11.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bff07008ec701e8028e2ceb8f83f0e4274ee62bd2dbdc4fefff2e9a91824081a"
+checksum = "b1d1a86f49236c215f271d40892d5fc950490551400b02ef360692c29815c714"
 dependencies = [
 dependencies = [
  "generic-array",
  "generic-array",
  "subtle",
  "subtle",
@@ -160,7 +154,6 @@ dependencies = [
  "byteorder",
  "byteorder",
  "diesel_derives",
  "diesel_derives",
  "pq-sys",
  "pq-sys",
- "r2d2",
 ]
 ]
 
 
 [[package]]
 [[package]]
@@ -203,9 +196,9 @@ dependencies = [
 
 
 [[package]]
 [[package]]
 name = "float-cmp"
 name = "float-cmp"
-version = "0.8.0"
+version = "0.9.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e1267f4ac4f343772758f7b1bdcbe767c218bbab93bb432acbf5162bbf85a6c4"
+checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4"
 dependencies = [
 dependencies = [
  "num-traits",
  "num-traits",
 ]
 ]
@@ -333,9 +326,9 @@ dependencies = [
 
 
 [[package]]
 [[package]]
 name = "halfbrown"
 name = "halfbrown"
-version = "0.1.11"
+version = "0.1.12"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c12499524b5585419ab2f51545a19b842263a373580a83c0eb98a0142a260a10"
+checksum = "3ed39577259d319b81a15176a32673271be2786cb463889703c58c90fe83c825"
 dependencies = [
 dependencies = [
  "hashbrown",
  "hashbrown",
  "serde",
  "serde",
@@ -343,12 +336,11 @@ dependencies = [
 
 
 [[package]]
 [[package]]
 name = "hashbrown"
 name = "hashbrown"
-version = "0.7.2"
+version = "0.11.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "96282e96bfcd3da0d3aa9938bedf1e50df3269b6db08b4876d2da0bb1a0841cf"
+checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e"
 dependencies = [
 dependencies = [
- "ahash 0.3.8",
- "autocfg",
+ "ahash",
 ]
 ]
 
 
 [[package]]
 [[package]]
@@ -362,9 +354,9 @@ dependencies = [
 
 
 [[package]]
 [[package]]
 name = "hmac"
 name = "hmac"
-version = "0.10.1"
+version = "0.11.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c1441c6b1e930e2817404b5046f1f989899143a12bf92de603b69f4e0aee1e15"
+checksum = "2a2a2320eb7ec0ebe8da8f744d7812d9fc4cb4d09344ac01898dbcb6a20ae69b"
 dependencies = [
 dependencies = [
  "crypto-mac",
  "crypto-mac",
  "digest",
  "digest",
@@ -398,15 +390,15 @@ checksum = "acd94fdbe1d4ff688b67b04eee2e17bd50995534a61539e45adfefb45e5e5503"
 
 
 [[package]]
 [[package]]
 name = "httpdate"
 name = "httpdate"
-version = "1.0.1"
+version = "1.0.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6456b8a6c8f33fee7d958fcd1b60d55b11940a79e63ae87013e6d22e26034440"
+checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421"
 
 
 [[package]]
 [[package]]
 name = "instant"
 name = "instant"
-version = "0.1.11"
+version = "0.1.12"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "716d3d89f35ac6a34fd0eed635395f4c3b76fa889338a4632e5231a8684216bd"
+checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
 dependencies = [
 dependencies = [
  "cfg-if",
  "cfg-if",
 ]
 ]
@@ -441,15 +433,15 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
 
 
 [[package]]
 [[package]]
 name = "libc"
 name = "libc"
-version = "0.2.102"
+version = "0.2.108"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a2a5ac8f984bfcf3a823267e5fde638acc3325f6496633a5da6bb6eb2171e103"
+checksum = "8521a1b57e76b1ec69af7599e75e38e7b7fad6610f037db8c79b127201b5d119"
 
 
 [[package]]
 [[package]]
 name = "libmimalloc-sys"
 name = "libmimalloc-sys"
-version = "0.1.22"
+version = "0.1.23"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1d1b8479c593dba88c2741fc50b92e13dbabbbe0bd504d979f244ccc1a5b1c01"
+checksum = "9636c194f9db483f4d0adf2f99a65011a99f904bd222bbd67fb4df4f37863c30"
 dependencies = [
 dependencies = [
  "cc",
  "cc",
 ]
 ]
@@ -497,18 +489,18 @@ checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
 
 
 [[package]]
 [[package]]
 name = "mimalloc"
 name = "mimalloc"
-version = "0.1.26"
+version = "0.1.27"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fb74897ce508e6c49156fd1476fc5922cbc6e75183c65e399c765a09122e5130"
+checksum = "cf5f78c1d9892fb5677a8b2f543f967ab891ac0f71feecd961435b74f877283a"
 dependencies = [
 dependencies = [
  "libmimalloc-sys",
  "libmimalloc-sys",
 ]
 ]
 
 
 [[package]]
 [[package]]
 name = "mio"
 name = "mio"
-version = "0.7.13"
+version = "0.7.14"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8c2bdb6314ec10835cd3293dd268473a835c02b7b352e788be788b3c6ca6bb16"
+checksum = "8067b404fe97c70829f082dec8bcf4f71225d7eaea1d8645349cb76fa06205cc"
 dependencies = [
 dependencies = [
  "libc",
  "libc",
  "log",
  "log",
@@ -599,18 +591,18 @@ checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e"
 
 
 [[package]]
 [[package]]
 name = "phf"
 name = "phf"
-version = "0.8.0"
+version = "0.10.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3dfb61232e34fcb633f43d12c58f83c1df82962dcdfa565a4e866ffc17dafe12"
+checksum = "b9fc3db1018c4b59d7d582a739436478b6035138b6aecbce989fc91c3e98409f"
 dependencies = [
 dependencies = [
  "phf_shared",
  "phf_shared",
 ]
 ]
 
 
 [[package]]
 [[package]]
 name = "phf_shared"
 name = "phf_shared"
-version = "0.8.0"
+version = "0.10.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c00cf8b9eafe68dde5e9eaa2cef8ee84a9336a47d566ec55ca16589633b65af7"
+checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096"
 dependencies = [
 dependencies = [
  "siphasher",
  "siphasher",
 ]
 ]
@@ -629,9 +621,9 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
 
 
 [[package]]
 [[package]]
 name = "postgres-protocol"
 name = "postgres-protocol"
-version = "0.6.1"
+version = "0.6.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ff3e0f70d32e20923cabf2df02913be7c1842d4c772db8065c00fcfdd1d1bff3"
+checksum = "b145e6a4ed52cb316a27787fc20fe8a25221cb476479f61e4e0327c15b98d91a"
 dependencies = [
 dependencies = [
  "base64",
  "base64",
  "byteorder",
  "byteorder",
@@ -647,9 +639,9 @@ dependencies = [
 
 
 [[package]]
 [[package]]
 name = "postgres-types"
 name = "postgres-types"
-version = "0.2.1"
+version = "0.2.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "430f4131e1b7657b0cd9a2b0c3408d77c9a43a042d300b8c77f981dffcc43a2f"
+checksum = "04619f94ba0cc80999f4fc7073607cb825bc739a883cb6d20900fc5e009d6b0d"
 dependencies = [
 dependencies = [
  "bytes",
  "bytes",
  "fallible-iterator",
  "fallible-iterator",
@@ -658,9 +650,9 @@ dependencies = [
 
 
 [[package]]
 [[package]]
 name = "ppv-lite86"
 name = "ppv-lite86"
-version = "0.2.10"
+version = "0.2.15"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857"
+checksum = "ed0cfbc8191465bed66e1718596ee0b0b35d5ee1f41c5df2189d0fe8bde535ba"
 
 
 [[package]]
 [[package]]
 name = "pq-sys"
 name = "pq-sys"
@@ -694,24 +686,13 @@ dependencies = [
 
 
 [[package]]
 [[package]]
 name = "quote"
 name = "quote"
-version = "1.0.9"
+version = "1.0.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7"
+checksum = "38bc8cc6a5f2e3655e0899c1b848643b2562f853f114bfec7be120678e3ace05"
 dependencies = [
 dependencies = [
  "proc-macro2",
  "proc-macro2",
 ]
 ]
 
 
-[[package]]
-name = "r2d2"
-version = "0.8.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "545c5bc2b880973c9c10e4067418407a0ccaa3091781d1671d46eb35107cb26f"
-dependencies = [
- "log",
- "parking_lot",
- "scheduled-thread-pool",
-]
-
 [[package]]
 [[package]]
 name = "rand"
 name = "rand"
 version = "0.8.4"
 version = "0.8.4"
@@ -804,15 +785,6 @@ dependencies = [
  "sailfish-compiler",
  "sailfish-compiler",
 ]
 ]
 
 
-[[package]]
-name = "scheduled-thread-pool"
-version = "0.2.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dc6f74fd1204073fa02d5d5d68bec8021be4c38690b61264b2fdb48083d0e7d7"
-dependencies = [
- "parking_lot",
-]
-
 [[package]]
 [[package]]
 name = "scopeguard"
 name = "scopeguard"
 version = "1.1.0"
 version = "1.1.0"
@@ -841,9 +813,9 @@ dependencies = [
 
 
 [[package]]
 [[package]]
 name = "serde_json"
 name = "serde_json"
-version = "1.0.68"
+version = "1.0.71"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0f690853975602e1bfe1ccbf50504d67174e3bcf340f23b5ea9992e0587a52d8"
+checksum = "063bf466a64011ac24040a49009724ee60a57da1b437617ceb32e53ad61bfb19"
 dependencies = [
 dependencies = [
  "itoa",
  "itoa",
  "ryu",
  "ryu",
@@ -874,9 +846,9 @@ dependencies = [
 
 
 [[package]]
 [[package]]
 name = "simd-json"
 name = "simd-json"
-version = "0.4.7"
+version = "0.4.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "529edb21cdc2629d7214de58672ec9fe5678b623e8fffb03327f77d7291d8865"
+checksum = "e67eb096671712144fb1357787c4312720c99444f52900ca2a20bee57a02cc64"
 dependencies = [
 dependencies = [
  "halfbrown",
  "halfbrown",
  "serde",
  "serde",
@@ -899,15 +871,15 @@ checksum = "533494a8f9b724d33625ab53c6c4800f7cc445895924a8ef649222dcb76e938b"
 
 
 [[package]]
 [[package]]
 name = "slab"
 name = "slab"
-version = "0.4.4"
+version = "0.4.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c307a32c1c5c437f38c7fd45d753050587732ba8628319fbdf12a7e289ccc590"
+checksum = "9def91fd1e018fe007022791f865d0ccc9b3a0d5001e01aabb8b40e46000afb5"
 
 
 [[package]]
 [[package]]
 name = "smallvec"
 name = "smallvec"
-version = "1.6.1"
+version = "1.7.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e"
+checksum = "1ecab6c735a6bb4139c0caafd0cc3635748bbb3acf4550e8138122099251f309"
 
 
 [[package]]
 [[package]]
 name = "socket2"
 name = "socket2"
@@ -937,15 +909,21 @@ checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601"
 
 
 [[package]]
 [[package]]
 name = "syn"
 name = "syn"
-version = "1.0.76"
+version = "1.0.80"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c6f107db402c2c2055242dbf4d2af0e69197202e9faacbef9571bbe47f5a1b84"
+checksum = "d010a1623fbd906d51d650a9916aaefc05ffa0e4053ff7fe601167f3e715d194"
 dependencies = [
 dependencies = [
  "proc-macro2",
  "proc-macro2",
  "quote",
  "quote",
  "unicode-xid",
  "unicode-xid",
 ]
 ]
 
 
+[[package]]
+name = "tang-rs"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b625cb7d76f7bb1887a11d2e7b97677539924010773844ed17252c6ec7877595"
+
 [[package]]
 [[package]]
 name = "tiny-keccak"
 name = "tiny-keccak"
 version = "2.0.2"
 version = "2.0.2"
@@ -957,9 +935,9 @@ dependencies = [
 
 
 [[package]]
 [[package]]
 name = "tinyvec"
 name = "tinyvec"
-version = "1.4.0"
+version = "1.5.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5241dd6f21443a3606b432718b166d3cedc962fd4b8bea54a8bc7f514ebda986"
+checksum = "2c1c1d5a42b6245520c249549ec267180beaffcc0615401ac8e31853d4b6d8d2"
 dependencies = [
 dependencies = [
  "tinyvec_macros",
  "tinyvec_macros",
 ]
 ]
@@ -972,9 +950,9 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
 
 
 [[package]]
 [[package]]
 name = "tokio"
 name = "tokio"
-version = "1.12.0"
+version = "1.14.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c2c2416fdedca8443ae44b4527de1ea633af61d8f7169ffa6e72c5b53d24efcc"
+checksum = "70e992e41e0d2fb9f755b37446f20900f64446ef54874f40a60c78f021ac6144"
 dependencies = [
 dependencies = [
  "autocfg",
  "autocfg",
  "bytes",
  "bytes",
@@ -992,9 +970,9 @@ dependencies = [
 
 
 [[package]]
 [[package]]
 name = "tokio-macros"
 name = "tokio-macros"
-version = "1.3.0"
+version = "1.6.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "54473be61f4ebe4efd09cec9bd5d16fa51d70ea0192213d754d2d500457db110"
+checksum = "c9efc1aba077437943f7515666aa2b882dfabfbfdf89c819ea75a8d6e9eaba5e"
 dependencies = [
 dependencies = [
  "proc-macro2",
  "proc-macro2",
  "quote",
  "quote",
@@ -1003,9 +981,9 @@ dependencies = [
 
 
 [[package]]
 [[package]]
 name = "tokio-postgres"
 name = "tokio-postgres"
-version = "0.7.2"
+version = "0.7.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2d2b1383c7e4fb9a09e292c7c6afb7da54418d53b045f1c1fac7a911411a2b8b"
+checksum = "4b6c8b33df661b548dcd8f9bf87debb8c56c05657ed291122e1188698c2ece95"
 dependencies = [
 dependencies = [
  "async-trait",
  "async-trait",
  "byteorder",
  "byteorder",
@@ -1026,9 +1004,9 @@ dependencies = [
 
 
 [[package]]
 [[package]]
 name = "tokio-util"
 name = "tokio-util"
-version = "0.6.8"
+version = "0.6.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "08d3725d3efa29485e87311c5b699de63cde14b00ed4d256b8318aa30ca452cd"
+checksum = "9e99e1983e5d376cd8eb4b66604d2e99e79f5bd988c3055891dcd8c9e2604cc0"
 dependencies = [
 dependencies = [
  "bytes",
  "bytes",
  "futures-core",
  "futures-core",
@@ -1040,9 +1018,9 @@ dependencies = [
 
 
 [[package]]
 [[package]]
 name = "tracing"
 name = "tracing"
-version = "0.1.28"
+version = "0.1.29"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "84f96e095c0c82419687c20ddf5cb3eadb61f4e1405923c9dc8e53a1adacbda8"
+checksum = "375a639232caf30edfc78e8d89b2d4c375515393e7af7e16f01cd96917fb2105"
 dependencies = [
 dependencies = [
  "cfg-if",
  "cfg-if",
  "pin-project-lite",
  "pin-project-lite",
@@ -1051,12 +1029,9 @@ dependencies = [
 
 
 [[package]]
 [[package]]
 name = "tracing-core"
 name = "tracing-core"
-version = "0.1.20"
+version = "0.1.21"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "46125608c26121c81b0c6d693eab5a420e416da7e43c426d2e8f7df8da8a3acf"
-dependencies = [
- "lazy_static",
-]
+checksum = "1f4ed65637b8390770814083d20756f87bfa2c21bf2f110babdc5438351746e4"
 
 
 [[package]]
 [[package]]
 name = "typenum"
 name = "typenum"
@@ -1066,9 +1041,9 @@ checksum = "b63708a265f51345575b27fe43f9500ad611579e764c79edbc2037b1121959ec"
 
 
 [[package]]
 [[package]]
 name = "unicode-bidi"
 name = "unicode-bidi"
-version = "0.3.6"
+version = "0.3.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "246f4c42e67e7a4e3c6106ff716a5d067d4132a642840b242e357e468a2a0085"
+checksum = "1a01404663e3db436ed2746d9fefef640d868edae3cceb81c3b8d5732fda678f"
 
 
 [[package]]
 [[package]]
 name = "unicode-normalization"
 name = "unicode-normalization"
@@ -1087,9 +1062,9 @@ checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
 
 
 [[package]]
 [[package]]
 name = "value-trait"
 name = "value-trait"
-version = "0.2.8"
+version = "0.2.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4b637f98040dfa411b01a85b238a8cadbd797b303c23007157dee4bbbd3a72af"
+checksum = "0393efdd7d82f856a927b0fcafa80bca45911f5c89ef6b9d80197bebc284f72e"
 dependencies = [
 dependencies = [
  "float-cmp",
  "float-cmp",
  "halfbrown",
  "halfbrown",
@@ -1152,47 +1127,63 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
 [[package]]
 [[package]]
 name = "xitca-http"
 name = "xitca-http"
 version = "0.1.0"
 version = "0.1.0"
-source = "git+https://github.com/fakeshadow/xitca-web.git?rev=7499e8a5aa4f7e1f17bbf6b6ee0816828dff3149#7499e8a5aa4f7e1f17bbf6b6ee0816828dff3149"
+source = "git+https://github.com/fakeshadow/xitca-web.git?rev=cce3eee576a9b303a71d4654a6c922402b5fd4f3#cce3eee576a9b303a71d4654a6c922402b5fd4f3"
 dependencies = [
 dependencies = [
- "bytes",
  "futures-core",
  "futures-core",
  "http",
  "http",
  "httparse",
  "httparse",
  "httpdate",
  "httpdate",
  "itoa",
  "itoa",
  "pin-project-lite",
  "pin-project-lite",
+ "socket2",
  "tokio",
  "tokio",
  "tracing",
  "tracing",
- "xitca-server",
+ "xitca-io",
  "xitca-service",
  "xitca-service",
 ]
 ]
 
 
 [[package]]
 [[package]]
-name = "xitca-server"
+name = "xitca-http-codegen"
+version = "0.1.0"
+source = "git+https://github.com/fakeshadow/xitca-web.git?rev=cce3eee576a9b303a71d4654a6c922402b5fd4f3#cce3eee576a9b303a71d4654a6c922402b5fd4f3"
+dependencies = [
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "xitca-io"
 version = "0.1.0"
 version = "0.1.0"
-source = "git+https://github.com/fakeshadow/xitca-web.git?rev=7499e8a5aa4f7e1f17bbf6b6ee0816828dff3149#7499e8a5aa4f7e1f17bbf6b6ee0816828dff3149"
+source = "git+https://github.com/fakeshadow/xitca-web.git?rev=cce3eee576a9b303a71d4654a6c922402b5fd4f3#cce3eee576a9b303a71d4654a6c922402b5fd4f3"
 dependencies = [
 dependencies = [
  "bytes",
  "bytes",
+ "tokio",
+]
+
+[[package]]
+name = "xitca-server"
+version = "0.1.0"
+source = "git+https://github.com/fakeshadow/xitca-web.git?rev=cce3eee576a9b303a71d4654a6c922402b5fd4f3#cce3eee576a9b303a71d4654a6c922402b5fd4f3"
+dependencies = [
  "futures-core",
  "futures-core",
  "num_cpus",
  "num_cpus",
- "pin-project-lite",
  "tokio",
  "tokio",
  "tracing",
  "tracing",
+ "xitca-io",
  "xitca-service",
  "xitca-service",
 ]
 ]
 
 
 [[package]]
 [[package]]
 name = "xitca-service"
 name = "xitca-service"
 version = "0.1.0"
 version = "0.1.0"
-source = "git+https://github.com/fakeshadow/xitca-web.git?rev=7499e8a5aa4f7e1f17bbf6b6ee0816828dff3149#7499e8a5aa4f7e1f17bbf6b6ee0816828dff3149"
+source = "git+https://github.com/fakeshadow/xitca-web.git?rev=cce3eee576a9b303a71d4654a6c922402b5fd4f3#cce3eee576a9b303a71d4654a6c922402b5fd4f3"
 
 
 [[package]]
 [[package]]
 name = "xitca-web"
 name = "xitca-web"
 version = "0.1.0"
 version = "0.1.0"
 dependencies = [
 dependencies = [
- "ahash 0.7.4",
+ "ahash",
  "atoi",
  "atoi",
- "bytes",
  "core_affinity",
  "core_affinity",
  "diesel",
  "diesel",
  "futures-util",
  "futures-util",
@@ -1201,20 +1192,24 @@ dependencies = [
  "sailfish",
  "sailfish",
  "serde",
  "serde",
  "simd-json",
  "simd-json",
+ "tang-rs",
  "tokio",
  "tokio",
  "tokio-postgres",
  "tokio-postgres",
  "xitca-http",
  "xitca-http",
- "xitca-web 0.1.0 (git+https://github.com/fakeshadow/xitca-web.git?rev=7499e8a5aa4f7e1f17bbf6b6ee0816828dff3149)",
+ "xitca-http-codegen",
+ "xitca-server",
+ "xitca-service",
+ "xitca-web 0.1.0 (git+https://github.com/fakeshadow/xitca-web.git?rev=cce3eee576a9b303a71d4654a6c922402b5fd4f3)",
 ]
 ]
 
 
 [[package]]
 [[package]]
 name = "xitca-web"
 name = "xitca-web"
 version = "0.1.0"
 version = "0.1.0"
-source = "git+https://github.com/fakeshadow/xitca-web.git?rev=7499e8a5aa4f7e1f17bbf6b6ee0816828dff3149#7499e8a5aa4f7e1f17bbf6b6ee0816828dff3149"
+source = "git+https://github.com/fakeshadow/xitca-web.git?rev=cce3eee576a9b303a71d4654a6c922402b5fd4f3#cce3eee576a9b303a71d4654a6c922402b5fd4f3"
 dependencies = [
 dependencies = [
- "bytes",
  "futures-core",
  "futures-core",
  "xitca-http",
  "xitca-http",
+ "xitca-io",
  "xitca-server",
  "xitca-server",
  "xitca-service",
  "xitca-service",
 ]
 ]

+ 19 - 14
frameworks/Rust/xitca-web/Cargo.toml

@@ -1,7 +1,7 @@
 [package]
 [package]
 name = "xitca-web"
 name = "xitca-web"
 version = "0.1.0"
 version = "0.1.0"
-edition = "2018"
+edition = "2021"
 
 
 [[bin]]
 [[bin]]
 name = "xitca-web"
 name = "xitca-web"
@@ -12,23 +12,25 @@ name = "xitca-web-diesel"
 path = "./src/main_diesel.rs"
 path = "./src/main_diesel.rs"
 
 
 [dependencies]
 [dependencies]
-xitca-http = { git = "https://github.com/fakeshadow/xitca-web.git", rev = "a470092d8f1e1c3bb7a9831c175bf112b70f81e7" }
-xitca-web = { git = "https://github.com/fakeshadow/xitca-web.git", rev = "a470092d8f1e1c3bb7a9831c175bf112b70f81e7" }
+xitca-http = "0.1"
+xitca-http-codegen = "0.1"
+xitca-server = "0.1"
+xitca-service = "0.1"
+xitca-web = "0.1"
 
 
-ahash = { version = "0.7.4", features = ["compile-time-rng"] }
+ahash = { version = "0.7.6", features = ["compile-time-rng"] }
 atoi = "0.4.0"
 atoi = "0.4.0"
-bytes = "1"
 core_affinity = "0.5.10"
 core_affinity = "0.5.10"
-diesel = { version = "1.4.7", features = ["postgres"] }
-futures-util = { version = "0.3", default-features = false, features = ["alloc"] }
-mimalloc = { version = "0.1.25", default-features = false }
+diesel = { version = "1.4.8", features = ["postgres"] }
+futures-util = { version = "0.3.18", default-features = false, features = ["alloc"] }
+mimalloc = { version = "0.1.27", default-features = false }
 rand = { version = "0.8", default-features = false, features = ["min_const_gen", "small_rng"] }
 rand = { version = "0.8", default-features = false, features = ["min_const_gen", "small_rng"] }
 sailfish = "0.3.3"
 sailfish = "0.3.3"
 serde = "1"
 serde = "1"
-simd-json = "0.4.6"
+simd-json = "0.4.8"
 tang-rs = "0.2"
 tang-rs = "0.2"
-tokio = { version = "1.7", features = ["macros", "rt"] }
-tokio-postgres = "0.7.2"
+tokio = { version = "1.14", features = ["macros", "rt"] }
+tokio-postgres = "0.7.5"
 
 
 [profile.release]
 [profile.release]
 lto = true
 lto = true
@@ -37,6 +39,9 @@ codegen-units = 1
 panic = "abort"
 panic = "abort"
 
 
 [patch.crates-io]
 [patch.crates-io]
-xitca-http = { git = "https://github.com/fakeshadow/xitca-web.git", rev = "a470092d8f1e1c3bb7a9831c175bf112b70f81e7" }
-xitca-server = { git = "https://github.com/fakeshadow/xitca-web.git", rev = "a470092d8f1e1c3bb7a9831c175bf112b70f81e7" }
-xitca-service = { git = "https://github.com/fakeshadow/xitca-web.git", rev = "a470092d8f1e1c3bb7a9831c175bf112b70f81e7" }
+xitca-http = { git = "https://github.com/fakeshadow/xitca-web.git", rev = "958e3c2205e6036c7e0cbf4813d08ddf6029fd1d" }
+xitca-http-codegen = { git = "https://github.com/fakeshadow/xitca-web.git", rev = "958e3c2205e6036c7e0cbf4813d08ddf6029fd1d" }
+xitca-io = { git = "https://github.com/fakeshadow/xitca-web.git", rev = "958e3c2205e6036c7e0cbf4813d08ddf6029fd1d" }
+xitca-server = { git = "https://github.com/fakeshadow/xitca-web.git", rev = "958e3c2205e6036c7e0cbf4813d08ddf6029fd1d" }
+xitca-service = { git = "https://github.com/fakeshadow/xitca-web.git", rev = "958e3c2205e6036c7e0cbf4813d08ddf6029fd1d" }
+xitca-web = { git = "https://github.com/fakeshadow/xitca-web.git", rev = "958e3c2205e6036c7e0cbf4813d08ddf6029fd1d" }

+ 22 - 31
frameworks/Rust/xitca-web/src/db_diesel.rs

@@ -15,14 +15,14 @@ type DbResult<T> = Result<T, Box<dyn Error + Send + Sync + 'static>>;
 pub struct DieselPoolManager(String);
 pub struct DieselPoolManager(String);
 
 
 impl Manager for DieselPoolManager {
 impl Manager for DieselPoolManager {
-    type Connection = PgConnection;
+    type Connection = (PgConnection, SmallRng);
     type Error = DieselPoolError;
     type Error = DieselPoolError;
     type Timeout = Sleep;
     type Timeout = Sleep;
     type TimeoutError = ();
     type TimeoutError = ();
 
 
     fn connect(&self) -> ManagerFuture<Result<Self::Connection, Self::Error>> {
     fn connect(&self) -> ManagerFuture<Result<Self::Connection, Self::Error>> {
         let conn = PgConnection::establish(self.0.as_str());
         let conn = PgConnection::establish(self.0.as_str());
-        Box::pin(async move { Ok(conn?) })
+        Box::pin(async move { Ok((conn?, SmallRng::from_entropy())) })
     }
     }
 
 
     fn is_valid<'a>(
     fn is_valid<'a>(
@@ -86,10 +86,7 @@ impl From<()> for DieselPoolError {
 }
 }
 
 
 #[derive(Clone)]
 #[derive(Clone)]
-pub struct DieselPool {
-    pool: Pool<DieselPoolManager>,
-    rng: SmallRng,
-}
+pub struct DieselPool(Pool<DieselPoolManager>);
 
 
 pub async fn create(config: &str) -> io::Result<DieselPool> {
 pub async fn create(config: &str) -> io::Result<DieselPool> {
     let pool = tang_rs::Builder::new()
     let pool = tang_rs::Builder::new()
@@ -102,24 +99,22 @@ pub async fn create(config: &str) -> io::Result<DieselPool> {
         .await
         .await
         .map_err(|e| io::Error::new(io::ErrorKind::Other, e))?;
         .map_err(|e| io::Error::new(io::ErrorKind::Other, e))?;
 
 
-    Ok(DieselPool {
-        pool,
-        rng: SmallRng::from_entropy(),
-    })
+    Ok(DieselPool(pool))
 }
 }
 
 
 impl DieselPool {
 impl DieselPool {
     pub async fn get_world(&self) -> DbResult<World> {
     pub async fn get_world(&self) -> DbResult<World> {
-        let mut rng = self.rng.clone();
-        let conn = self.pool.get_owned().await?;
+        let mut conn = self.0.get_owned().await?;
 
 
         spawn_blocking(move || {
         spawn_blocking(move || {
             use crate::schema::world::dsl::*;
             use crate::schema::world::dsl::*;
 
 
+            let (c, rng) = &mut *conn;
+
             let random_id = rng.gen_range(1..10_001);
             let random_id = rng.gen_range(1..10_001);
             let w = world
             let w = world
                 .filter(id.eq(random_id))
                 .filter(id.eq(random_id))
-                .load::<World>(&*conn)?
+                .load::<World>(c)?
                 .pop()
                 .pop()
                 .unwrap();
                 .unwrap();
 
 
@@ -129,20 +124,17 @@ impl DieselPool {
     }
     }
 
 
     pub async fn get_worlds(&self, num: u16) -> DbResult<Vec<World>> {
     pub async fn get_worlds(&self, num: u16) -> DbResult<Vec<World>> {
-        let mut rng = self.rng.clone();
-        let conn = self.pool.get_owned().await?;
+        let mut conn = self.0.get_owned().await?;
 
 
         spawn_blocking(move || {
         spawn_blocking(move || {
             use crate::schema::world::dsl::*;
             use crate::schema::world::dsl::*;
 
 
+            let (c, rng) = &mut *conn;
+
             (0..num)
             (0..num)
                 .map(|_| {
                 .map(|_| {
                     let w_id = rng.gen_range(1..10_001);
                     let w_id = rng.gen_range(1..10_001);
-                    let w = world
-                        .filter(id.eq(w_id))
-                        .load::<World>(&*conn)?
-                        .pop()
-                        .unwrap();
+                    let w = world.filter(id.eq(w_id)).load::<World>(c)?.pop().unwrap();
                     Ok(w)
                     Ok(w)
                 })
                 })
                 .collect()
                 .collect()
@@ -151,20 +143,17 @@ impl DieselPool {
     }
     }
 
 
     pub async fn update(&self, num: u16) -> DbResult<Vec<World>> {
     pub async fn update(&self, num: u16) -> DbResult<Vec<World>> {
-        let mut rng = self.rng.clone();
-        let conn = self.pool.get_owned().await?;
+        let mut conn = self.0.get_owned().await?;
 
 
         spawn_blocking(move || {
         spawn_blocking(move || {
             use crate::schema::world::dsl::*;
             use crate::schema::world::dsl::*;
 
 
+            let (c, rng) = &mut *conn;
+
             let mut worlds = (0..num)
             let mut worlds = (0..num)
                 .map(|_| {
                 .map(|_| {
                     let w_id: i32 = rng.gen_range(1..10_001);
                     let w_id: i32 = rng.gen_range(1..10_001);
-                    let mut w = world
-                        .filter(id.eq(w_id))
-                        .load::<World>(&*conn)?
-                        .pop()
-                        .unwrap();
+                    let mut w = world.filter(id.eq(w_id)).load::<World>(c)?.pop().unwrap();
                     w.randomnumber = rng.gen_range(1..10_001);
                     w.randomnumber = rng.gen_range(1..10_001);
                     Ok(w)
                     Ok(w)
                 })
                 })
@@ -172,12 +161,12 @@ impl DieselPool {
 
 
             worlds.sort_by_key(|w| w.id);
             worlds.sort_by_key(|w| w.id);
 
 
-            conn.transaction::<_, diesel::result::Error, _>(|| {
+            c.transaction::<_, diesel::result::Error, _>(|| {
                 for w in &worlds {
                 for w in &worlds {
                     diesel::update(world)
                     diesel::update(world)
                         .filter(id.eq(w.id))
                         .filter(id.eq(w.id))
                         .set(randomnumber.eq(w.randomnumber))
                         .set(randomnumber.eq(w.randomnumber))
-                        .execute(&*conn)?;
+                        .execute(c)?;
                 }
                 }
                 Ok(())
                 Ok(())
             })?;
             })?;
@@ -188,12 +177,14 @@ impl DieselPool {
     }
     }
 
 
     pub async fn tell_fortune(&self) -> DbResult<Fortunes> {
     pub async fn tell_fortune(&self) -> DbResult<Fortunes> {
-        let conn = self.pool.get_owned().await?;
+        let mut conn = self.0.get_owned().await?;
 
 
         spawn_blocking(move || {
         spawn_blocking(move || {
             use crate::schema::fortune::dsl::*;
             use crate::schema::fortune::dsl::*;
 
 
-            let mut items = fortune.load::<Fortune>(&*conn)?;
+            let (c, _) = &mut *conn;
+
+            let mut items = fortune.load::<Fortune>(c)?;
 
 
             items.push(Fortune::new(0, "Additional fortune added at request time."));
             items.push(Fortune::new(0, "Additional fortune added at request time."));
             items.sort_by(|it, next| it.message.cmp(&next.message));
             items.sort_by(|it, next| it.message.cmp(&next.message));

+ 139 - 69
frameworks/Rust/xitca-web/src/main.rs

@@ -1,3 +1,5 @@
+#![feature(generic_associated_types, type_alias_impl_trait)]
+
 #[global_allocator]
 #[global_allocator]
 static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc;
 static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc;
 
 
@@ -6,110 +8,178 @@ mod ser;
 mod util;
 mod util;
 
 
 use std::{
 use std::{
+    convert::Infallible,
     error::Error,
     error::Error,
     future::ready,
     future::ready,
     io,
     io,
     sync::{Arc, Mutex},
     sync::{Arc, Mutex},
 };
 };
 
 
-use bytes::Bytes;
-use xitca_http::http::{
-    header::{CONTENT_TYPE, SERVER},
-    Method,
+use serde::Serialize;
+use xitca_http::{
+    body::ResponseBody,
+    bytes::Bytes,
+    config::HttpServiceConfig,
+    h1::RequestBody,
+    http::{
+        self,
+        header::{CONTENT_TYPE, SERVER},
+        IntoResponse,
+    },
+    util::service::get,
+    HttpServiceBuilder,
 };
 };
-use xitca_web::{dev::fn_service, request::WebRequest, App, HttpServer};
+use xitca_server::Builder;
 
 
 use self::db::Client;
 use self::db::Client;
+use self::ser::Message;
 use self::util::{
 use self::util::{
-    internal, json, json_response, not_found, plain_text, AppState, HandleResult, QueryParse,
+    internal, not_found, AppState, QueryParse, JSON_HEADER_VALUE, SERVER_HEADER_VALUE,
+    TEXT_HEADER_VALUE,
 };
 };
 
 
-type State = AppState<Client>;
+type Request = http::Request<RequestBody>;
+
+type Response = http::Response<ResponseBody>;
 
 
 #[tokio::main(flavor = "current_thread")]
 #[tokio::main(flavor = "current_thread")]
 async fn main() -> io::Result<()> {
 async fn main() -> io::Result<()> {
-    let config = "postgres://benchmarkdbuser:benchmarkdbpass@tfb-database/hello_world";
-
     let cores = core_affinity::get_core_ids().unwrap_or_else(Vec::new);
     let cores = core_affinity::get_core_ids().unwrap_or_else(Vec::new);
     let cores = Arc::new(Mutex::new(cores));
     let cores = Arc::new(Mutex::new(cores));
 
 
-    HttpServer::new(move || {
-        App::with_async_state(move || async move {
-            let client = db::create(config).await;
-            AppState::new(client)
+    let factory = || {
+        let http = Http {
+            config: "postgres://benchmarkdbuser:benchmarkdbpass@tfb-database/hello_world",
+        };
+
+        let config = HttpServiceConfig::new()
+            .disable_vectored_write()
+            .max_request_headers::<8>();
+
+        HttpServiceBuilder::h1(get(http)).config(config)
+    };
+
+    Builder::new()
+        .on_worker_start(move || {
+            if let Some(core) = cores.lock().unwrap().pop() {
+                core_affinity::set_for_current(core);
+            }
+            ready(())
         })
         })
-        .service(fn_service(handle))
-    })
-    .force_flat_buf()
-    .max_request_headers::<8>()
-    .on_worker_start(move || {
-        if let Some(core) = cores.lock().unwrap().pop() {
-            core_affinity::set_for_current(core);
-        }
-        ready(())
-    })
-    .bind("0.0.0.0:8080")?
-    .run()
-    .await
+        .bind("xitca-web", "0.0.0.0:8080", factory)?
+        .build()
+        .await
 }
 }
 
 
-async fn handle(req: &mut WebRequest<'_, State>) -> HandleResult {
-    let inner = req.request_mut();
-
-    match (inner.method(), inner.uri().path()) {
-        (&Method::GET, "/plaintext") => plain_text(req),
-        (&Method::GET, "/json") => json(req),
-        (&Method::GET, "/db") => db(req).await,
-        (&Method::GET, "/fortunes") => fortunes(req).await,
-        (&Method::GET, "/queries") => queries(req).await,
-        (&Method::GET, "/updates") => updates(req).await,
-        _ => not_found(),
-    }
+#[derive(Clone)]
+struct Http {
+    config: &'static str,
 }
 }
 
 
-async fn db(req: &mut WebRequest<'_, State>) -> HandleResult {
-    match req.state().client().get_world().await {
-        Ok(ref world) => json_response(req, world),
-        Err(_) => internal(),
-    }
+struct HttpService {
+    state: AppState<Client>,
 }
 }
 
 
-async fn fortunes(req: &mut WebRequest<'_, State>) -> HandleResult {
-    match _fortunes(req.state().client()).await {
-        Ok(body) => {
-            let mut res = req.as_response(body);
+#[xitca_http_codegen::service_impl]
+impl HttpService {
+    async fn new_service(http: &Http, _: ()) -> Result<Self, ()> {
+        let client = db::create(http.config).await;
 
 
-            res.headers_mut().append(SERVER, util::SERVER_HEADER_VALUE);
-            res.headers_mut()
-                .append(CONTENT_TYPE, util::HTML_HEADER_VALUE);
+        Ok(HttpService {
+            state: AppState::new(client),
+        })
+    }
+
+    async fn ready(&self) -> Result<(), Infallible> {
+        Ok(())
+    }
 
 
-            Ok(res)
+    async fn call(&self, req: Request) -> Result<Response, Infallible> {
+        match req.uri().path() {
+            "/plaintext" => self.plain_text(req),
+            "/json" => self.json(req),
+            "/db" => self.db(req).await,
+            "/fortunes" => self.fortunes(req).await,
+            "/queries" => self.queries(req).await,
+            "/updates" => self.updates(req).await,
+            _ => not_found(),
         }
         }
-        Err(_) => internal(),
     }
     }
 }
 }
 
 
-async fn queries(req: &mut WebRequest<'_, State>) -> HandleResult {
-    let num = req.request_mut().uri().query().parse_query();
+impl HttpService {
+    fn plain_text(&self, req: Request) -> Result<Response, Infallible> {
+        let mut res = req.into_response("Hello, World!");
 
 
-    match req.state().client().get_worlds(num).await {
-        Ok(worlds) => json_response(req, worlds.as_slice()),
-        Err(_) => internal(),
+        res.headers_mut().append(SERVER, SERVER_HEADER_VALUE);
+        res.headers_mut().append(CONTENT_TYPE, TEXT_HEADER_VALUE);
+
+        Ok(res)
+    }
+
+    #[inline]
+    fn json(&self, req: Request) -> Result<Response, Infallible> {
+        self._json(req, &Message::new())
     }
     }
-}
 
 
-async fn updates(req: &mut WebRequest<'_, State>) -> HandleResult {
-    let num = req.request_mut().uri().query().parse_query();
+    async fn db(&self, req: Request) -> Result<Response, Infallible> {
+        match self.state.client().get_world().await {
+            Ok(ref world) => self._json(req, world),
+            Err(_) => internal(),
+        }
+    }
+
+    async fn fortunes(&self, req: Request) -> Result<Response, Infallible> {
+        match self._fortunes().await {
+            Ok(body) => {
+                let mut res = req.into_response(body);
 
 
-    match req.state().client().update(num).await {
-        Ok(worlds) => json_response(req, worlds.as_slice()),
-        Err(_) => internal(),
+                res.headers_mut().append(SERVER, util::SERVER_HEADER_VALUE);
+                res.headers_mut()
+                    .append(CONTENT_TYPE, util::HTML_HEADER_VALUE);
+
+                Ok(res)
+            }
+            Err(_) => internal(),
+        }
+    }
+
+    async fn queries(&self, req: Request) -> Result<Response, Infallible> {
+        let num = req.uri().query().parse_query();
+        match self.state.client().get_worlds(num).await {
+            Ok(worlds) => self._json(req, worlds.as_slice()),
+            Err(_) => internal(),
+        }
     }
     }
-}
 
 
-#[inline]
-async fn _fortunes(client: &Client) -> Result<Bytes, Box<dyn Error>> {
-    use sailfish::TemplateOnce;
-    let fortunes = client.tell_fortune().await?.render_once()?;
-    Ok(fortunes.into())
+    async fn updates(&self, req: Request) -> Result<Response, Infallible> {
+        let num = req.uri().query().parse_query();
+        match self.state.client().update(num).await {
+            Ok(worlds) => self._json(req, worlds.as_slice()),
+            Err(_) => internal(),
+        }
+    }
+
+    #[inline]
+    async fn _fortunes(&self) -> Result<Bytes, Box<dyn Error>> {
+        use sailfish::TemplateOnce;
+        let fortunes = self.state.client().tell_fortune().await?.render_once()?;
+        Ok(fortunes.into())
+    }
+
+    #[inline]
+    fn _json<S>(&self, req: Request, value: &S) -> Result<Response, Infallible>
+    where
+        S: ?Sized + Serialize,
+    {
+        let mut writer = self.state.writer();
+        simd_json::to_writer(&mut writer, value).unwrap();
+        let body = writer.take();
+
+        let mut res = req.into_response(body);
+        res.headers_mut().append(SERVER, SERVER_HEADER_VALUE);
+        res.headers_mut().append(CONTENT_TYPE, JSON_HEADER_VALUE);
+
+        Ok(res)
+    }
 }
 }

+ 46 - 10
frameworks/Rust/xitca-web/src/main_diesel.rs

@@ -11,16 +11,22 @@ mod util;
 
 
 use std::{error::Error, io};
 use std::{error::Error, io};
 
 
-use bytes::Bytes;
-use xitca_http::http::{
-    header::{CONTENT_TYPE, SERVER},
-    Method,
+use serde::Serialize;
+use xitca_web::{
+    dev::{bytes::Bytes, fn_service},
+    http::{
+        header::{CONTENT_TYPE, SERVER},
+        Method,
+    },
+    request::WebRequest,
+    App, HttpServer,
 };
 };
-use xitca_web::{dev::fn_service, request::WebRequest, App, HttpServer};
 
 
 use self::db_diesel::{create, DieselPool};
 use self::db_diesel::{create, DieselPool};
+use self::ser::Message;
 use self::util::{
 use self::util::{
-    internal, json, json_response, not_found, plain_text, AppState, HandleResult, QueryParse,
+    internal, not_found, AppState, HandleResult, QueryParse, JSON_HEADER_VALUE,
+    SERVER_HEADER_VALUE, TEXT_HEADER_VALUE,
 };
 };
 
 
 type State = AppState<DieselPool>;
 type State = AppState<DieselPool>;
@@ -36,7 +42,7 @@ async fn main() -> io::Result<()> {
         })
         })
         .service(fn_service(handle))
         .service(fn_service(handle))
     })
     })
-    .force_flat_buf()
+    .disable_vectored_write()
     .max_request_headers::<8>()
     .max_request_headers::<8>()
     .bind("0.0.0.0:8080")?
     .bind("0.0.0.0:8080")?
     .run()
     .run()
@@ -59,7 +65,7 @@ async fn handle(req: &mut WebRequest<'_, State>) -> HandleResult {
 
 
 async fn db(req: &mut WebRequest<'_, State>) -> HandleResult {
 async fn db(req: &mut WebRequest<'_, State>) -> HandleResult {
     match req.state().client().get_world().await {
     match req.state().client().get_world().await {
-        Ok(world) => json_response(req, &world),
+        Ok(world) => _json(req, &world),
         Err(_) => internal(),
         Err(_) => internal(),
     }
     }
 }
 }
@@ -83,7 +89,7 @@ async fn queries(req: &mut WebRequest<'_, State>) -> HandleResult {
     let num = req.request_mut().uri().query().parse_query();
     let num = req.request_mut().uri().query().parse_query();
 
 
     match req.state().client().get_worlds(num).await {
     match req.state().client().get_worlds(num).await {
-        Ok(worlds) => json_response(req, worlds.as_slice()),
+        Ok(worlds) => _json(req, worlds.as_slice()),
         Err(_) => internal(),
         Err(_) => internal(),
     }
     }
 }
 }
@@ -92,7 +98,7 @@ async fn updates(req: &mut WebRequest<'_, State>) -> HandleResult {
     let num = req.request_mut().uri().query().parse_query();
     let num = req.request_mut().uri().query().parse_query();
 
 
     match req.state().client().update(num).await {
     match req.state().client().update(num).await {
-        Ok(worlds) => json_response(req, worlds.as_slice()),
+        Ok(worlds) => _json(req, worlds.as_slice()),
         Err(_) => internal(),
         Err(_) => internal(),
     }
     }
 }
 }
@@ -103,3 +109,33 @@ async fn _fortunes(pool: &DieselPool) -> Result<Bytes, Box<dyn Error + Send + Sy
     let fortunes = pool.tell_fortune().await?.render_once()?;
     let fortunes = pool.tell_fortune().await?.render_once()?;
     Ok(fortunes.into())
     Ok(fortunes.into())
 }
 }
+
+fn plain_text<D>(req: &mut WebRequest<'_, D>) -> HandleResult {
+    let mut res = req.as_response(Bytes::from_static(b"Hello, World!"));
+
+    res.headers_mut().append(SERVER, SERVER_HEADER_VALUE);
+    res.headers_mut().append(CONTENT_TYPE, TEXT_HEADER_VALUE);
+
+    Ok(res)
+}
+
+#[inline(always)]
+fn json<D>(req: &mut WebRequest<'_, AppState<D>>) -> HandleResult {
+    _json(req, &Message::new())
+}
+
+#[inline]
+fn _json<S, D>(req: &mut WebRequest<'_, AppState<D>>, value: &S) -> HandleResult
+where
+    S: ?Sized + Serialize,
+{
+    let mut writer = req.state().writer();
+    simd_json::to_writer(&mut writer, value).unwrap();
+    let body = writer.take();
+
+    let mut res = req.as_response(body);
+    res.headers_mut().append(SERVER, SERVER_HEADER_VALUE);
+    res.headers_mut().append(CONTENT_TYPE, JSON_HEADER_VALUE);
+
+    Ok(res)
+}

+ 9 - 41
frameworks/Rust/xitca-web/src/util.rs

@@ -1,3 +1,5 @@
+#![allow(clippy::declare_interior_mutable_const)]
+
 use std::{
 use std::{
     cell::{RefCell, RefMut},
     cell::{RefCell, RefMut},
     cmp,
     cmp,
@@ -5,19 +7,15 @@ use std::{
     io,
     io,
 };
 };
 
 
-use bytes::{Bytes, BytesMut};
-use serde::Serialize;
-use xitca_http::http::{
-    header::{HeaderValue, CONTENT_TYPE, SERVER},
-    StatusCode,
-};
 use xitca_web::{
 use xitca_web::{
-    request::WebRequest,
+    dev::bytes::{Bytes, BytesMut},
+    http::{
+        header::{HeaderValue, SERVER},
+        StatusCode,
+    },
     response::{WebResponse, WebResponseBuilder},
     response::{WebResponse, WebResponseBuilder},
 };
 };
 
 
-use super::ser::Message;
-
 pub(super) type HandleResult = Result<WebResponse, Infallible>;
 pub(super) type HandleResult = Result<WebResponse, Infallible>;
 
 
 pub(super) struct Writer<'a>(RefMut<'a, BytesMut>);
 pub(super) struct Writer<'a>(RefMut<'a, BytesMut>);
@@ -87,39 +85,9 @@ pub const SERVER_HEADER_VALUE: HeaderValue = HeaderValue::from_static("TFB");
 
 
 pub const HTML_HEADER_VALUE: HeaderValue = HeaderValue::from_static("text/html; charset=utf-8");
 pub const HTML_HEADER_VALUE: HeaderValue = HeaderValue::from_static("text/html; charset=utf-8");
 
 
-const TEXT_HEADER_VALUE: HeaderValue = HeaderValue::from_static("text/plain");
-
-const JSON_HEADER_VALUE: HeaderValue = HeaderValue::from_static("application/json");
+pub const TEXT_HEADER_VALUE: HeaderValue = HeaderValue::from_static("text/plain");
 
 
-pub(super) fn plain_text<D>(req: &mut WebRequest<'_, D>) -> HandleResult {
-    let mut res = req.as_response(Bytes::from_static(b"Hello, World!"));
-
-    res.headers_mut().append(SERVER, SERVER_HEADER_VALUE);
-    res.headers_mut().append(CONTENT_TYPE, TEXT_HEADER_VALUE);
-
-    Ok(res)
-}
-
-#[inline(always)]
-pub(super) fn json<D>(req: &mut WebRequest<'_, AppState<D>>) -> HandleResult {
-    json_response(req, &Message::new())
-}
-
-#[inline]
-pub(super) fn json_response<S, D>(req: &mut WebRequest<'_, AppState<D>>, value: &S) -> HandleResult
-where
-    S: ?Sized + Serialize,
-{
-    let mut writer = req.state().writer();
-    simd_json::to_writer(&mut writer, value).unwrap();
-    let body = writer.take();
-
-    let mut res = req.as_response(body);
-    res.headers_mut().append(SERVER, SERVER_HEADER_VALUE);
-    res.headers_mut().append(CONTENT_TYPE, JSON_HEADER_VALUE);
-
-    Ok(res)
-}
+pub const JSON_HEADER_VALUE: HeaderValue = HeaderValue::from_static("application/json");
 
 
 macro_rules! error {
 macro_rules! error {
     ($error: ident, $code: path) => {
     ($error: ident, $code: path) => {

+ 2 - 2
frameworks/Rust/xitca-web/xitca-web-diesel.dockerfile

@@ -1,10 +1,10 @@
-FROM rust:1.53
+FROM rust:1.56
 
 
 ADD ./ /xitca-web
 ADD ./ /xitca-web
 WORKDIR /xitca-web
 WORKDIR /xitca-web
 
 
+RUN rustup default nightly-2021-11-27
 RUN cargo clean
 RUN cargo clean
-RUN rustup default nightly-2021-08-03
 RUN RUSTFLAGS="-C target-cpu=native" cargo build --release --bin xitca-web-diesel
 RUN RUSTFLAGS="-C target-cpu=native" cargo build --release --bin xitca-web-diesel
 
 
 EXPOSE 8080
 EXPOSE 8080

+ 2 - 2
frameworks/Rust/xitca-web/xitca-web.dockerfile

@@ -1,10 +1,10 @@
-FROM rust:1.53
+FROM rust:1.56
 
 
 ADD ./ /xitca-web
 ADD ./ /xitca-web
 WORKDIR /xitca-web
 WORKDIR /xitca-web
 
 
+RUN rustup default nightly-2021-11-27
 RUN cargo clean
 RUN cargo clean
-RUN rustup default nightly-2021-08-03
 RUN RUSTFLAGS="-C target-cpu=native" cargo build --release --bin xitca-web
 RUN RUSTFLAGS="-C target-cpu=native" cargo build --release --bin xitca-web
 
 
 EXPOSE 8080
 EXPOSE 8080

+ 3 - 3
frameworks/Scala/http4s/README.md

@@ -3,6 +3,6 @@
 ## Infrastructure Software Versions
 ## Infrastructure Software Versions
 
 
 The tests were run with:
 The tests were run with:
-* [OpenJDK](https://hub.docker.com/_/openjdk) 8 for building and the latest alpine for running, as per the [Scala JDK Compatibility page](https://docs.scala-lang.org/overviews/jdk-compatibility/overview.html)
-* [http4s 0.21.3](http://http4s.org/)
-* [doobie 0.8.8](https://tpolecat.github.io/doobie/)
+* [OpenJDK](https://hub.docker.com/_/openjdk) 17, the latest LTS release, as per the [Scala JDK Compatibility page](https://docs.scala-lang.org/overviews/jdk-compatibility/overview.html)
+* [http4s 0.23.6](http://http4s.org/)
+* [cats-effets 3.3.0](https://typelevel.org/cats-effect/)

+ 6 - 5
frameworks/Scala/http4s/build.sbt → frameworks/Scala/http4s/blaze/build.sbt

@@ -2,7 +2,7 @@ name := "http4s"
 
 
 version := "1.0"
 version := "1.0"
 
 
-scalaVersion := "2.13.6"
+scalaVersion := "2.13.7"
 
 
 scalacOptions ++= Seq(
 scalacOptions ++= Seq(
   "-deprecation",
   "-deprecation",
@@ -13,12 +13,13 @@ scalacOptions ++= Seq(
   "-language:reflectiveCalls",
   "-language:reflectiveCalls",
   "-Ywarn-numeric-widen",
   "-Ywarn-numeric-widen",
   "-target:11",
   "-target:11",
+  "-Xlint:-byname-implicit",
   "-Xlint"
   "-Xlint"
 )
 )
 
 
 enablePlugins(SbtTwirl)
 enablePlugins(SbtTwirl)
 
 
-val http4sVersion = "0.23.1"
+val http4sVersion = "0.23.6"
 
 
 assembly / assemblyMergeStrategy := {
 assembly / assemblyMergeStrategy := {
   case PathList(xs @ _*) if xs.last == "io.netty.versions.properties" => MergeStrategy.rename
   case PathList(xs @ _*) if xs.last == "io.netty.versions.properties" => MergeStrategy.rename
@@ -32,9 +33,9 @@ libraryDependencies ++= Seq(
   "org.http4s" %% "http4s-circe" % http4sVersion,
   "org.http4s" %% "http4s-circe" % http4sVersion,
   // Optional for auto-derivation of JSON codecs
   // Optional for auto-derivation of JSON codecs
   "io.circe" %% "circe-generic" % "0.14.1",
   "io.circe" %% "circe-generic" % "0.14.1",
-  "org.typelevel" %% "cats-effect" % "3.2.2",
-  "co.fs2" %% "fs2-core" % "3.1.0",
-  "co.fs2" %% "fs2-io" % "3.1.0",
+  "org.typelevel" %% "cats-effect" % "3.3.0",
+  "co.fs2" %% "fs2-core" % "3.2.2",
+  "co.fs2" %% "fs2-io" % "3.2.2",
   "io.getquill" %% "quill-jasync-postgres" % "3.9.0",
   "io.getquill" %% "quill-jasync-postgres" % "3.9.0",
   "io.getquill" %% "quill-jasync" % "3.9.0",
   "io.getquill" %% "quill-jasync" % "3.9.0",
   "ch.qos.logback" % "logback-classic" % "1.2.5"
   "ch.qos.logback" % "logback-classic" % "1.2.5"

+ 0 - 0
frameworks/Scala/http4s/project/build.properties → frameworks/Scala/http4s/blaze/project/build.properties


+ 0 - 0
frameworks/Scala/http4s/project/plugins.sbt → frameworks/Scala/http4s/blaze/project/plugins.sbt


+ 0 - 0
frameworks/Scala/http4s/src/main/resources/application.properties → frameworks/Scala/http4s/blaze/src/main/resources/application.properties


+ 0 - 0
frameworks/Scala/http4s/src/main/resources/logback.xml → frameworks/Scala/http4s/blaze/src/main/resources/logback.xml


+ 0 - 0
frameworks/Scala/http4s/src/main/scala/http4s/techempower/benchmark/DatabaseService.scala → frameworks/Scala/http4s/blaze/src/main/scala/http4s/techempower/benchmark/DatabaseService.scala


+ 2 - 6
frameworks/Scala/http4s/src/main/scala/http4s/techempower/benchmark/WebServer.scala → frameworks/Scala/http4s/blaze/src/main/scala/http4s/techempower/benchmark/WebServer.scala

@@ -1,7 +1,6 @@
 package http4s.techempower.benchmark
 package http4s.techempower.benchmark
 
 
 import java.util.concurrent.Executors
 import java.util.concurrent.Executors
-import scala.concurrent.ExecutionContext
 import cats.effect.{ExitCode, IO, IOApp, Resource}
 import cats.effect.{ExitCode, IO, IOApp, Resource}
 import com.typesafe.config.ConfigValueFactory
 import com.typesafe.config.ConfigValueFactory
 import io.circe.generic.auto._
 import io.circe.generic.auto._
@@ -13,7 +12,6 @@ import org.http4s._
 import org.http4s.dsl._
 import org.http4s.dsl._
 import org.http4s.circe._
 import org.http4s.circe._
 import org.http4s.implicits._
 import org.http4s.implicits._
-import org.http4s.server.Router
 import org.http4s.blaze.server.BlazeServerBuilder
 import org.http4s.blaze.server.BlazeServerBuilder
 import org.http4s.headers.Server
 import org.http4s.headers.Server
 import org.http4s.twirl._
 import org.http4s.twirl._
@@ -97,13 +95,11 @@ object WebServer extends IOApp with Http4sDsl[IO] {
         } yield newWorlds.asJson)
         } yield newWorlds.asJson)
     })
     })
 
 
-  val blazeEc = ExecutionContext.fromExecutor(Executors.newFixedThreadPool(32))
-
   // Given a fully constructed HttpService, start the server and wait for completion
   // Given a fully constructed HttpService, start the server and wait for completion
   def startServer(service: HttpRoutes[IO]) =
   def startServer(service: HttpRoutes[IO]) =
-    BlazeServerBuilder[IO](blazeEc)
+    BlazeServerBuilder[IO]
       .bindHttp(8080, "0.0.0.0")
       .bindHttp(8080, "0.0.0.0")
-      .withHttpApp(Router("/" -> service).orNotFound)
+      .withHttpApp(service.orNotFound)
       .withSocketKeepAlive(true)
       .withSocketKeepAlive(true)
       .resource
       .resource
 
 

+ 0 - 0
frameworks/Scala/http4s/src/main/twirl/index.scala.html → frameworks/Scala/http4s/blaze/src/main/twirl/index.scala.html


+ 11 - 5
frameworks/Scala/http4s/http4s.dockerfile

@@ -1,8 +1,8 @@
-FROM openjdk:15 AS builder
+FROM openjdk:17 AS builder
 WORKDIR /http4s
 WORKDIR /http4s
-COPY project project
-COPY src src
-COPY build.sbt build.sbt
+COPY blaze/project project
+COPY blaze/src src
+COPY blaze/build.sbt build.sbt
 COPY sbt sbt
 COPY sbt sbt
 RUN ./sbt assembly -batch && \
 RUN ./sbt assembly -batch && \
     mv target/scala-2.13/http4s-assembly-1.0.jar . && \
     mv target/scala-2.13/http4s-assembly-1.0.jar . && \
@@ -11,7 +11,8 @@ RUN ./sbt assembly -batch && \
     rm -Rf ~/.sbt && \
     rm -Rf ~/.sbt && \
     rm -Rf ~/.ivy2 && \
     rm -Rf ~/.ivy2 && \
     rm -Rf /var/cache
     rm -Rf /var/cache
-FROM openjdk:15
+    
+FROM openjdk:17
 WORKDIR /http4s
 WORKDIR /http4s
 COPY --from=builder /http4s/http4s-assembly-1.0.jar /http4s/http4s-assembly-1.0.jar
 COPY --from=builder /http4s/http4s-assembly-1.0.jar /http4s/http4s-assembly-1.0.jar
 
 
@@ -21,6 +22,11 @@ CMD java \
       -server \
       -server \
       -Xms2g \
       -Xms2g \
       -Xmx2g \
       -Xmx2g \
+      -XX:NewSize=1g \
+      -XX:MaxNewSize=1g \
+      -XX:InitialCodeCacheSize=256m \
+      -XX:ReservedCodeCacheSize=256m \
+      -XX:+UseParallelGC \
       -XX:+AlwaysPreTouch \
       -XX:+AlwaysPreTouch \
       -Dcats.effect.stackTracingMode=disabled \
       -Dcats.effect.stackTracingMode=disabled \
       -jar \
       -jar \

+ 5 - 1
frameworks/Swift/vapor/README.md

@@ -31,4 +31,8 @@ http://localhost:8080/db
 
 
 ### Multiple database queries test
 ### Multiple database queries test
 
 
-http://localhost:8080/queries/[1...500]
+http://localhost:8080/queries?queries=[1...500]
+
+### Database updates test
+
+http://localhost:8080/updates?queries=[1...500]

+ 38 - 0
frameworks/Swift/vapor/benchmark_config.json

@@ -59,6 +59,7 @@
     "postgres": {
     "postgres": {
       "db_url": "/db",
       "db_url": "/db",
       "query_url": "/queries?queries=",
       "query_url": "/queries?queries=",
+      "update_url": "/updates?queries=",
       "port": 8080,
       "port": 8080,
       "approach": "Realistic",
       "approach": "Realistic",
       "classification": "Fullstack",
       "classification": "Fullstack",
@@ -74,6 +75,43 @@
       "display_name": "Vapor",
       "display_name": "Vapor",
       "notes": "",
       "notes": "",
       "versus": "None"
       "versus": "None"
+    },
+    "mongo-fluent": {
+      "db_url": "/db",
+      "query_url": "/queries?queries=",
+      "port": 8080,
+      "approach": "Realistic",
+      "classification": "Fullstack",
+      "database": "MongoDb",
+      "framework": "Vapor",
+      "language": "Swift",
+      "flavor": "None",
+      "orm": "Full",
+      "platform": "None",
+      "webserver": "None",
+      "os": "Linux",
+      "database_os": "Linux",
+      "display_name": "Vapor",
+      "notes": "",
+      "versus": "None"
+    },
+    "mongo": {
+      "db_url": "/db",
+      "port": 8080,
+      "approach": "Realistic",
+      "classification": "Fullstack",
+      "database": "MongoDb",
+      "framework": "Vapor",
+      "language": "Swift",
+      "flavor": "None",
+      "orm": "Raw",
+      "platform": "None",
+      "webserver": "None",
+      "os": "Linux",
+      "database_os": "Linux",
+      "display_name": "Vapor",
+      "notes": "",
+      "versus": "None"
     }
     }
   }]
   }]
 }
 }

+ 1 - 0
frameworks/Swift/vapor/config.toml

@@ -28,6 +28,7 @@ versus = "None"
 [postgres]
 [postgres]
 urls.db = "/db"
 urls.db = "/db"
 urls.query = "/queries?queries="
 urls.query = "/queries?queries="
+urls.update = "/updates?queries="
 approach = "Realistic"
 approach = "Realistic"
 classification = "Fullstack"
 classification = "Fullstack"
 database = "Postgres"
 database = "Postgres"

برخی فایل ها در این مقایسه diff نمایش داده نمی شوند زیرا تعداد فایل ها بسیار زیاد است