瀏覽代碼

Merge pull request #29 from TechEmpower/master

aa
三刀 2 年之前
父節點
當前提交
30e31090d2
共有 80 個文件被更改,包括 1121 次插入589 次删除
  1. 0 2
      frameworks/C++/cppcms/benchmark_config.json
  2. 0 4
      frameworks/C++/lithium/benchmark_config.json
  3. 18 2
      frameworks/C/h2o/h2o.dockerfile
  4. 30 14
      frameworks/C/h2o/src/database.c
  5. 1 4
      frameworks/Haskell/ihp/ihp.dockerfile
  6. 1 1
      frameworks/Haskell/ihp/src/default.nix
  7. 1 1
      frameworks/Java/micronaut/gradle.properties
  8. 1 1
      frameworks/PHP/mark/composer.json
  9. 1 0
      frameworks/PHP/mark/mark.dockerfile
  10. 3 1
      frameworks/PHP/mark/start.php
  11. 3 3
      frameworks/PHP/php-ngx/php-ngx-async.dockerfile
  12. 2 2
      frameworks/PHP/php-ngx/php-ngx-mysql.dockerfile
  13. 3 3
      frameworks/PHP/php-ngx/php-ngx-pgsql.dockerfile
  14. 3 3
      frameworks/PHP/php-ngx/php-ngx.dockerfile
  15. 2 0
      frameworks/PHP/spiral/.env
  16. 1 1
      frameworks/PHP/spiral/.rr.yaml
  17. 3 3
      frameworks/PHP/spiral/README.md
  18. 14 15
      frameworks/PHP/spiral/app.php
  19. 10 18
      frameworks/PHP/spiral/app/src/App.php
  20. 0 37
      frameworks/PHP/spiral/app/src/Bootloader/DebugBootloader.php
  21. 19 20
      frameworks/PHP/spiral/app/src/Bootloader/RoutesBootloader.php
  22. 20 66
      frameworks/PHP/spiral/app/src/Controller/BenchmarkController.php
  23. 5 13
      frameworks/PHP/spiral/app/src/Model/Fortune.php
  24. 3 9
      frameworks/PHP/spiral/app/src/Model/Repository/FortuneRepository.php
  25. 4 10
      frameworks/PHP/spiral/app/src/Model/Repository/WorldRepository.php
  26. 5 13
      frameworks/PHP/spiral/app/src/Model/World.php
  27. 7 6
      frameworks/PHP/spiral/composer.json
  28. 1 1
      frameworks/PHP/spiral/spiral.dockerfile
  29. 24 2
      frameworks/Pascal/mormot/benchmark_config.json
  30. 1 1
      frameworks/Pascal/mormot/setup_and_build.sh
  31. 260 124
      frameworks/Pascal/mormot/src/raw.pas
  32. 3 0
      frameworks/Python/falcon/app.py
  33. 0 2
      frameworks/Python/falcon/benchmark_config.json
  34. 0 2
      frameworks/Python/falcon/config.toml
  35. 7 5
      frameworks/Python/falcon/falcon-asgi.dockerfile
  36. 4 1
      frameworks/Python/falcon/falcon-bjoern.dockerfile
  37. 5 2
      frameworks/Python/falcon/falcon-fastwsgi.dockerfile
  38. 7 4
      frameworks/Python/falcon/falcon-orjson.dockerfile
  39. 4 1
      frameworks/Python/falcon/falcon-pypy3.dockerfile
  40. 3 1
      frameworks/Python/falcon/falcon-socketify-asgi-pypy3.dockerfile
  41. 6 5
      frameworks/Python/falcon/falcon-socketify-asgi.dockerfile
  42. 4 1
      frameworks/Python/falcon/falcon-socketify-wsgi-pypy3.dockerfile
  43. 7 5
      frameworks/Python/falcon/falcon-socketify-wsgi.dockerfile
  44. 4 1
      frameworks/Python/falcon/falcon-waitress.dockerfile
  45. 6 4
      frameworks/Python/falcon/falcon.dockerfile
  46. 2 5
      frameworks/Python/falcon/helpers.py
  47. 0 1
      frameworks/Python/falcon/requirements-bjoern.txt
  48. 3 0
      frameworks/Python/falcon/requirements-db-pony.txt
  49. 3 0
      frameworks/Python/falcon/requirements-db-tortoise.txt
  50. 0 1
      frameworks/Python/falcon/requirements-fastwsgi.txt
  51. 2 0
      frameworks/Python/falcon/requirements-meinheld.txt
  52. 0 4
      frameworks/Python/falcon/requirements-pypy.txt
  53. 0 1
      frameworks/Python/falcon/requirements-socketify-pypy.txt
  54. 0 1
      frameworks/Python/falcon/requirements-socketify.txt
  55. 4 0
      frameworks/Python/falcon/requirements-uvicorn.txt
  56. 1 0
      frameworks/Python/falcon/requirements-waitress.txt
  57. 0 12
      frameworks/Python/falcon/requirements.txt
  58. 280 0
      frameworks/Python/fastwsgi/app-asgi.py
  59. 4 2
      frameworks/Python/fastwsgi/app.py
  60. 25 1
      frameworks/Python/fastwsgi/benchmark_config.json
  61. 19 1
      frameworks/Python/fastwsgi/config.toml
  62. 13 0
      frameworks/Python/fastwsgi/fastwsgi-asgi.dockerfile
  63. 2 3
      frameworks/Python/fastwsgi/fastwsgi.dockerfile
  64. 6 1
      frameworks/Python/fastwsgi/requirements.txt
  65. 10 0
      frameworks/Python/fastwsgi/templates/fortune.html
  66. 1 0
      frameworks/Python/flask/app.py
  67. 0 4
      frameworks/Python/flask/benchmark_config.json
  68. 2 2
      frameworks/Python/flask/nginx.conf
  69. 1 1
      frameworks/Python/flask/uwsgi.ini
  70. 0 4
      frameworks/Python/tornado/benchmark_config.json
  71. 0 16
      frameworks/Ruby/sinatra-sequel/benchmark_config.json
  72. 0 6
      frameworks/Ruby/sinatra/benchmark_config.json
  73. 1 3
      frameworks/Rust/salvo/Cargo.toml
  74. 198 73
      frameworks/Rust/xitca-web/Cargo.lock
  75. 10 10
      frameworks/Rust/xitca-web/Cargo.toml
  76. 1 1
      frameworks/Rust/xitca-web/rust-toolchain.toml
  77. 8 3
      frameworks/Rust/xitca-web/src/db.rs
  78. 14 6
      frameworks/Rust/xitca-web/src/main_iou.rs
  79. 1 1
      frameworks/Rust/xitca-web/xitca-web.dockerfile
  80. 4 12
      frameworks/TypeScript/nest/benchmark_config.json

+ 0 - 2
frameworks/C++/cppcms/benchmark_config.json

@@ -24,10 +24,8 @@
       "notes": ""
     },
     "postgres": {
-      "json_url": "/json",
       "db_url": "/db",
       "fortune_url": "/fortunes",
-      "plaintext_url": "/plaintext",
       "cached_query_url": "/cached-worlds/",
       "port": 8080,
       "approach": "Realistic",

+ 0 - 4
frameworks/C++/lithium/benchmark_config.json

@@ -27,12 +27,10 @@
         "versus": "None"
       },
       "postgres": {
-        "json_url"       : "/json",
         "db_url"         : "/db",
         "query_url"      : "/queries?N=",
         "fortune_url"    : "/fortunes",
         "update_url"     : "/updates?N=",
-        "plaintext_url"  : "/plaintext",
         "cached_query_url": "/cached-worlds?N=",
         "port": 8080,
         "approach": "Realistic",
@@ -51,12 +49,10 @@
         "versus": "None"
       },
       "postgres-beta": {
-        "json_url"       : "/json",
         "db_url"         : "/db",
         "query_url"      : "/queries?N=",
         "fortune_url"    : "/fortunes",
         "update_url"     : "/updates?N=",
-        "plaintext_url"  : "/plaintext",
         "cached_query_url": "/cached-worlds?N=",
         "port": 8080,
         "approach": "Realistic",

+ 18 - 2
frameworks/C/h2o/h2o.dockerfile

@@ -15,8 +15,9 @@ RUN apt-get -yqq update && \
       g++ \
       libbrotli-dev \
       libcap-dev \
+      libicu-dev \
       libnuma-dev \
-      libpq-dev \
+      libreadline-dev \
       libssl-dev \
       libtool \
       libuv1-dev \
@@ -25,6 +26,7 @@ RUN apt-get -yqq update && \
       libz-dev \
       make \
       ninja-build \
+      patch \
       pkg-config \
       systemtap-sdt-dev
 
@@ -52,6 +54,20 @@ RUN curl -LSs "https://github.com/x86-64/mustache-c/archive/${MUSTACHE_C_REVISIO
     CFLAGS="-flto -march=native -mtune=native -O3" ./autogen.sh && \
     make -j "$(nproc)" install
 
+ARG POSTGRESQL_VERSION=7b7fa85130330128b404eddebd4f33c6739454b0
+
+WORKDIR /tmp/postgresql-build
+RUN curl -LSs "https://github.com/postgres/postgres/archive/${POSTGRESQL_VERSION}.tar.gz" | \
+      tar --strip-components=1 -xz && \
+    curl -LSs "https://www.postgresql.org/message-id/attachment/146614/v2-0001-Add-PQsendSyncMessage-to-libpq.patch" | \
+      patch -Np1 && \
+    CFLAGS="-flto -march=native -mtune=native -O3" ./configure \
+      --includedir=/usr/local/include/postgresql \
+      --prefix=/usr/local \
+      --with-ssl=openssl && \
+    make -j "$(nproc)" -C src/include install && \
+    make -j "$(nproc)" -C src/interfaces/libpq install
+
 ARG H2O_APP_PREFIX
 WORKDIR /tmp/build
 COPY CMakeLists.txt ../
@@ -72,11 +88,11 @@ ARG DEBIAN_FRONTEND=noninteractive
 RUN apt-get -yqq update && \
     apt-get -yqq install \
       libnuma1 \
-      libpq5 \
       libyajl2
 ARG H2O_APP_PREFIX
 COPY --from=compile "${H2O_APP_PREFIX}" "${H2O_APP_PREFIX}/"
 COPY --from=compile /usr/local/lib/libmustache_c.so "${H2O_APP_PREFIX}/lib/"
+COPY --from=compile /usr/local/lib/libpq.so.5.16 "${H2O_APP_PREFIX}/lib/libpq.so.5"
 ENV LD_LIBRARY_PATH="${H2O_APP_PREFIX}/lib"
 EXPOSE 8080
 ARG BENCHMARK_ENV

+ 30 - 14
frameworks/C/h2o/src/database.c

@@ -61,7 +61,7 @@ typedef struct {
 } prepared_statement_t;
 
 static h2o_socket_t *create_socket(int sd, h2o_loop_t *loop);
-static int do_execute_query(db_conn_t *conn, db_query_param_t *param);
+static int do_execute_query(db_conn_t *conn, db_query_param_t *param, bool flush);
 static void error_notification(db_conn_pool_t *pool, bool timeout, const char *error_string);
 static void on_database_connect_error(db_conn_t *conn, bool timeout, const char *error_string);
 static void on_database_connect_read_ready(h2o_socket_t *sock, const char *err);
@@ -104,7 +104,7 @@ static h2o_socket_t *create_socket(int sd, h2o_loop_t *loop)
 	return ret;
 }
 
-static int do_execute_query(db_conn_t *conn, db_query_param_t *param)
+static int do_execute_query(db_conn_t *conn, db_query_param_t *param, bool flush)
 {
 	assert(conn->query_num);
 	assert((conn->queries.head && conn->query_num < conn->pool->config->max_pipeline_query_num) ||
@@ -132,19 +132,21 @@ static int do_execute_query(db_conn_t *conn, db_query_param_t *param)
 		return 1;
 	}
 
-	if (!PQpipelineSync(conn->conn)) {
-		LIBRARY_ERROR("PQpipelineSync", PQerrorMessage(conn->conn));
+	if (!PQsendSyncMessage(conn->conn)) {
+		LIBRARY_ERROR("PQsendSyncMessage", PQerrorMessage(conn->conn));
 		return 1;
 	}
 
-	const int send_status = PQflush(conn->conn);
+	if (flush) {
+		const int send_status = PQflush(conn->conn);
 
-	if (send_status < 0) {
-		LIBRARY_ERROR("PQflush", PQerrorMessage(conn->conn));
-		return 1;
+		if (send_status < 0) {
+			LIBRARY_ERROR("PQflush", PQerrorMessage(conn->conn));
+			return 1;
+		}
+		else if (send_status)
+			h2o_socket_notify_write(conn->sock, on_database_write_ready);
 	}
-	else if (send_status)
-		h2o_socket_notify_write(conn->sock, on_database_write_ready);
 
 	if (!conn->queries.head && !(conn->flags & (EXPECT_SYNC | IGNORE_RESULT))) {
 		assert(!h2o_timer_is_linked(&conn->timer));
@@ -527,8 +529,8 @@ static void prepare_statements(db_conn_t *conn)
 			iter = iter->next;
 		} while (iter);
 
-		if (!PQpipelineSync(conn->conn)) {
-			LIBRARY_ERROR("PQpipelineSync", PQerrorMessage(conn->conn));
+		if (!PQsendSyncMessage(conn->conn)) {
+			LIBRARY_ERROR("PQsendSyncMessage", PQerrorMessage(conn->conn));
 			on_database_connect_error(conn, false, DB_ERROR);
 			return;
 		}
@@ -547,6 +549,8 @@ static void prepare_statements(db_conn_t *conn)
 
 static void process_queries(db_conn_t *conn)
 {
+	const bool flush = conn->query_num && conn->pool->queries.head;
+
 	while (conn->query_num && conn->pool->queries.head) {
 		db_query_param_t * const param = H2O_STRUCT_FROM_MEMBER(db_query_param_t,
 		                                                        l,
@@ -559,7 +563,7 @@ static void process_queries(db_conn_t *conn)
 
 		conn->pool->queries.head = param->l.next;
 
-		if (do_execute_query(conn, param)) {
+		if (do_execute_query(conn, param, false)) {
 			param->on_error(param, DB_ERROR);
 
 			if (PQstatus(conn->conn) != CONNECTION_OK) {
@@ -569,6 +573,18 @@ static void process_queries(db_conn_t *conn)
 		}
 	}
 
+	if (flush) {
+		const int send_status = PQflush(conn->conn);
+
+		if (send_status < 0) {
+			LIBRARY_ERROR("PQflush", PQerrorMessage(conn->conn));
+			on_database_error(conn, DB_ERROR);
+			return;
+		}
+		else if (send_status)
+			h2o_socket_notify_write(conn->sock, on_database_write_ready);
+	}
+
 	if (!conn->queries.head && !(conn->flags & (EXPECT_SYNC | IDLE | IGNORE_RESULT))) {
 		conn->l.next = conn->pool->conn;
 		conn->pool->conn = &conn->l;
@@ -657,7 +673,7 @@ int execute_database_query(db_conn_pool_t *pool, db_query_param_t *param)
 		assert(conn->flags & IDLE);
 		assert(!(conn->flags & (EXPECT_SYNC | IGNORE_RESULT)));
 		pool->conn = conn->l.next;
-		ret = do_execute_query(conn, param);
+		ret = do_execute_query(conn, param, true);
 
 		if (ret) {
 			if (PQstatus(conn->conn) == CONNECTION_OK) {

+ 1 - 4
frameworks/Haskell/ihp/ihp.dockerfile

@@ -7,11 +7,8 @@ WORKDIR /ihp
 RUN nix-env -i cachix
 RUN cachix use digitallyinduced
 
-# Warmup docker build cache
-RUN nix-shell -j auto --cores 0 --command "echo ok"
-
 # Build 
-RUN nix-build
+RUN nix-build -j auto --cores 0
 
 # Setup
 ENV DATABASE_URL=postgres://benchmarkdbuser:benchmarkdbpass@tfb-database:5432/hello_world

+ 1 - 1
frameworks/Haskell/ihp/src/default.nix

@@ -1,7 +1,7 @@
 let
     ihp = builtins.fetchGit {
         url = "https://github.com/digitallyinduced/ihp.git";
-        ref = "refs/tags/v0.20.0";
+        ref = "refs/tags/v1.0.1";
     };
     haskellEnv = import "${ihp}/NixSupport/default.nix" {
         ihp = ihp;

+ 1 - 1
frameworks/Java/micronaut/gradle.properties

@@ -1 +1 @@
-micronautVersion = 3.9.0
+micronautVersion = 3.9.1

+ 1 - 1
frameworks/PHP/mark/composer.json

@@ -1,5 +1,5 @@
 {
     "require": {
-        "mark-php/mark": "^1.1"
+        "mark-php/mark": "^v2"
     }
 }

+ 1 - 0
frameworks/PHP/mark/mark.dockerfile

@@ -18,6 +18,7 @@ ADD ./ /mark
 WORKDIR /mark
 
 RUN composer install --optimize-autoloader --classmap-authoritative --no-dev --quiet
+RUN sed -i "s|opcache.jit=off|opcache.jit=tracing|g" /etc/php/8.2/cli/conf.d/10-opcache.ini
 
 EXPOSE 8080
 

+ 3 - 1
frameworks/PHP/mark/start.php

@@ -7,6 +7,8 @@ require 'vendor/autoload.php';
 
 $api = new App('http://0.0.0.0:8080');
 
+$api->name = "Mark";
+
 $api->count = (int) shell_exec('nproc');
 
 $api->any('/plaintext', function () {
@@ -27,7 +29,7 @@ $api->get('/json', function () {
 
 $date = gmdate('D, d M Y H:i:s').' GMT';
 
-$api->onWorkerStart = function () {
+$api->onWorkerStart = static function () {
     Timer::add(1, function () {
         global $date;
         $date = gmdate('D, d M Y H:i:s').' GMT';

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

@@ -10,13 +10,13 @@ RUN LC_ALL=C.UTF-8 add-apt-repository ppa:ondrej/php > /dev/null && \
 RUN apt-get update -yqq > /dev/null && \
     apt-get install -yqq wget git libxml2-dev systemtap-sdt-dev \
                     zlib1g-dev libpcre3-dev libargon2-0-dev libsodium-dev \
-                    php8.1-cli php8.1-dev libphp8.1-embed php8.1-mysql > /dev/null
+                    php8.2-cli php8.2-dev libphp8.2-embed php8.2-mysql > /dev/null
 
 ADD . .
 
 ENV NGINX_VERSION 1.24.0
 
-RUN git clone -b v0.0.26 --single-branch --depth 1 https://github.com/rryqszq4/ngx_php7.git > /dev/null
+RUN git clone -b v0.0.27 --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 && \
     tar -zxf nginx-${NGINX_VERSION}.tar.gz && \
@@ -28,7 +28,7 @@ RUN wget -q http://nginx.org/download/nginx-${NGINX_VERSION}.tar.gz && \
             --add-module=/ngx_php7/third_party/ngx_devel_kit \
             --add-module=/ngx_php7 > /dev/null && \
     make > /dev/null && make install > /dev/null
-RUN sed -i "s|opcache.jit=off|;opcache.jit=off|g" /etc/php/8.1/embed/conf.d/10-opcache.ini
+RUN sed -i "s|opcache.jit=off|;opcache.jit=off|g" /etc/php/8.2/embed/conf.d/10-opcache.ini
 
 EXPOSE 8080
 

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

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

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

@@ -10,12 +10,12 @@ RUN LC_ALL=C.UTF-8 add-apt-repository ppa:ondrej/php > /dev/null && \
 RUN apt-get update -yqq > /dev/null && \
     apt-get install -yqq wget git libxml2-dev systemtap-sdt-dev \
                     zlib1g-dev libpcre3-dev libargon2-0-dev libsodium-dev \
-                    php8.1-cli php8.1-dev libphp8.1-embed php8.1-pgsql > /dev/null
+                    php8.2-cli php8.2-dev libphp8.2-embed php8.2-pgsql > /dev/null
 ADD . .
 
 ENV NGINX_VERSION 1.24.0
 
-RUN git clone -b v0.0.26 --single-branch --depth 1 https://github.com/rryqszq4/ngx-php.git > /dev/null
+RUN git clone -b v0.0.27 --single-branch --depth 1 https://github.com/rryqszq4/ngx-php.git > /dev/null
 
 RUN wget -q http://nginx.org/download/nginx-${NGINX_VERSION}.tar.gz && \
     tar -zxf nginx-${NGINX_VERSION}.tar.gz && \
@@ -32,7 +32,7 @@ RUN sed -i "s|app.php|app-pg.php|g" /deploy/nginx.conf
 
 RUN export WORKERS=$(( 4 * $(nproc) )) && \
     sed -i "s|worker_processes  auto|worker_processes $WORKERS|g" /deploy/nginx.conf
-RUN sed -i "s|opcache.jit=off|opcache.jit=function|g" /etc/php/8.1/embed/conf.d/10-opcache.ini
+RUN sed -i "s|opcache.jit=off|opcache.jit=function|g" /etc/php/8.2/embed/conf.d/10-opcache.ini
 EXPOSE 8080
 
 CMD /nginx/sbin/nginx -c /deploy/nginx.conf

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

@@ -10,12 +10,12 @@ RUN LC_ALL=C.UTF-8 add-apt-repository ppa:ondrej/php > /dev/null && \
 RUN apt-get update -yqq > /dev/null && \
     apt-get install -yqq wget git libxml2-dev systemtap-sdt-dev \
                     zlib1g-dev libpcre3-dev libargon2-0-dev libsodium-dev \
-                    php8.1-cli php8.1-dev libphp8.1-embed php8.1-mysql > /dev/null
+                    php8.2-cli php8.2-dev libphp8.2-embed php8.2-mysql
 ADD . .
 
 ENV NGINX_VERSION 1.24.0
 
-RUN git clone -b v0.0.26 --single-branch --depth 1 https://github.com/rryqszq4/ngx-php.git > /dev/null
+RUN git clone -b v0.0.27 --single-branch --depth 1 https://github.com/rryqszq4/ngx-php.git > /dev/null
 
 RUN wget -q http://nginx.org/download/nginx-${NGINX_VERSION}.tar.gz && \
     tar -zxf nginx-${NGINX_VERSION}.tar.gz && \
@@ -27,7 +27,7 @@ RUN wget -q http://nginx.org/download/nginx-${NGINX_VERSION}.tar.gz && \
             --add-module=/ngx-php/third_party/ngx_devel_kit \
             --add-module=/ngx-php > /dev/null && \
     make > /dev/null && make install > /dev/null
-RUN sed -i "s|opcache.jit=off|;opcache.jit=off|g" /etc/php/8.1/embed/conf.d/10-opcache.ini
+RUN sed -i "s|opcache.jit=off|;opcache.jit=off|g" /etc/php/8.2/embed/conf.d/10-opcache.ini
 
 EXPOSE 8080
 

+ 2 - 0
frameworks/PHP/spiral/.env

@@ -4,3 +4,5 @@ DB_DSN=mysql:host=tfb-database:3306;charset=utf8;dbname=hello_world;user=benchma
 
 # Set to application specific value, used to encrypt/decrypt cookies and etc.
 ENCRYPTER_KEY=def00000f6f989c4ba99b5eec3dcd4f5b0fb7e5fbbf95d3cacb6b7ed049e22b4a931db7ad59085225b36d051fb06530c8a41b83d10761439326656536293473c2472d911
+
+TOKENIZER_CACHE_TARGETS=true

+ 1 - 1
frameworks/PHP/spiral/.rr.yaml

@@ -1,4 +1,4 @@
-version: '2.7'
+version: '3'
 
 rpc:
   listen: tcp://127.0.0.1:6001

+ 3 - 3
frameworks/PHP/spiral/README.md

@@ -6,11 +6,11 @@ Benchmark code is located in `app/src/Controllers/BenchmarkController.php`.
 
 ## Infrastructure and Versions
 The tests were run with:
-* [Spiral Framework Version 2](https://github.com/spiral/framework/)
+* [Spiral Framework Version 3.7](https://github.com/spiral/framework/)
 * [Spiral/Stempler](https://github.com/spiral/stempler) as template engine
 * [Cycle ORM 2.*](https://github.com/cycle/orm)
-* [RoadRunner 2.*](https://roadrunner.dev/)
-* [PHP Version 8.0.*](http://www.php.net/) in CLI mode with OPCache
+* [RoadRunner 2023.*](https://roadrunner.dev/)
+* [PHP Version 8.2.*](http://www.php.net/) in CLI mode with OPCache
 
 ## Test URLs
 Test                | URL 

+ 14 - 15
frameworks/PHP/spiral/app.php

@@ -1,23 +1,22 @@
 <?php
-/**
- * Spiral Framework.
- *
- * @license   MIT
- * @author    Anton Titov (Wolfy-J)
- */
+
 declare(strict_types=1);
 
-mb_internal_encoding('UTF-8');
-error_reporting(E_ALL | E_STRICT);
-ini_set('display_errors', 'stderr');
+use App\App;
+
+\mb_internal_encoding('UTF-8');
+\error_reporting(E_ALL | E_STRICT ^ E_DEPRECATED);
+\ini_set('display_errors', 'stderr');
 
-//Composer
+// Register Composer's auto loader.
 require __DIR__ . '/vendor/autoload.php';
 
-//Initiating shared container, bindings, directories and etc
-$app = \App\App::init(['root' => __DIR__]);
+// Initialize shared container, bindings, directories and etc.
+$app = App::create(directories: ['root' => __DIR__])->run();
 
-if ($app !== null) {
-    $code = (int)$app->serve();
-    exit($code);
+if ($app === null) {
+    exit(255);
 }
+
+$code = (int)$app->serve();
+exit($code);

+ 10 - 18
frameworks/PHP/spiral/app/src/App.php

@@ -1,16 +1,11 @@
 <?php
-/**
- * Spiral Framework.
- *
- * @license   MIT
- * @author    Anton Titov (Wolfy-J)
- */
+
 declare(strict_types=1);
 
 namespace App;
 
-use App\Bootloader\DebugBootloader;
 use App\Bootloader\RoutesBootloader;
+use Spiral\Boot\Bootloader\CoreBootloader;
 use Spiral\Bootloader;
 use Spiral\DotEnv\Bootloader as DotEnv;
 use Spiral\Framework\Kernel;
@@ -19,31 +14,33 @@ use Spiral\Cycle\Bootloader as CycleBridge;
 use Spiral\RoadRunnerBridge\Bootloader as RoadRunnerBridge;
 use Spiral\Stempler\Bootloader as Stempler;
 use Spiral\Scaffolder\Bootloader as Scaffolder;
+use Spiral\Tokenizer\Bootloader\TokenizerListenerBootloader;
 
 class App extends Kernel
 {
+    protected const SYSTEM = [
+        CoreBootloader::class,
+        TokenizerListenerBootloader::class,
+        // Environment configuration
+        DotEnv\DotenvBootloader::class,
+    ];
+
     /*
      * List of components and extensions to be automatically registered
      * within system container on application start.
      */
     protected const LOAD = [
-        // Environment configuration
-        DotEnv\DotenvBootloader::class,
-
         // Core Services
         Bootloader\DebugBootloader::class,
         Bootloader\SnapshotsBootloader::class,
 
         // Security and validation
         Bootloader\Security\EncrypterBootloader::class,
-        Bootloader\Security\ValidationBootloader::class,
         Bootloader\Security\FiltersBootloader::class,
         Bootloader\Security\GuardBootloader::class,
 
         RoadRunnerBridge\HttpBootloader::class,
 
-        DebugBootloader::class,
-
         // HTTP extensions
         Nyholm\NyholmBootloader::class,
         Bootloader\Http\RouterBootloader::class,
@@ -67,12 +64,7 @@ class App extends Kernel
         // Framework commands
         Bootloader\CommandBootloader::class,
         RoadRunnerBridge\CommandBootloader::class,
-    ];
 
-    /*
-     * Application specific services and extensions.
-     */
-    protected const APP = [
         RoutesBootloader::class,
     ];
 }

+ 0 - 37
frameworks/PHP/spiral/app/src/Bootloader/DebugBootloader.php

@@ -1,37 +0,0 @@
-<?php
-/**
- * Spiral Framework.
- *
- * @license   MIT
- * @author    Anton Titov (Wolfy-J)
- */
-declare(strict_types=1);
-
-namespace App\Bootloader;
-
-use Psr\Http\Message\ResponseInterface;
-use Psr\Http\Message\ServerRequestInterface;
-use Psr\Http\Server\MiddlewareInterface;
-use Psr\Http\Server\RequestHandlerInterface;
-use Spiral\Boot\Bootloader\Bootloader;
-use Spiral\Bootloader\Http\HttpBootloader;
-
-class DebugBootloader extends Bootloader implements MiddlewareInterface
-{
-    public function boot(HttpBootloader $http)
-    {
-        $http->addMiddleware(self::class);
-    }
-
-    public function process(
-        ServerRequestInterface $request,
-        RequestHandlerInterface $handler
-    ): ResponseInterface {
-        try {
-            return $handler->handle($request);
-        } catch (\Throwable $e) {
-            dumprr((string)$e);
-            throw $e;
-        }
-    }
-}

+ 19 - 20
frameworks/PHP/spiral/app/src/Bootloader/RoutesBootloader.php

@@ -1,30 +1,29 @@
 <?php
-/**
- * Spiral Framework.
- *
- * @license   MIT
- * @author    Anton Titov (Wolfy-J)
- */
+
 declare(strict_types=1);
 
 namespace App\Bootloader;
 
 use App\Controller\BenchmarkController;
-use Spiral\Boot\Bootloader\Bootloader;
-use Spiral\Router\Route;
-use Spiral\Router\RouterInterface;
-use Spiral\Router\Target\Controller;
+use Spiral\Bootloader\Http\RoutesBootloader as BaseRoutesBootloader;
+use Spiral\Router\Loader\Configurator\RoutingConfigurator;
 
-class RoutesBootloader extends Bootloader
+final class RoutesBootloader extends BaseRoutesBootloader
 {
-    /**
-     * @param RouterInterface $router
-     */
-    public function boot(RouterInterface $router)
+    protected function globalMiddleware(): array
+    {
+        return [];
+    }
+
+    protected function middlewareGroups(): array
+    {
+        return [];
+    }
+
+    protected function defineRoutes(RoutingConfigurator $routes): void
     {
-        $router->addRoute(
-            'benchmark',
-            new Route('/<action>[/<queries>]', new Controller(BenchmarkController::class))
-        );
+        $routes
+            ->add('benchmark', '/<action>[/<queries>]')
+            ->controller(BenchmarkController::class);
     }
-}
+}

+ 20 - 66
frameworks/PHP/spiral/app/src/Controller/BenchmarkController.php

@@ -1,10 +1,5 @@
 <?php
-/**
- * Spiral Framework.
- *
- * @license   MIT
- * @author    Anton Titov (Wolfy-J)
- */
+
 declare(strict_types=1);
 
 namespace App\Controller;
@@ -13,61 +8,32 @@ use App\Model\Fortune;
 use App\Model\Repository\FortuneRepository;
 use App\Model\Repository\WorldRepository;
 use App\Model\World;
-use Cycle\ORM\Transaction;
+use Cycle\ORM\EntityManagerInterface;
 use Nyholm\Psr7\Response;
+use Psr\Http\Message\ResponseInterface;
 use Spiral\Core\Container\SingletonInterface;
-use Spiral\Views\ViewInterface;
 use Spiral\Views\ViewsInterface;
 
 final class BenchmarkController implements SingletonInterface
 {
-    /** @var ViewInterface */
-    private $view;
-
-    /** @var Transaction */
-    private $transaction;
-
-    /** @var FortuneRepository */
-    private $fortunes;
-
-    /** @var WorldRepository */
-    private $worlds;
-
-    /** @var Response */
-    private $plain;
+    private ResponseInterface $plain;
 
-    /**
-     * @param ViewsInterface    $views
-     * @param Transaction       $transaction
-     * @param FortuneRepository $fortunes
-     * @param WorldRepository   $worlds
-     */
     public function __construct(
-        ViewsInterface $views,
-        Transaction $transaction,
-        FortuneRepository $fortunes,
-        WorldRepository $worlds
+        private readonly ViewsInterface $views,
+        private readonly EntityManagerInterface $entityManager,
+        private readonly FortuneRepository $fortunes,
+        private readonly WorldRepository $worlds
     ) {
-        $this->view = $views->get('fortunes');
-
-        $this->transaction = $transaction;
-        $this->fortunes = $fortunes;
-        $this->worlds = $worlds;
-
         $this->plain = new Response(200, ['Content-Type' => 'text/plain', 'Server' => 'Spiral']);
         $this->plain->getBody()->write('Hello, World!');
     }
 
-    /**
-     * @return array
-     */
-    public function json()
+    public function json(): array
     {
         return ['message' => 'Hello, World!'];
     }
 
     /**
-     * @return World|null
      * @throws \Exception
      */
     public function db(): ?World
@@ -76,11 +42,9 @@ final class BenchmarkController implements SingletonInterface
     }
 
     /**
-     * @param int $queries
-     * @return array
      * @throws \Exception
      */
-    public function queries($queries = 1): array
+    public function queries(mixed $queries = null): array
     {
         $queries = $this->clamp($queries);
 
@@ -105,51 +69,41 @@ final class BenchmarkController implements SingletonInterface
 
         $fortunes[] = $fortune;
 
-        usort($fortunes, function ($left, $right) {
-            return strcmp($left->message, $right->message);
-        });
+        \usort($fortunes, static fn ($left, $right): int => \strcmp($left->message, $right->message));
 
-        return $this->view->render(compact('fortunes'));
+        return $this->views->render('fortunes', ['fortunes' => $fortunes]);
     }
 
     /**
-     * @param int $queries
-     * @return array
      * @throws \Throwable
      */
-    public function updates($queries = 1): array
+    public function updates(mixed $queries = null): array
     {
         $queries = $this->clamp($queries);
 
         $worlds = [];
-
         while ($queries--) {
             $world = $this->worlds->findRandom();
-            $world->randomNumber = random_int(1, 10000);
+            $world->randomNumber = \random_int(1, 10000);
 
-            $this->transaction->persist($world)->run();
+            $this->entityManager->persist($world);
 
             $worlds[] = $world;
         }
 
+        $this->entityManager->run();
+
         return $worlds;
     }
 
-    /**
-     * @return Response
-     */
-    public function plaintext(): Response
+    public function plaintext(): ResponseInterface
     {
         return $this->plain;
     }
 
-    /**
-     * @param $value
-     * @return int
-     */
-    private function clamp($value): int
+    private function clamp(mixed $value): int
     {
-        if (!is_numeric($value) || $value < 1) {
+        if (!\is_numeric($value) || $value < 1) {
             return 1;
         }
 

+ 5 - 13
frameworks/PHP/spiral/app/src/Model/Fortune.php

@@ -1,10 +1,5 @@
 <?php
-/**
- * Spiral Framework.
- *
- * @license   MIT
- * @author    Anton Titov (Wolfy-J)
- */
+
 declare(strict_types=1);
 
 namespace App\Model;
@@ -13,19 +8,16 @@ use App\Model\Repository;
 use Cycle\Annotated\Annotation\Column;
 use Cycle\Annotated\Annotation\Entity;
 
-#[Entity(table: 'Fortune', repository: Repository\FortuneRepository::class)]
+#[Entity(table: 'fortune', repository: Repository\FortuneRepository::class)]
 class Fortune implements \JsonSerializable
 {
     #[Column(type: 'primary')]
-    public $id;
+    public int $id;
 
     #[Column(type: 'text')]
-    public $message;
+    public string $message;
 
-    /**
-     * @return array
-     */
-    public function jsonSerialize(): mixed
+    public function jsonSerialize(): array
     {
         return ['id' => $this->id, 'message' => $this->message];
     }

+ 3 - 9
frameworks/PHP/spiral/app/src/Model/Repository/FortuneRepository.php

@@ -1,17 +1,11 @@
 <?php
-/**
- * Spiral Framework.
- *
- * @license   MIT
- * @author    Anton Titov (Wolfy-J)
- */
+
 declare(strict_types=1);
 
 namespace App\Model\Repository;
 
 use Cycle\ORM\Select\Repository;
 
-class FortuneRepository extends Repository
+final class FortuneRepository extends Repository
 {
-
-}
+}

+ 4 - 10
frameworks/PHP/spiral/app/src/Model/Repository/WorldRepository.php

@@ -1,10 +1,5 @@
 <?php
-/**
- * Spiral Framework.
- *
- * @license   MIT
- * @author    Anton Titov (Wolfy-J)
- */
+
 declare(strict_types=1);
 
 namespace App\Model\Repository;
@@ -12,14 +7,13 @@ namespace App\Model\Repository;
 use App\Model\World;
 use Cycle\ORM\Select\Repository;
 
-class WorldRepository extends Repository
+final class WorldRepository extends Repository
 {
     /**
-     * @return World|null
      * @throws \Exception
      */
     public function findRandom(): ?World
     {
-        return $this->findByPK(random_int(1, 10000));
+        return $this->findByPK(\random_int(1, 10000));
     }
-}
+}

+ 5 - 13
frameworks/PHP/spiral/app/src/Model/World.php

@@ -1,10 +1,5 @@
 <?php
-/**
- * Spiral Framework.
- *
- * @license   MIT
- * @author    Anton Titov (Wolfy-J)
- */
+
 declare(strict_types=1);
 
 namespace App\Model;
@@ -13,19 +8,16 @@ use App\Model\Repository;
 use Cycle\Annotated\Annotation\Column;
 use Cycle\Annotated\Annotation\Entity;
 
-#[Entity(table: 'World', repository: Repository\WorldRepository::class)]
+#[Entity(table: 'world', repository: Repository\WorldRepository::class)]
 class World implements \JsonSerializable
 {
     #[Column(type: 'primary')]
-    public $id;
+    public int $id;
 
     #[Column(type: 'int', name: 'randomNumber')]
-    public $randomNumber;
+    public int $randomNumber;
 
-    /**
-     * @return array
-     */
-    public function jsonSerialize(): mixed
+    public function jsonSerialize(): array
     {
         return ['id' => $this->id, 'randomNumber' => $this->randomNumber];
     }

+ 7 - 6
frameworks/PHP/spiral/composer.json

@@ -9,18 +9,19 @@
     }
   ],
   "require": {
-    "php": ">=8.0",
-    "spiral/framework": "^2.9",
-    "spiral/nyholm-bridge": "^1.0",
-    "spiral/cycle-bridge": "^1.0",
-    "spiral/roadrunner-bridge": "^1.0"
+    "php": ">=8.1",
+    "spiral/framework": "^3.7",
+    "spiral/nyholm-bridge": "^1.3",
+    "spiral/cycle-bridge": "^2.4",
+    "spiral/roadrunner-bridge": "^3.0",
+    "spiral/roadrunner-cli": "^2.4"
   },
   "scripts": {
     "post-create-project-cmd": [
       "php -r \"copy('.env.sample', '.env');\"",
       "php app.php encrypt:key -m .env",
       "php app.php configure -vv",
-      "spiral get-binary"
+      "rr get-binary --quiet"
     ]
   },
   "autoload": {

+ 1 - 1
frameworks/PHP/spiral/spiral.dockerfile

@@ -1,4 +1,4 @@
-FROM php:8.1.2
+FROM php:8.2.0-cli
 
 RUN docker-php-ext-install pdo_mysql > /dev/null
 

+ 24 - 2
frameworks/Pascal/mormot/benchmark_config.json

@@ -22,7 +22,7 @@
         "webserver": "None",
         "os": "Linux",
         "database_os": "Linux",
-        "display_name": "mormot",
+        "display_name": "mormot [orm]",
         "notes": "",
         "versus": "None"
       },
@@ -44,7 +44,29 @@
         "webserver": "None",
         "os": "Linux",
         "database_os": "Linux",
-        "display_name": "mormot",
+        "display_name": "mormot [direct]",
+        "notes": "",
+        "versus": "None"
+      },
+      "postgres-async": {
+        "dockerfile": "mormot.dockerfile",
+        "db_url": "/asyncdb",
+        "query_url": "/asyncqueries?queries=",
+        "fortune_url": "/asyncfortunes",
+        "update_url": "/asyncupdates?queries=",
+        "port": 8080,
+        "approach": "Realistic",
+        "classification": "Fullstack",
+        "database": "postgres",
+        "framework": "mormot",
+        "language": "Pascal",
+        "flavor": "None",
+        "orm": "Raw",
+        "platform": "None",
+        "webserver": "None",
+        "os": "Linux",
+        "database_os": "Linux",
+        "display_name": "mormot [async]",
         "notes": "",
         "versus": "None"
       }

+ 1 - 1
frameworks/Pascal/mormot/setup_and_build.sh

@@ -35,7 +35,7 @@ echo "Download statics from $URL ..."
 wget -qO- "$URL" | tar -xz -C ./libs/mORMot/static
 
 # uncomment for fixed commit URL
-URL=https://github.com/synopse/mORMot2/tarball/f430300300ed51f128b32036423d920032628159
+URL=https://github.com/synopse/mORMot2/tarball/bda095131d3f848b070981ab155adc9a73c0e75c
 #URL="https://api.github.com/repos/synopse/mORMot2/tarball/$USED_TAG"
 echo "Download and unpacking mORMot sources from $URL ..."
 wget -qO- "$URL" | tar -xz -C ./libs/mORMot  --strip-components=1

+ 260 - 124
frameworks/Pascal/mormot/src/raw.pas

@@ -6,7 +6,7 @@ program raw;
    in modern pascal and the mORMot 2 framework
  ----------------------------------------------------
  https://github.com/TechEmpower/FrameworkBenchmarks/wiki
- command line optional syntax: raw [threads] [cores] [servers]
+ command line optional syntax: run "raw -?"
 }
 
 {$I mormot.defines.inc}
@@ -80,15 +80,20 @@ type
 
   // main server class
   TRawAsyncServer = class(TSynPersistent)
-  private
+  protected
     fHttpServer: THttpAsyncServer;
     fDbPool: TSqlDBConnectionProperties;
     fModel: TOrmModel;
     fStore: TRestServerDB;
     fTemplate: TSynMustache;
-  protected
-    // as used by /rawqueries and /rawupdates
+    fCachedWorldsTable: POrmCacheTable;
+    fAsyncWorldRead, fAsyncFortunesRead: TSqlDBPostgresAsyncStatement;
+    fAsyncWorldUpdate: TSqlDBPostgresAsyncStatement;
+    procedure OnAsyncDb(Statement: TSqlDBPostgresAsyncStatement; Context: TObject);
+    procedure OnAsyncFortunes(Statement: TSqlDBPostgresAsyncStatement; Context: TObject);
+    // pipelined reading as used by /rawqueries and /rawupdates
     function GetRawRandomWorlds(cnt: PtrInt; out res: TWorlds): boolean;
+    function ComputeRawFortunes(stmt: TSqlDBStatement; ctxt: THttpServerRequest): integer;
   public
     constructor Create(threadCount: integer; flags: THttpServerOptions;
       pin2Core: integer = -1); reintroduce;
@@ -106,6 +111,11 @@ type
     function rawqueries(ctxt: THttpServerRequest): cardinal;
     function rawfortunes(ctxt: THttpServerRequest): cardinal;
     function rawupdates(ctxt: THttpServerRequest): cardinal;
+    // asynchronous PostgreSQL pipelined DB access
+    function asyncdb(ctxt: THttpServerRequest): cardinal;
+    function asyncqueries(ctxt: THttpServerRequest): cardinal;
+    function asyncfortunes(ctxt: THttpServerRequest): cardinal;
+    function asyncupdates(ctxt: THttpServerRequest): cardinal;
   end;
 
 {$I-}
@@ -116,7 +126,7 @@ const
 
   WORLD_COUNT       = 10000;
   WORLD_READ_SQL    = 'select id,randomNumber from World where id=?';
-  WORLD_UPDATE_SQLN ='update World as t set randomNumber = v.r from ' +
+  WORLD_UPDATE_SQLN = 'update World as t set randomNumber = v.r from ' +
     '(SELECT unnest(?::bigint[]), unnest(?::bigint[]) order by 1) as v(id, r)' +
     ' where t.id = v.id';
   FORTUNES_SQL      = 'select id,message from Fortune';
@@ -177,8 +187,20 @@ begin
   fStore := TRestServerDB.Create(fModel, SQLITE_MEMORY_DATABASE_NAME);
   fStore.NoAjaxJson := true;
   fStore.Server.CreateMissingTables; // create SQlite3 virtual tables
+  with (fDBPool as TSqlDBPostgresConnectionProperties).Async do
+  begin
+    fAsyncWorldRead := NewStatement(WORLD_READ_SQL,
+      [asoForceConnectionFlush, asoForcePipelineSync]);
+    fAsyncFortunesRead := NewStatement(FORTUNES_SQL,
+      [asoForceConnectionFlush, asoForcePipelineSync]);
+    fAsyncWorldUpdate := NewStatement(WORLD_UPDATE_SQLN,
+      [asoForceConnectionFlush, asoForcePipelineSync, asoExpectNoResult]);
+    // no SetThreadCpuAffinity(fAsyncWorldRead.Owner.Thread, pin2Core) needed
+  end;
+  // pre-fill the ORM
   if fStore.Server.Cache.SetCache(TOrmCachedWorld) then
     fStore.Server.Cache.FillFromQuery(TOrmCachedWorld, '', []);
+  fCachedWorldsTable := fStore.Orm.Cache.Table(TOrmCachedWorld);
   // initialize the mustache template for /fortunes
   fTemplate := TSynMustache.Parse(FORTUNES_TPL);
   // setup the HTTP server
@@ -219,7 +241,7 @@ end;
 function TRawAsyncServer.GetRawRandomWorlds(cnt: PtrInt; out res: TWorlds): boolean;
 var
   conn: TSqlDBConnection;
-  stmt: ISQLDBStatement;
+  stmt: ISqlDBStatement;
   pConn: TSqlDBPostgresConnection absolute conn;
   pStmt: TSqlDBPostgresStatement;
   i: PtrInt;
@@ -239,7 +261,6 @@ begin
     pStmt.SendPipelinePrepared;
     pConn.PipelineSync;
   end;
-  pConn.Flush; // in case we use modified libpq what not flush inside PQPipelineSync - flush manually
   for i := 0 to cnt - 1 do
   begin
     pStmt.GetPipelineResult;
@@ -254,6 +275,38 @@ begin
   result := true;
 end;
 
+function FortuneCompareByMessage(const A, B): integer;
+begin
+  result := StrComp(pointer(TFortune(A).message), pointer(TFortune(B).message));
+end;
+
+function TRawAsyncServer.ComputeRawFortunes(
+  stmt: TSqlDBStatement; ctxt: THttpServerRequest): integer;
+var
+  list: TFortunes;
+  arr: TDynArray;
+  n: integer;
+  f: ^TFortune;
+begin
+  result := HTTP_BADREQUEST;
+  if stmt = nil then
+    exit;
+  arr.Init(TypeInfo(TFortunes), list, @n);
+  while stmt.Step do
+  begin
+    f := arr.NewPtr;
+    f.id := stmt.ColumnInt(0);
+    f.message := stmt.ColumnUtf8(1);
+  end;
+  f := arr.NewPtr;
+  f.id := 0;
+  f.message := FORTUNES_MESSAGE;
+  arr.Sort(FortuneCompareByMessage);
+  ctxt.OutContent := fTemplate.RenderDataArray(arr);
+  ctxt.OutContentType := HTML_CONTENT_TYPE;
+  result := HTTP_SUCCESS;
+end;
+
 // following methods implement the server endpoints
 
 function TRawAsyncServer.plaintext(ctxt: THttpServerRequest): cardinal;
@@ -302,12 +355,10 @@ function TRawAsyncServer.cached_queries(ctxt: THttpServerRequest): cardinal;
 var
   i: PtrInt;
   res: TOrmWorlds;
-  cache: POrmCacheTable;
 begin
-  cache := fStore.Orm.Cache.Table(TOrmCachedWorld);
   SetLength(res, GetQueriesParamValue(ctxt, 'COUNT='));
   for i := 0 to length(res) - 1 do
-    res[i] := cache.Get(ComputeRandomWorld);
+    res[i] := fCachedWorldsTable.Get(ComputeRandomWorld);
   ctxt.SetOutJson(@res, TypeInfo(TOrmWorlds));
   result := HTTP_SUCCESS;
 end;
@@ -372,7 +423,7 @@ end;
 function TRawAsyncServer.rawdb(ctxt: THttpServerRequest): cardinal;
 var
   conn: TSqlDBConnection;
-  stmt: ISQLDBStatement;
+  stmt: ISqlDBStatement;
 begin
   result := HTTP_SERVERERROR;
   conn := fDbPool.ThreadSafeConnection;
@@ -399,37 +450,15 @@ begin
   result := HTTP_SUCCESS;
 end;
 
-function FortuneCompareByMessage(const A, B): integer;
-begin
-  result := StrComp(pointer(TFortune(A).message), pointer(TFortune(B).message));
-end;
-
 function TRawAsyncServer.rawfortunes(ctxt: THttpServerRequest): cardinal;
 var
   conn: TSqlDBConnection;
-  stmt: ISQLDBStatement;
-  list: TFortunes;
-  arr: TDynArray;
-  n: integer;
-  f: ^TFortune;
+  stmt: ISqlDBStatement;
 begin
   conn := fDbPool.ThreadSafeConnection;
   stmt := conn.NewStatementPrepared(FORTUNES_SQL, true, true);
   stmt.ExecutePrepared;
-  arr.Init(TypeInfo(TFortunes), list, @n);
-  while stmt.Step do
-  begin
-    f := arr.NewPtr;
-    f.id := stmt.ColumnInt(0);
-    f.message := stmt.ColumnUtf8(1);
-  end;
-  f := arr.NewPtr;
-  f.id := 0;
-  f.message := FORTUNES_MESSAGE;
-  arr.Sort(FortuneCompareByMessage);
-  ctxt.OutContent := fTemplate.RenderDataArray(arr);
-  ctxt.OutContentType := HTML_CONTENT_TYPE;
-  result := HTTP_SUCCESS;
+  result := ComputeRawFortunes(stmt.Instance, ctxt);
 end;
 
 var
@@ -473,7 +502,7 @@ var
   res: TWorlds;
   ids, nums: TInt64DynArray;
   conn: TSqlDBConnection;
-  stmt: ISQLDBStatement;
+  stmt: ISqlDBStatement;
 begin
   result := HTTP_SERVERERROR;
   conn := fDbPool.ThreadSafeConnection;
@@ -512,94 +541,163 @@ begin
   result := HTTP_SUCCESS;
 end;
 
+// asynchronous PostgreSQL pipelined DB access
 
+function TRawAsyncServer.asyncdb(ctxt: THttpServerRequest): cardinal;
+begin
+  fAsyncWorldRead.Lock;
+  try
+    fAsyncWorldRead.Bind(1, ComputeRandomWorld);
+    fAsyncWorldRead.ExecuteAsync(ctxt, OnAsyncDb);
+  finally
+    fAsyncWorldRead.UnLock;
+  end;
+  result := ctxt.SetAsyncResponse;
+end;
 
+procedure TRawAsyncServer.OnAsyncDb(Statement: TSqlDBPostgresAsyncStatement;
+  Context: TObject);
 var
-  rawServers: array of TRawAsyncServer;
-  threads, servers, i, k, cpuIdx: integer;
-  pinServers2Cores: boolean;
-  cpuMask: TCpuSet;
-  accessibleCPUCount: PtrInt;
-  flags: THttpServerOptions;
+  ctxt: THttpServerRequest absolute Context;
+begin
+  if (Statement = nil) or
+     not Statement.Step then
+    ctxt.ErrorMessage := 'asyncdb failed'
+  else
+    ctxt.SetOutJson('{"id":%,"randomNumber":%}',
+      [Statement.ColumnInt(0), Statement.ColumnInt(1)]);
+  ctxt.OnAsyncResponse(ctxt);
+end;
 
-function FindCmdLineSwitchVal(const Switch: string; out Value: string): Boolean;
+function TRawAsyncServer.asyncfortunes(ctxt: THttpServerRequest): cardinal;
+begin
+  fAsyncFortunesRead.ExecuteAsyncNoParam(ctxt, OnAsyncFortunes);
+  result := ctxt.SetAsyncResponse;
+end;
+
+procedure TRawAsyncServer.OnAsyncFortunes(Statement: TSqlDBPostgresAsyncStatement;
+  Context: TObject);
 var
-  I, L: integer;
-  S, T: string;
-begin
-  Result := False;
-  S := Switch;
-  Value := '';
-  S := UpperCase(S);
-  I := ParamCount;
-  while (Not Result) and (I>0) do
-  begin
-    L := Length(Paramstr(I));
-    if (L>0) and (ParamStr(I)[1] in SwitchChars) then
-    begin
-      T := Copy(ParamStr(I),2,L-1);
-      T := UpperCase(T);
-      Result := S=T;
-      if Result and (I <> ParamCount) then
-        Value := ParamStr(I+1)
-    end;
-    Dec(i);
+  ctxt: THttpServerRequest absolute Context;
+begin
+  ctxt.OnAsyncResponse(ctxt, ComputeRawFortunes(Statement, ctxt));
+end;
+
+type
+  // simple state machine used for /asyncqueries and /asyncupdates
+  TAsyncWorld = class
+  public
+    server: TRawAsyncServer;
+    request: THttpServerRequest;
+    res: TWorlds;
+    count: PtrInt;
+    fromupdates: boolean;
+    function Queries(owner: TRawAsyncServer; ctxt: THttpServerRequest): cardinal;
+    function Updates(owner: TRawAsyncServer; ctxt: THttpServerRequest): cardinal;
+    procedure DoUpdates;
+    procedure OnQueries(Statement: TSqlDBPostgresAsyncStatement; Context: TObject);
+    procedure OnRes({%H-}Statement: TSqlDBPostgresAsyncStatement; Context: TObject);
   end;
+
+function TRawAsyncServer.asyncqueries(ctxt: THttpServerRequest): cardinal;
+begin
+  result := TAsyncWorld.Create.Queries(self, ctxt);
 end;
 
-procedure ComputeExecutionContextFromParams(cpusAccessible: PtrInt);
-var
-  sw: string;
+function TRawAsyncServer.asyncupdates(ctxt: THttpServerRequest): cardinal;
 begin
-  // user specified some values at command line: raw [-s serversCount] [-t threadsPerServer] [-p]
-  if not FindCmdLineSwitchVal('t', sw) or not TryStrToInt(sw, threads) then
-    threads := cpusAccessible * 4;
-  if not FindCmdLineSwitchVal('s', sw) or not TryStrToInt(sw, servers) then
-    servers := 1;
-  pinServers2Cores := FindCmdLineSwitch('p', true) or FindCmdLineSwitch('-pin', true);
-  if threads < 1 then
-    threads := 1
-  else if threads > 256 then
-    threads := 256; // max. threads for THttpAsyncServer
-
-  if servers < 1 then
-    servers := 1
-  else if servers > 256 then
-    servers := 256;
+  result := TAsyncWorld.Create.Updates(self, ctxt);
 end;
 
-procedure ComputeExecutionContextFromNumberOfProcessors(cpusAccessible: PtrInt);
+
+{ TAsyncWorld }
+
+function TAsyncWorld.Queries(owner: TRawAsyncServer; ctxt: THttpServerRequest): cardinal;
+var
+  n: PtrInt;
+  opt: TSqlDBPostgresAsyncStatementOptions; // for modified libpq
 begin
-  // automatically guess best parameters depending on available CPU cores
-  if cpusAccessible >= 6 then
-  begin
-    // scale using several listeners (one per core)
-    // see https://synopse.info/forum/viewtopic.php?pid=39263#p39263
-    servers := cpusAccessible;
-    threads := 8;
-    pinServers2Cores := true;
-  end
-  else
-  begin
-    // low-level CPU - a single instance and a few threads per core
-    servers := 1;
-    threads := cpusAccessible * 4;
-    pinServers2Cores := false;
+  server := owner;
+  request := ctxt;
+  n := getQueriesParamValue(ctxt);
+  SetLength(res, n); // n is > 0
+  server.fAsyncWorldRead.Lock;
+  try
+    opt := server.fAsyncWorldRead.AsyncOptions - [asoForceConnectionFlush];
+    repeat
+      dec(n);
+      server.fAsyncWorldRead.Bind(1, ComputeRandomWorld);
+      if n = 0 then // last item
+        opt := server.fAsyncWorldRead.AsyncOptions;
+      server.fAsyncWorldRead.ExecuteAsync(ctxt, OnQueries, @opt);
+    until n = 0;
+  finally
+    server.fAsyncWorldRead.UnLock;
   end;
+  result := ctxt.SetAsyncResponse;
+end;
+
+function TAsyncWorld.Updates(owner: TRawAsyncServer;
+  ctxt: THttpServerRequest): cardinal;
+begin
+  fromupdates := true;
+  result := Queries(owner, ctxt);
 end;
 
+procedure TAsyncWorld.OnQueries(Statement: TSqlDBPostgresAsyncStatement;
+  Context: TObject);
+begin
+  if (Statement <> nil) and
+     Statement.Step then
+    with res[count] do
+    begin
+      id := Statement.ColumnInt(0);
+      randomNumber := Statement.ColumnInt(1);
+    end;
+  inc(count);
+  if count = length(res) then // we retrieved all SELECT
+    if fromupdates then
+      DoUpdates
+    else
+      OnRes(Statement, Context);
+end;
+
+procedure TAsyncWorld.DoUpdates;
+var
+  i: PtrInt;
+  ids, nums: TInt64DynArray;
 begin
-  if FindCmdLineSwitch('?') or FindCmdLineSwitch('h') or FindCmdLineSwitch('-help', ['-'], false) then
+  setLength(ids{%H-}, count);
+  setLength(nums{%H-}, count);
+  for i := 0 to count - 1 do
+  with res[i] do
   begin
-    writeln('Usage: ' + UTF8ToString(ExeVersion.ProgramName) + ' [-s serversCount] [-t threadsPerServer] [-p]');
-    writeln('Options:');
-    writeln('  -?, --help            displays this message');
-    writeln('  -s  serversCount      count of servers (listener sockets)');
-    writeln('  -t  threadsPerServer  per-server thread poll size');
-    writeln('  -p, --pin             pin each server to CPU starting from 0');
-    exit;
+    randomNumber := ComputeRandomWorld;
+    ids[i] := id;
+    nums[i] := randomNumber;
   end;
+  // note: no need of server.fAsyncWorldUpdate.Lock/UnLock inside the callbacks
+  server.fAsyncWorldUpdate.BindArray(1, ids);
+  server.fAsyncWorldUpdate.BindArray(2, nums);
+  server.fAsyncWorldUpdate.ExecuteAsync(request, OnRes);
+end;
+
+procedure TAsyncWorld.OnRes(Statement: TSqlDBPostgresAsyncStatement;
+  Context: TObject);
+begin
+  request.SetOutJson(@res, TypeInfo(TWorlds));
+  request.OnAsyncResponse(Context as THttpServerRequest);
+  Free; // we don't need this state machine any more
+end;
 
+
+var
+  rawServers: array of TRawAsyncServer;
+  threads, servers, i, k, cpuIdx, cpuCount: integer;
+  pinServers2Cores: boolean;
+  cpuMask: TCpuSet;
+  flags: THttpServerOptions;
+begin
   // setup logs
   {$ifdef WITH_LOGS}
   TSynLog.Family.Level := LOG_VERBOSE; // disable logs for benchmarking
@@ -616,24 +714,61 @@ begin
     TypeInfo(TWorldRec),   'id,randomNumber:integer',
     TypeInfo(TFortune),    'id:integer message:RawUtf8']);
 
-  // setup execution context
-  accessibleCPUCount := CurrentCpuSet(cpuMask);
-
-  if ParamCount > 0 then
-    ComputeExecutionContextFromParams(accessibleCPUCount)
+  // compute default execution context from HW information
+  cpuCount := CurrentCpuSet(cpuMask); // may run from a "taskset" command
+  if cpuCount >= 6 then
+  begin
+    // high-end CPU would scale better using several listeners (one per core)
+    // see https://synopse.info/forum/viewtopic.php?pid=39263#p39263
+    servers := cpuCount;
+    threads := 8;
+    pinServers2Cores := true;
+    if GetEnvironmentVariable('TFB_TEST_NAME') = 'mormot-postgres-async' then
+    begin
+      // asynchronus test
+      servers := cpuCount * 2;
+      threads := 1;
+    end;
+  end
   else
-    ComputeExecutionContextFromNumberOfProcessors(accessibleCPUCount);
-  flags := [];
-  if servers > 1 then
-    include(flags, hsoReusePort); // allow several bindings on the same port
+  begin
+    // simple CPU will have a single instance and a few threads per core
+    servers := 1;
+    threads := cpuCount * 4;
+    pinServers2Cores := false;
+  end;
+
+  // parse command line parameters
+  with Executable.Command do
+  begin
+    ExeDescription := 'TFB Server using mORMot 2';
+    if Option(['p', 'pin'], 'pin each server to a CPU') then
+      pinServers2Cores := true;
+    if Option('nopin', 'disable the CPU pinning') then
+      pinServers2Cores := false; // no option would keep the default boolean
+    Get(['s', 'servers'], servers, '#count of servers (listener sockets)', servers);
+    Get(['t', 'threads'], threads, 'per-server thread pool #size', threads);
+    if Option(['?', 'help'], 'display this message') then
+    begin
+      ConsoleWrite(FullDescription);
+      exit;
+    end;
+    if ConsoleWriteUnknown then
+      exit;
+  end;
 
   // start the server instance(s), in hsoReusePort mode if needed
+  flags := [];
+  if servers > 1 then
+    include(flags, hsoReusePort) // allow several bindings on the same port
+  else
+    pinServers2Cores := false;   // don't make any sense
   SetLength(rawServers{%H-}, servers);
   cpuIdx := -1; // do not pin to CPU by default
   for i := 0 to servers - 1 do begin
     if pinServers2Cores then
     begin
-      k := i mod accessibleCPUCount;
+      k := i mod cpuCount;
       cpuIdx := -1;
       // find real CPU index according to the cpuMask
       repeat
@@ -641,7 +776,7 @@ begin
         if GetBit(cpuMask, cpuIdx) then
           dec(k);
       until k = -1;
-      writeln('Pin server #', i, ' to #', cpuIdx, ' CPU');
+      writeln('Pin #', i, ' server to #', cpuIdx, ' CPU');
     end;
     rawServers[i] := TRawAsyncServer.Create(threads, flags, cpuIdx)
   end;
@@ -651,31 +786,32 @@ begin
     writeln;
     writeln(rawServers[0].fHttpServer.ClassName,
      ' running on localhost:', rawServers[0].fHttpServer.SockPort);
-    writeln(' num thread=', threads,
+    writeln(' num servers=', servers,
+            ', threads per server=', threads,
+            ', total threads=', threads * servers,
             ', total CPU=', SystemInfo.dwNumberOfProcessors,
-            ', accessible CPU=', accessibleCPUCount,
-            ', num servers=', servers,
+            ', accessible CPU=', cpuCount,
             ', pinned=', pinServers2Cores,
-            ', total workers=', threads * servers,
             ', db=', rawServers[0].fDbPool.DbmsEngineName);
     writeln(' options=', GetSetName(TypeInfo(THttpServerOptions), flags));
-    writeln('Press [Enter] or Ctrl+C or send SIGTERM to terminate'#10);
+    writeln('Press [Enter] or Ctrl+C or send SIGTERM to terminate');
     ConsoleWaitForEnterKey;
     //TSynLog.Family.Level := LOG_VERBOSE; // enable shutdown logs for debug
     if servers = 1 then
-      writeln(ObjectToJsonDebug(rawServers[i].fHttpServer,
+      writeln(ObjectToJsonDebug(rawServers[0].fHttpServer,
         [woDontStoreVoid, woHumanReadable]))
     else
     begin
       writeln('Per-server accepted connections:');
       for i := 0 to servers - 1 do
-        write(rawServers[i].fHttpServer.Async.Accepted, ' ');
-      writeln;
+        write(' ', rawServers[i].fHttpServer.Async.Accepted);
+      writeln(#10'Please wait: Shutdown ', servers, ' servers');
     end;
   finally
     // clear all server instance(s)
     ObjArrayClear(rawServers);
   end;
+  write('Shutdown complete'#10);
   {$ifdef FPC_X64MM}
   WriteHeapStatus(' ', 16, 8, {compileflags=}true);
   {$endif FPC_X64MM}

+ 3 - 0
frameworks/Python/falcon/app.py

@@ -140,6 +140,8 @@ if __name__ == "__main__":
         logging.basicConfig()
         logging.getLogger().setLevel(logging.CRITICAL)
         logging.disable(True)
+        if workers < 4:
+            workers = 4
         waitress.serve(
             app=wsgi,
             listen=f"{opt.host}:{opt.port}",
@@ -170,6 +172,7 @@ if __name__ == "__main__":
             import fastwsgi
             response_server = "FastWSGI"
             response_add_date = False
+            fastwsgi.server.backlog = 4096
             fastwsgi.run(app, host=opt.host, port=opt.port, loglevel=opt.verbose)
 
         if opt.server == 'socketify':

+ 0 - 2
frameworks/Python/falcon/benchmark_config.json

@@ -151,8 +151,6 @@
       "db_url": "/db",
       "query_url": "/queries/",
       "update_url": "/updates/",
-      "fortune_url": "/fortunes",
-      "plaintext_url": "/plaintext",
       "port": 8080,
       "approach": "Realistic",
       "classification": "Micro",

+ 0 - 2
frameworks/Python/falcon/config.toml

@@ -75,8 +75,6 @@ json.url = "/json"
 db.url = "/db"
 query.url = "/queries/"
 update.url = "/updates/"
-fortune.url = "/fortunes"
-plaintext.url = "/plaintext"
 port = 8080
 approach = "Realistic"
 classification = "Micro"

+ 7 - 5
frameworks/Python/falcon/falcon-asgi.dockerfile

@@ -1,12 +1,14 @@
-FROM python:3.9-bullseye
+FROM python:3.10-bullseye
 
 RUN apt-get update; apt-get install libpq-dev python3-dev -y
 WORKDIR /falcon
 COPY ./ /falcon
-RUN pip3 install -U pip; \
-    pip3 install cython==0.29.26; \
-    pip3 install -r /falcon/requirements.txt; \
-    pip3 install falcon==3.1.1 --no-binary :all:;
+RUN pip3 install -U pip
+RUN pip3 install cython==0.29.26
+#RUN pip3 install falcon==3.1.1 --no-binary :all:
+RUN pip3 install -r /falcon/requirements.txt
+RUN pip3 install -r /falcon/requirements-uvicorn.txt
+RUN pip3 install -r /falcon/requirements-db-tortoise.txt
 
 ENV ASYNCIO=true
 

+ 4 - 1
frameworks/Python/falcon/falcon-bjoern.dockerfile

@@ -3,7 +3,10 @@ FROM python:3.9-bullseye
 RUN apt-get update; apt-get install libpq-dev python3-dev libev-dev -y
 WORKDIR /falcon
 COPY ./ /falcon
-RUN pip3 install -U pip; pip3 install -r /falcon/requirements-bjoern.txt
+RUN pip3 install -U pip
+RUN pip3 install -r /falcon/requirements.txt
+RUN pip3 install -r /falcon/requirements-bjoern.txt
+RUN pip3 install -r /falcon/requirements-db-pony.txt
 
 EXPOSE 8080
 

+ 5 - 2
frameworks/Python/falcon/falcon-fastwsgi.dockerfile

@@ -1,9 +1,12 @@
-FROM python:3.9-bullseye
+FROM python:3.11-bullseye
 
 RUN apt-get update; apt-get install libpq-dev python3-dev -y
 WORKDIR /falcon
 COPY ./ /falcon
-RUN pip3 install -U pip; pip3 install -r /falcon/requirements-fastwsgi.txt
+RUN pip3 install -U pip
+RUN pip3 install -r /falcon/requirements.txt
+RUN pip3 install -r /falcon/requirements-fastwsgi.txt
+RUN pip3 install -r /falcon/requirements-db-pony.txt
 
 EXPOSE 8080
 

+ 7 - 4
frameworks/Python/falcon/falcon-orjson.dockerfile

@@ -3,10 +3,13 @@ FROM python:3.9-bullseye
 RUN apt-get update; apt-get install libpq-dev python3-dev -y
 WORKDIR /falcon
 COPY ./ /falcon
-RUN pip3 install -U pip; \
-    pip3 install cython==0.29.26; \
-    pip3 install -r /falcon/requirements.txt; \
-    pip3 install falcon==3.1.1 --no-binary :all:;
+RUN pip3 install -U pip
+RUN pip3 install cython==0.29.26
+RUN pip3 install orjson==3.8.10
+#RUN pip3 install falcon==3.1.1 --no-binary :all:
+RUN pip3 install -r /falcon/requirements.txt
+RUN pip3 install -r /falcon/requirements-meinheld.txt
+RUN pip3 install -r /falcon/requirements-db-pony.txt
 
 EXPOSE 8080
 

+ 4 - 1
frameworks/Python/falcon/falcon-pypy3.dockerfile

@@ -3,7 +3,10 @@ FROM pypy:3.9-bullseye
 RUN apt-get update; apt-get install libpq-dev python3-dev -y
 COPY ./ /falcon
 WORKDIR /falcon
-RUN pip3 install -U pip; pip3 install -r /falcon/requirements-pypy.txt
+RUN pip3 install -U pip
+RUN pip3 install -r /falcon/requirements.txt
+RUN pip3 install gunicorn==20.1.0
+RUN pip3 install -r /falcon/requirements-db-pony.txt
 
 EXPOSE 8080
 

+ 3 - 1
frameworks/Python/falcon/falcon-socketify-asgi-pypy3.dockerfile

@@ -3,7 +3,9 @@ FROM pypy:3.9-bullseye
 RUN apt-get update; apt-get install libpq-dev python3-dev libuv1-dev -y
 COPY ./ /falcon
 WORKDIR /falcon
-RUN pip3 install -U pip; pip3 install -r /falcon/requirements-socketify-pypy.txt
+RUN pip3 install -U pip
+RUN pip3 install -r /falcon/requirements.txt
+RUN pip3 install -r /falcon/requirements-socketify.txt
 
 EXPOSE 8080
 

+ 6 - 5
frameworks/Python/falcon/falcon-socketify-asgi.dockerfile

@@ -1,12 +1,13 @@
-FROM python:3.9-bullseye
+FROM python:3.11-bullseye
 
 RUN apt-get update; apt-get install libpq-dev python3-dev libuv1-dev -y
 COPY ./ /falcon
 WORKDIR /falcon
-RUN pip3 install -U pip; \
-    pip3 install cython==0.29.26; \
-    pip3 install falcon==3.1.1 --no-binary :all:; \
-    pip3 install -r /falcon/requirements-socketify.txt
+RUN pip3 install -U pip
+RUN pip3 install cython==0.29.26
+#RUN pip3 install falcon==3.1.1 --no-binary :all:
+RUN pip3 install -r /falcon/requirements.txt
+RUN pip3 install -r /falcon/requirements-socketify.txt
 
 EXPOSE 8080
 

+ 4 - 1
frameworks/Python/falcon/falcon-socketify-wsgi-pypy3.dockerfile

@@ -3,7 +3,10 @@ FROM pypy:3.9-bullseye
 RUN apt-get update; apt-get install libpq-dev python3-dev libuv1-dev -y
 COPY ./ /falcon
 WORKDIR /falcon
-RUN pip3 install -U pip; pip3 install -r /falcon/requirements-socketify-pypy.txt
+RUN pip3 install -U pip
+RUN pip3 install -r /falcon/requirements.txt
+RUN pip3 install -r /falcon/requirements-socketify.txt
+RUN pip3 install -r /falcon/requirements-db-pony.txt
 
 EXPOSE 8080
 

+ 7 - 5
frameworks/Python/falcon/falcon-socketify-wsgi.dockerfile

@@ -1,12 +1,14 @@
-FROM python:3.9-bullseye
+FROM python:3.11-bullseye
 
 RUN apt-get update; apt-get install libpq-dev python3-dev libuv1-dev -y
 COPY ./ /falcon
 WORKDIR /falcon
-RUN pip3 install -U pip; \
-    pip3 install cython==0.29.26; \
-    pip3 install falcon==3.1.1 --no-binary :all:; \
-    pip3 install -r /falcon/requirements-socketify.txt
+RUN pip3 install -U pip
+RUN pip3 install cython==0.29.26
+#RUN pip3 install falcon==3.1.1 --no-binary :all:
+RUN pip3 install -r /falcon/requirements.txt
+RUN pip3 install -r /falcon/requirements-socketify.txt
+RUN pip3 install -r /falcon/requirements-db-pony.txt
 
 EXPOSE 8080
 

+ 4 - 1
frameworks/Python/falcon/falcon-waitress.dockerfile

@@ -3,7 +3,10 @@ FROM python:3.9-bullseye
 RUN apt-get update; apt-get install libpq-dev python3-dev -y
 WORKDIR /falcon
 COPY ./ /falcon
-RUN pip3 install -U pip; pip3 install -r /falcon/requirements.txt
+RUN pip3 install -U pip
+RUN pip3 install -r /falcon/requirements.txt
+RUN pip3 install -r /falcon/requirements-waitress.txt
+RUN pip3 install -r /falcon/requirements-db-pony.txt
 
 EXPOSE 8080
 

+ 6 - 4
frameworks/Python/falcon/falcon.dockerfile

@@ -3,10 +3,12 @@ FROM python:3.9-bullseye
 RUN apt-get update; apt-get install libpq-dev python3-dev -y
 WORKDIR /falcon
 COPY ./ /falcon
-RUN pip3 install -U pip; \
-    pip3 install cython==0.29.26; \
-    pip3 install -r /falcon/requirements.txt; \
-    pip3 install falcon==3.1.1 --no-binary :all:;
+RUN pip3 install -U pip
+RUN pip3 install cython==0.29.26
+#RUN pip3 install falcon==3.1.1 --no-binary :all:
+RUN pip3 install -r /falcon/requirements.txt
+RUN pip3 install -r /falcon/requirements-meinheld.txt
+RUN pip3 install -r /falcon/requirements-db-pony.txt
 
 EXPOSE 8080
 

+ 2 - 5
frameworks/Python/falcon/helpers.py

@@ -1,7 +1,7 @@
 import jinja2
 from pathlib import Path
 from collections import namedtuple
-from random import randint
+import random
 
 
 def sanitize(query):
@@ -19,10 +19,7 @@ def sanitize(query):
 
 
 def generate_ids(num_queries):
-    ids = {randint(1, 10000) for _ in range(num_queries)}
-    while len(ids) < num_queries:
-        ids.add(randint(1, 10000))
-    return list(sorted(ids))
+    return random.sample(range(1, 10000), num_queries)
 
 
 def load_template():

+ 0 - 1
frameworks/Python/falcon/requirements-bjoern.txt

@@ -1,2 +1 @@
--r requirements.txt
 bjoern==3.1.0

+ 3 - 0
frameworks/Python/falcon/requirements-db-pony.txt

@@ -0,0 +1,3 @@
+pony==0.7.16
+psycopg2-binary==2.9.3; implementation_name=='cpython'
+psycopg2cffi==2.9.0; implementation_name=='pypy'

+ 3 - 0
frameworks/Python/falcon/requirements-db-tortoise.txt

@@ -0,0 +1,3 @@
+asyncpg==0.27.0
+orjson==3.8.10
+tortoise-orm==0.19.3

+ 0 - 1
frameworks/Python/falcon/requirements-fastwsgi.txt

@@ -1,3 +1,2 @@
--r requirements.txt
 click==8.0.1
 fastwsgi==0.0.9

+ 2 - 0
frameworks/Python/falcon/requirements-meinheld.txt

@@ -0,0 +1,2 @@
+gunicorn==20.1.0
+meinheld==1.0.2

+ 0 - 4
frameworks/Python/falcon/requirements-pypy.txt

@@ -1,5 +1 @@
-falcon==3.1.1
 gunicorn==20.1.0
-jinja2==3.0.3
-pony==0.7.16
-psycopg2cffi==2.9.0; implementation_name=='pypy'

+ 0 - 1
frameworks/Python/falcon/requirements-socketify-pypy.txt

@@ -1,2 +1 @@
--r requirements-pypy.txt
 git+https://github.com/cirospaciari/socketify.py.git@main#socketify

+ 0 - 1
frameworks/Python/falcon/requirements-socketify.txt

@@ -1,2 +1 @@
--r requirements.txt
 git+https://github.com/cirospaciari/socketify.py.git@main#socketify

+ 4 - 0
frameworks/Python/falcon/requirements-uvicorn.txt

@@ -0,0 +1,4 @@
+gunicorn==20.1.0
+httptools==0.5.0
+uvloop==0.17.0
+uvicorn==0.21.1

+ 1 - 0
frameworks/Python/falcon/requirements-waitress.txt

@@ -0,0 +1 @@
+waitress==2.1.2

+ 0 - 12
frameworks/Python/falcon/requirements.txt

@@ -1,14 +1,2 @@
-asyncpg==0.27.0
-Cython==0.29.26
 falcon==3.1.1
-gunicorn==20.1.0
 jinja2==3.0.3
-meinheld==1.0.2
-orjson==3.6.5
-pony==0.7.16
-psycopg2-binary==2.9.3; implementation_name=='cpython'
-tortoise-orm==0.19.2
-httptools==0.5.0
-uvloop==0.17.0
-uvicorn==0.20.0
-waitress==2.1.2

+ 280 - 0
frameworks/Python/fastwsgi/app-asgi.py

@@ -0,0 +1,280 @@
+import os
+import sys
+import asyncio
+import asyncpg
+import jinja2
+import random
+from operator import itemgetter
+from urllib.parse import parse_qs
+import asyncache
+import cachetools
+
+try:
+    from ujson import dumps as jsonify
+except:
+    from json import dumps as jsonify
+
+
+db_pool = None
+
+async def db_setup():
+    global db_pool
+    db_pool = await asyncpg.create_pool(
+        user=os.getenv('PGUSER', 'benchmarkdbuser'),
+        password=os.getenv('PGPASS', 'benchmarkdbpass'),
+        database='hello_world',
+        host='tfb-database',
+        port=5432
+    )
+
+READ_ROW_SQL = 'SELECT "randomnumber", "id" FROM "world" WHERE id = $1'
+WRITE_ROW_SQL = 'UPDATE "world" SET "randomnumber"=$1 WHERE id=$2'
+ADDITIONAL_ROW = [0, 'Additional fortune added at request time.']
+
+JSON_RESPONSE = {
+    'type': 'http.response.start',
+    'status': 200,
+    'headers': [
+        [b'Content-Type', b'application/json'],
+    ]
+}
+
+HTML_RESPONSE = {
+    'type': 'http.response.start',
+    'status': 200,
+    'headers': [
+        [b'Content-Type', b'text/html; charset=utf-8'],
+    ]
+}
+
+PLAINTEXT_RESPONSE = {
+    'type': 'http.response.start',
+    'status': 200,
+    'headers': [
+        [b'Content-Type', b'text/plain; charset=utf-8'],
+    ]
+}
+
+
+def get_num_queries(scope, name = b'queries'):
+    try:
+        query_string = scope['query_string']
+        query_count = int(parse_qs(query_string)[name][0])
+    except (KeyError, IndexError, ValueError):
+        return 1
+    if query_count < 1:
+        return 1
+    if query_count > 500:
+        return 500
+    return query_count
+
+
+async def json_serialization(scope, receive, send):
+    content = jsonify( {'message': 'Hello, world!'} )
+    await send(JSON_RESPONSE)
+    await send({
+        'type': 'http.response.body',
+        'body': content.encode('utf8'),
+        'more_body': False
+    })
+
+
+async def single_database_query(scope, receive, send):
+    row_id = random.randint(1, 10000)
+    db_conn = await db_pool.acquire()
+    try:
+        number = await db_conn.fetchval(READ_ROW_SQL, row_id)
+        world = {'id': row_id, 'randomNumber': number}
+    finally:
+        await db_pool.release(db_conn)
+
+    content = jsonify(world)
+    await send(JSON_RESPONSE)
+    await send({
+        'type': 'http.response.body',
+        'body': content.encode('utf8'),
+        'more_body': False
+    })
+
+
+async def multiple_database_queries(scope, receive, send):
+    num_queries = get_num_queries(scope)
+    row_ids = random.sample(range(1, 10000), num_queries)
+    worlds = [ ]
+
+    db_conn = await db_pool.acquire()
+    try:
+        statement = await db_conn.prepare(READ_ROW_SQL)
+        for row_id in row_ids:
+            number = await statement.fetchval(row_id)
+            worlds.append( {'id': row_id, 'randomNumber': number} )
+    finally:
+        await db_pool.release(db_conn)
+
+    content = jsonify(worlds)
+    await send(JSON_RESPONSE)
+    await send({
+        'type': 'http.response.body',
+        'body': content.encode('utf8'),
+        'more_body': False
+    })
+
+
+_get_item1 = itemgetter(1)
+fortunes_template = None
+fn = os.path.join('templates', 'fortune.html')
+with open(fn, 'r') as file:
+    text = file.read()
+    fortunes_template = jinja2.Template(text)
+
+
+async def fortunes(scope, receive, send):
+    db_conn = await db_pool.acquire()
+    try:
+        fortunes = await db_conn.fetch('SELECT * FROM Fortune')
+    finally:
+        await db_pool.release(db_conn)
+
+    fortunes.append(ADDITIONAL_ROW)
+    fortunes.sort(key = _get_item1)
+    content = fortunes_template.render(fortunes=fortunes)
+    await send(HTML_RESPONSE)
+    await send({
+        'type': 'http.response.body',
+        'body': content.encode('utf8'),
+        'more_body': False
+    })
+
+
+async def database_updates(scope, receive, send):
+    num_queries = get_num_queries(scope)
+    ids = sorted(random.sample(range(1, 10000 + 1), num_queries))
+    numbers = sorted(random.sample(range(1, 10000), num_queries))
+    updates = list(zip(ids, numbers))
+    
+    worlds = [ {"id": row_id, "randomNumber": number} for row_id, number in updates ]
+
+    db_conn = await db_pool.acquire()
+    try:
+        statement = await db_conn.prepare(READ_ROW_SQL)
+        for row_id, _ in updates:
+            await statement.fetchval(row_id)
+        await db_conn.executemany(WRITE_ROW_SQL, updates)
+    finally:
+        await db_pool.release(db_conn)
+
+    content = jsonify(worlds)
+    await send(JSON_RESPONSE)
+    await send({
+        'type': 'http.response.body',
+        'body': content.encode('utf8'),
+        'more_body': False
+    })
+
+
+from asyncache import cached
+from cachetools.keys import hashkey
+
+@cached(cache={}, key=lambda stmt, id: hashkey(id))
+async def get_cached_world(stmt, id):
+    result = await stmt.fetchrow(id)
+    return {'id': result[1], 'randomNumber': result[0]}
+
+async def cached_queries(scope, receive, send):
+    count = get_num_queries(scope, b'count')
+    row_ids = random.sample(range(1, 10000 + 1), count)
+    
+    db_conn = await db_pool.acquire()
+    try:
+        statement = await db_conn.prepare(READ_ROW_SQL)
+        worlds = [ await get_cached_world(statement, id) for id in row_ids ]
+    finally:
+        await db_pool.release(db_conn)
+
+    content = jsonify(worlds)
+    await send(JSON_RESPONSE)
+    await send({
+        'type': 'http.response.body',
+        'body': content.encode('utf8'),
+        'more_body': False
+    })
+
+
+async def plaintext(scope, receive, send):
+    await send(PLAINTEXT_RESPONSE)
+    await send({
+        'type': 'http.response.body',
+        'body': b'Hello, world!',
+        'more_body': False
+    })
+
+
+async def handle_404(scope, receive, send):
+    await send(PLAINTEXT_RESPONSE)
+    await send({
+        'type': 'http.response.body',
+        'body': b'Not found',
+        'more_body': False
+    })
+
+
+routes = {
+    '/json': json_serialization,
+    '/db': single_database_query,
+    '/queries': multiple_database_queries,
+    '/fortunes': fortunes,
+    '/updates': database_updates,
+    '/cached-queries': cached_queries,
+    '/plaintext': plaintext,
+}
+
+
+async def app(scope, receive, send):
+    if scope['type'] == 'lifespan':
+        global db_pool
+        while True:
+            message = await receive()
+            if message['type'] == 'lifespan.startup':
+                await db_setup()
+                await send({'type': 'lifespan.startup.complete'})
+            elif message['type'] == 'lifespan.shutdown':
+                db_pool.close()
+                await send({'type': 'lifespan.shutdown.complete'})
+                return            
+    else:
+        path = scope['path']
+        app_handler = routes.get(path, handle_404)
+        await app_handler(scope, receive, send)
+
+# -----------------------------------------------------------------------------------------------------
+
+if __name__ == "__main__":
+    import multiprocessing
+    import fastwsgi
+
+    _is_travis = os.environ.get('TRAVIS') == 'true'
+
+    workers = int(multiprocessing.cpu_count())
+    if _is_travis:
+        workers = 2
+
+    host = '0.0.0.0'
+    port = 3000
+
+    def run_app():
+        loop = asyncio.get_event_loop()
+        loop.run_until_complete(db_setup())
+        fastwsgi.server.backlog = 4096
+        fastwsgi.run(app, host, port, loglevel=2)
+
+    def create_fork():
+        n = os.fork()
+        # n greater than 0 means parent process
+        if not n > 0:
+            run_app()
+
+    # fork limiting the cpu count - 1
+    for i in range(1, workers):
+        create_fork()
+
+    run_app()  # run app on the main process too :)

+ 4 - 2
frameworks/Python/fastwsgi/app.py

@@ -9,7 +9,8 @@ except:
 
 def app(environ, start_response):
     path = environ["PATH_INFO"]
-    headers = [ ('Server', 'FastWSGI') ]
+    headers = [ ]
+    #headers = [ ('Server', 'FastWSGI') ]
     
     if path == "/json":
         headers.append( ('Content-Type', 'application/json') )
@@ -38,7 +39,8 @@ if __name__ == "__main__":
     port = 3000
 
     def run_app():
-        fastwsgi.run(app, host, port, loglevel=0)
+        fastwsgi.server.backlog = 4096
+        fastwsgi.run(app, host, port, loglevel=2)
 
     def create_fork():
         n = os.fork()

+ 25 - 1
frameworks/Python/fastwsgi/benchmark_config.json

@@ -12,11 +12,35 @@
             "language": "Python",
             "flavor": "Python3",
             "orm": "Raw",
-            "platform": "None",
+            "platform": "WSGI",
             "webserver": "FastWSGI",
             "os": "Linux",
             "database_os": "Linux",
             "display_name": "FastWSGI",
+            "notes": "",
+            "versus": "wsgi"
+        },
+        "asgi": {
+            "json_url": "/json",
+            "plaintext_url": "/plaintext",
+            "db_url": "/db",
+            "query_url": "/queries?queries=",
+            "update_url": "/updates?queries=",
+            "cached_query_url": "/cached-queries?count=",
+            "fortune_url": "/fortunes",
+            "port": 3000,
+            "approach": "Realistic",
+            "classification": "Micro",
+            "database": "Postgres",
+            "framework": "None",
+            "language": "Python",
+            "flavor": "Python3",
+            "orm": "Raw",
+            "platform": "ASGI",
+            "webserver": "FastWSGI",
+            "os": "Linux",
+            "database_os": "Linux",
+            "display_name": "FastWSGI [ASGI]",
             "notes": ""
         }
     }]

+ 19 - 1
frameworks/Python/fastwsgi/config.toml

@@ -10,6 +10,24 @@ database = "None"
 database_os = "Linux"
 os = "Linux"
 orm = "Raw"
-platform = "None"
+platform = "WSGI"
+webserver = "FastWSGI"
+versus = "wsgi"
+
+[asgi]
+urls.json = "/json"
+urls.plaintext = "/plaintext"
+urls.db = "/db"
+urls.query = "/queries?queries="
+urls.update = "/updates?queries="
+urls.cached_query = "/cached-queries?count="
+urls.fortune = "/fortunes"
+approach = "Realistic"
+classification = "Platform"
+database = "Postgres"
+database_os = "Linux"
+os = "Linux"
+orm = "Raw"
+platform = "ASGI"
 webserver = "FastWSGI"
 versus = "None"

+ 13 - 0
frameworks/Python/fastwsgi/fastwsgi-asgi.dockerfile

@@ -0,0 +1,13 @@
+FROM python:3.11-bullseye
+
+WORKDIR /usr/src/app
+
+COPY requirements.txt ./
+RUN pip3 install -U pip
+RUN pip3 install --no-cache-dir -r requirements.txt
+
+COPY . .
+
+EXPOSE 3000
+
+CMD python ./app-asgi.py

+ 2 - 3
frameworks/Python/fastwsgi/fastwsgi.dockerfile

@@ -3,9 +3,8 @@ FROM python:3.11-bullseye
 WORKDIR /usr/src/app
 
 COPY requirements.txt ./
-RUN apt-get update
-RUN pip install --no-cache-dir ujson
-RUN pip install --no-cache-dir -r requirements.txt
+RUN pip3 install -U pip
+RUN pip3 install --no-cache-dir -r requirements.txt
 
 COPY . .
 

+ 6 - 1
frameworks/Python/fastwsgi/requirements.txt

@@ -1,3 +1,8 @@
 click==8.0.1
 ujson==5.7.0
-fastwsgi==0.0.9
+#fastwsgi==0.0.9
+git+https://github.com/remittor-pr/fastwsgi.git@6093fceaac44ffdfaffd577dd2b04ad4a4c6e3bf
+asyncpg==0.27.0
+Jinja2==3.1.2
+cachetools==5.3.0
+asyncache==0.3.1

+ 10 - 0
frameworks/Python/fastwsgi/templates/fortune.html

@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html>
+<head><title>Fortunes</title></head>
+<body>
+<table>
+<tr><th>id</th><th>message</th></tr>
+{% for fortune in fortunes %}<tr><td>{{ fortune[0] }}</td><td>{{ fortune[1]|e }}</td></tr>
+{% endfor %}</table>
+</body>
+</html>

+ 1 - 0
frameworks/Python/flask/app.py

@@ -306,6 +306,7 @@ if __name__ == "__main__":
             import fastwsgi
             response_server = "FastWSGI"
             response_add_date = False
+            fastwsgi.server.backlog = 4096
             fastwsgi.run(app, host=opt.host, port=opt.port, loglevel=opt.verbose)
 
         if opt.server == 'socketify':

+ 0 - 4
frameworks/Python/flask/benchmark_config.json

@@ -26,12 +26,10 @@
       "tags": [ ]
     },
     "raw": {
-      "json_url": "/json-raw",
       "db_url": "/db-raw",
       "query_url": "/query-raw?queries=",
       "fortune_url": "/fortunes-raw",
       "update_url": "/updates-raw?queries=",
-      "plaintext_url": "/plaintext",
       "port": 8080,
       "approach": "Realistic",
       "classification": "Micro",
@@ -112,12 +110,10 @@
       "tags": ["broken"]
     },
     "pypy-raw": {
-      "json_url": "/json-raw",
       "db_url": "/db-raw",
       "query_url": "/query-raw?queries=",
       "fortune_url": "/fortunes-raw",
       "update_url": "/updates-raw?queries=",
-      "plaintext_url": "/plaintext",
       "port": 8080,
       "approach": "Realistic",
       "classification": "Micro",

+ 2 - 2
frameworks/Python/flask/nginx.conf

@@ -6,7 +6,7 @@ error_log stderr error;
 events {
     # This needed to be increased because the nginx error log said so.
     # http://nginx.org/en/docs/ngx_core_module.html#worker_connections
-    worker_connections  4095;
+    worker_connections  65535;
     multi_accept on;
 }
 
@@ -37,7 +37,7 @@ http {
         # http://nginx.org/en/docs/http/ngx_http_core_module.html#listen
         # http://www.techrepublic.com/article/take-advantage-of-tcp-ip-options-to-optimize-data-transmission/
         # The backlog argument to listen() is set to match net.ipv4.tcp_max_syn_backlog and net.core.somaxconn
-        listen       8080 default_server deferred reuseport backlog=4095;
+        listen       8080 default_server deferred reuseport backlog=65535;
         server_name  localhost;
 
         location / {

+ 1 - 1
frameworks/Python/flask/uwsgi.ini

@@ -2,7 +2,7 @@
 master
 ; Increase listen queue used for nginx connecting to uWSGI. This matches
 ; net.ipv4.tcp_max_syn_backlog and net.core.somaxconn.
-listen = 4096
+listen = 16384
 ; for performance
 disable-logging
 ; use UNIX sockets instead of TCP loopback for performance

+ 0 - 4
frameworks/Python/tornado/benchmark_config.json

@@ -48,10 +48,8 @@
       "versus": "tornado"
     },
     "postgresql-raw": {
-      "json_url": "/json",
       "db_url": "/db",
       "query_url": "/queries?queries=",
-      "plaintext_url": "/plaintext",
       "port": 8080,
       "approach": "Realistic",
       "classification": "Platform",
@@ -93,10 +91,8 @@
       "versus": "tornado"
     },
     "py3-uvloop": {
-      "json_url": "/json",
       "db_url": "/db",
       "query_url": "/queries?queries=",
-      "plaintext_url": "/plaintext",
       "fortune_url": "/fortunes",
       "update_url": "/updates?queries=",
       "port": 8080,

+ 0 - 16
frameworks/Ruby/sinatra-sequel/benchmark_config.json

@@ -3,12 +3,10 @@
   "tests": [
     {
       "default": {
-        "json_url": "/json",
         "db_url": "/db",
         "query_url": "/queries?queries=",
         "fortune_url": "/fortunes",
         "update_url": "/updates?queries=",
-        "plaintext_url": "/plaintext",
         "port": 8080,
         "approach": "Realistic",
         "classification": "Micro",
@@ -25,12 +23,10 @@
         "notes": ""
       },
       "postgres": {
-        "json_url": "/json",
         "db_url": "/db",
         "query_url": "/queries?queries=",
         "fortune_url": "/fortunes",
         "update_url": "/updates?queries=",
-        "plaintext_url": "/plaintext",
         "port": 8080,
         "approach": "Realistic",
         "classification": "Micro",
@@ -47,12 +43,10 @@
         "notes": ""
       },
       "passenger-mri": {
-        "json_url": "/json",
         "db_url": "/db",
         "query_url": "/queries?queries=",
         "fortune_url": "/fortunes",
         "update_url": "/updates?queries=",
-        "plaintext_url": "/plaintext",
         "port": 8080,
         "approach": "Realistic",
         "classification": "Micro",
@@ -69,12 +63,10 @@
         "notes": ""
       },
       "postgres-passenger-mri": {
-        "json_url": "/json",
         "db_url": "/db",
         "query_url": "/queries?queries=",
         "fortune_url": "/fortunes",
         "update_url": "/updates?queries=",
-        "plaintext_url": "/plaintext",
         "port": 8080,
         "approach": "Realistic",
         "classification": "Micro",
@@ -91,12 +83,10 @@
         "notes": ""
       },
       "unicorn-mri": {
-        "json_url": "/json",
         "db_url": "/db",
         "query_url": "/queries?queries=",
         "fortune_url": "/fortunes",
         "update_url": "/updates?queries=",
-        "plaintext_url": "/plaintext",
         "port": 8080,
         "approach": "Realistic",
         "classification": "Micro",
@@ -113,12 +103,10 @@
         "notes": ""
       },
       "postgres-unicorn-mri": {
-        "json_url": "/json",
         "db_url": "/db",
         "query_url": "/queries?queries=",
         "fortune_url": "/fortunes",
         "update_url": "/updates?queries=",
-        "plaintext_url": "/plaintext",
         "port": 8080,
         "approach": "Realistic",
         "classification": "Micro",
@@ -135,12 +123,10 @@
         "notes": ""
       },
       "torquebox-jruby": {
-        "json_url": "/json",
         "db_url": "/db",
         "query_url": "/queries?queries=",
         "fortune_url": "/fortunes",
         "update_url": "/updates?queries=",
-        "plaintext_url": "/plaintext",
         "port": 8080,
         "approach": "Realistic",
         "classification": "Micro",
@@ -157,12 +143,10 @@
         "notes": ""
       },
       "postgres-torquebox-jruby": {
-        "json_url": "/json",
         "db_url": "/db",
         "query_url": "/queries?queries=",
         "fortune_url": "/fortunes",
         "update_url": "/updates?queries=",
-        "plaintext_url": "/plaintext",
         "port": 8080,
         "approach": "Realistic",
         "classification": "Micro",

+ 0 - 6
frameworks/Ruby/sinatra/benchmark_config.json

@@ -25,12 +25,10 @@
         "notes": ""
       },
       "postgres": {
-        "json_url": "/json",
         "db_url": "/db",
         "query_url": "/queries?queries=",
         "fortune_url": "/fortunes",
         "update_url": "/updates?queries=",
-        "plaintext_url": "/plaintext",
         "port": 8080,
         "approach": "Realistic",
         "classification": "Micro",
@@ -69,12 +67,10 @@
         "notes": ""
       },
       "postgres-passenger-mri": {
-        "json_url": "/json",
         "db_url": "/db",
         "query_url": "/queries?queries=",
         "fortune_url": "/fortunes",
         "update_url": "/updates?queries=",
-        "plaintext_url": "/plaintext",
         "port": 8080,
         "approach": "Realistic",
         "classification": "Micro",
@@ -113,12 +109,10 @@
         "notes": ""
       },
       "postgres-unicorn-mri": {
-        "json_url": "/json",
         "db_url": "/db",
         "query_url": "/queries?queries=",
         "fortune_url": "/fortunes",
         "update_url": "/updates?queries=",
-        "plaintext_url": "/plaintext",
         "port": 8080,
         "approach": "Realistic",
         "classification": "Micro",

+ 1 - 3
frameworks/Rust/salvo/Cargo.toml

@@ -49,7 +49,7 @@ markup = "0.13"
 mongodb = { version = "2.4.0", features = ["zstd-compression", "snappy-compression", "zlib-compression"] }
 once_cell = "1"
 rand = { version = "0.8", features = ["min_const_gen", "small_rng"] }
-salvo = { version = "0.38", default-features = false, features = ["anyhow", "http1", "affix"] }
+salvo = { version = "0.40", default-features = false, features = ["anyhow", "http1", "affix"] }
 serde = { version = "1", features = ["derive"] }
 serde_json = "1"
 # smallvec = "1"
@@ -68,5 +68,3 @@ opt-level = 3
 codegen-units = 1
 panic = "abort"
 
-[patch.crates-io]
-salvo = { git = "https://github.com/salvo-rs/salvo.git" }

+ 198 - 73
frameworks/Rust/xitca-web/Cargo.lock

@@ -10,7 +10,7 @@ checksum = "b9ccdd8f2a161be9bd5c023df56f1b2a0bd1d83872ae53b71a84a12c9bf6e842"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.13",
+ "syn 2.0.15",
 ]
 
 [[package]]
@@ -75,9 +75,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
 
 [[package]]
 name = "cpufeatures"
-version = "0.2.6"
+version = "0.2.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "280a9f2d8b3a38871a3c8a46fb80db65e5e5ed97da80c4d08bf27fb63e35e181"
+checksum = "3e4c1eaa2012c47becbbad2ab175484c2a84d1185b566fb2cc5b8707343dfe58"
 dependencies = [
  "libc",
 ]
@@ -94,9 +94,9 @@ dependencies = [
 
 [[package]]
 name = "diesel"
-version = "2.0.3"
+version = "2.0.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4391a22b19c916e50bec4d6140f29bdda3e3bb187223fe6e3ea0b6e4d1021c04"
+checksum = "72eb77396836a4505da85bae0712fa324b74acfe1876d7c2f7e694ef3d0ee373"
 dependencies = [
  "bitflags",
  "byteorder",
@@ -106,9 +106,9 @@ dependencies = [
 
 [[package]]
 name = "diesel-async"
-version = "0.2.1"
+version = "0.2.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "67d310b8050b1068b32fd03c55bd73f8d9ef5a789e2443c5b4e92f4587c63ed3"
+checksum = "5897424d8409385a0cc83d64a44527d15bedb5e3797b65f99daf46374eb6df8e"
 dependencies = [
  "async-trait",
  "diesel",
@@ -149,14 +149,14 @@ checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7"
 
 [[package]]
 name = "filetime"
-version = "0.2.20"
+version = "0.2.21"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8a3de6e8d11b22ff9edc6d916f890800597d60f8b2da1caf2955c274638d6412"
+checksum = "5cbc844cecaee9d4443931972e1289c8ff485cb4cc2767cb03ca139ed6885153"
 dependencies = [
  "cfg-if",
  "libc",
  "redox_syscall",
- "windows-sys",
+ "windows-sys 0.48.0",
 ]
 
 [[package]]
@@ -221,7 +221,7 @@ checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.13",
+ "syn 2.0.15",
 ]
 
 [[package]]
@@ -266,15 +266,21 @@ dependencies = [
 
 [[package]]
 name = "getrandom"
-version = "0.2.8"
+version = "0.2.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31"
+checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4"
 dependencies = [
  "cfg-if",
  "libc",
  "wasi",
 ]
 
+[[package]]
+name = "hashbrown"
+version = "0.12.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
+
 [[package]]
 name = "hermit-abi"
 version = "0.2.6"
@@ -295,11 +301,11 @@ dependencies = [
 
 [[package]]
 name = "home"
-version = "0.5.4"
+version = "0.5.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "747309b4b440c06d57b0b25f2aee03ee9b5e5397d288c60e21fc709bb98a7408"
+checksum = "5444c27eef6923071f7ebcc33e3444508466a76f7a2b93da00ed6e19f30c1ddb"
 dependencies = [
- "winapi",
+ "windows-sys 0.48.0",
 ]
 
 [[package]]
@@ -325,6 +331,16 @@ version = "1.0.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421"
 
+[[package]]
+name = "indexmap"
+version = "1.9.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99"
+dependencies = [
+ "autocfg",
+ "hashbrown",
+]
+
 [[package]]
 name = "io-uring"
 version = "0.5.13"
@@ -349,15 +365,15 @@ checksum = "9028f49264629065d057f340a86acb84867925865f73bbf8d47b4d149a7e88b8"
 
 [[package]]
 name = "libc"
-version = "0.2.140"
+version = "0.2.142"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "99227334921fae1a979cf0bfdfcc6b3e5ce376ef57e16fb6fb3ea2ed6095f80c"
+checksum = "6a987beff54b60ffa6d51982e1aa1146bc42f19bd26be28b0586f252fccf5317"
 
 [[package]]
 name = "libmimalloc-sys"
-version = "0.1.31"
+version = "0.1.33"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ef2c45001fb108f37d41bed8efd715769acb14674c1ce3e266ef0e317ef5f877"
+checksum = "f4ac0e912c8ef1b735e92369695618dc5b1819f5a7bf3f167301a3ba1cea515e"
 dependencies = [
  "cc",
  "libc",
@@ -399,9 +415,9 @@ checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
 
 [[package]]
 name = "mimalloc"
-version = "0.1.35"
+version = "0.1.37"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "92666043c712f7f5c756d07443469ddcda6dd971cc15258bb7f3c3216fd1b7aa"
+checksum = "4e2894987a3459f3ffb755608bd82188f8ed00d0ae077f1edea29c068d639d98"
 dependencies = [
  "libmimalloc-sys",
 ]
@@ -414,7 +430,7 @@ dependencies = [
  "libc",
  "log",
  "wasi",
- "windows-sys",
+ "windows-sys 0.45.0",
 ]
 
 [[package]]
@@ -468,7 +484,7 @@ dependencies = [
  "libc",
  "redox_syscall",
  "smallvec",
- "windows-sys",
+ "windows-sys 0.45.0",
 ]
 
 [[package]]
@@ -631,9 +647,9 @@ checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041"
 
 [[package]]
 name = "sailfish"
-version = "0.6.0"
+version = "0.6.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dd1369644089e936dea5df0d6201636e2b6eb2e9ad098efb2eca5606ece3fab7"
+checksum = "29a48cead573ab494535cd9f24838a721a613e5da899ee974c8e2fdbb3d60222"
 dependencies = [
  "itoap",
  "ryu",
@@ -643,9 +659,9 @@ dependencies = [
 
 [[package]]
 name = "sailfish-compiler"
-version = "0.6.0"
+version = "0.6.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "761019f0de360c654d51e068f07acfa8677e9566391ca290e1ab7c53a55d8d02"
+checksum = "f26deb100e96e303d266d1852525b1da033678ed9eabd539f5ed5eba01333394"
 dependencies = [
  "filetime",
  "home",
@@ -653,15 +669,15 @@ dependencies = [
  "proc-macro2",
  "quote",
  "serde",
- "syn 1.0.109",
+ "syn 2.0.15",
  "toml",
 ]
 
 [[package]]
 name = "sailfish-macros"
-version = "0.6.0"
+version = "0.6.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dad9a1a51d085aa939115cf537c71b1e438de7d27363a039ce525bf393262303"
+checksum = "41162f9a79f2541458a1cf20f8d81d55bb2bcbdd03d295c29765d9bcb43188c9"
 dependencies = [
  "proc-macro2",
  "sailfish-compiler",
@@ -691,35 +707,44 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
 
 [[package]]
 name = "serde"
-version = "1.0.159"
+version = "1.0.160"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3c04e8343c3daeec41f58990b9d77068df31209f2af111e059e9fe9646693065"
+checksum = "bb2f3770c8bce3bcda7e149193a069a0f4365bda1fa5cd88e03bca26afc1216c"
 dependencies = [
  "serde_derive",
 ]
 
 [[package]]
 name = "serde_derive"
-version = "1.0.159"
+version = "1.0.160"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4c614d17805b093df4b147b51339e7e44bf05ef59fba1e45d83500bcfb4d8585"
+checksum = "291a097c63d8497e00160b166a967a4a79c64f3facdd01cbd7502231688d77df"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.13",
+ "syn 2.0.15",
 ]
 
 [[package]]
 name = "serde_json"
-version = "1.0.95"
+version = "1.0.96"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d721eca97ac802aa7777b701877c8004d950fc142651367300d21c1cc0194744"
+checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1"
 dependencies = [
  "itoa",
  "ryu",
  "serde",
 ]
 
+[[package]]
+name = "serde_spanned"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0efd8caf556a6cebd3b285caf480045fcc1ac04f6bd786b09a6f11af30c4fcf4"
+dependencies = [
+ "serde",
+]
+
 [[package]]
 name = "sha2"
 version = "0.10.6"
@@ -773,12 +798,12 @@ dependencies = [
 
 [[package]]
 name = "socket2"
-version = "0.5.1"
+version = "0.5.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bc8d618c6641ae355025c449427f9e96b98abf99a772be3cef6708d15c77147a"
+checksum = "6d283f86695ae989d1e18440a943880967156325ba025f05049946bff47bcc2b"
 dependencies = [
  "libc",
- "windows-sys",
+ "windows-sys 0.48.0",
 ]
 
 [[package]]
@@ -810,9 +835,9 @@ dependencies = [
 
 [[package]]
 name = "syn"
-version = "2.0.13"
+version = "2.0.15"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4c9da457c5285ac1f936ebd076af6dac17a61cfe7826f2076b4d015cf47bc8ec"
+checksum = "a34fcf3e8b60f57e6a14301a2e916d323af98b0ea63c599441eec8558660c822"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -842,9 +867,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
 
 [[package]]
 name = "tokio"
-version = "1.27.0"
+version = "1.28.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d0de47a4eecbe11f498978a9b29d792f0d2692d1dd003650c24c76510e3bc001"
+checksum = "c3c786bf8134e5a3a166db9b29ab8f48134739014a3eca7bc6bfa95d673b136f"
 dependencies = [
  "autocfg",
  "bytes",
@@ -854,7 +879,7 @@ dependencies = [
  "pin-project-lite",
  "signal-hook-registry",
  "socket2 0.4.9",
- "windows-sys",
+ "windows-sys 0.48.0",
 ]
 
 [[package]]
@@ -876,7 +901,7 @@ dependencies = [
  "pin-project-lite",
  "postgres-protocol",
  "postgres-types",
- "socket2 0.5.1",
+ "socket2 0.5.2",
  "tokio",
  "tokio-util",
 ]
@@ -898,9 +923,9 @@ dependencies = [
 
 [[package]]
 name = "tokio-util"
-version = "0.7.7"
+version = "0.7.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5427d89453009325de0d8f342c9490009f76e999cb7672d77e46267448f7e6b2"
+checksum = "806fe8c2c87eccc8b3267cbae29ed3ab2d0bd37fca70ab622e46aaa9375ddb7d"
 dependencies = [
  "bytes",
  "futures-core",
@@ -912,20 +937,44 @@ dependencies = [
 
 [[package]]
 name = "toml"
-version = "0.5.11"
+version = "0.7.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234"
+checksum = "b403acf6f2bb0859c93c7f0d967cb4a75a7ac552100f9322faf64dc047669b21"
 dependencies = [
  "serde",
+ "serde_spanned",
+ "toml_datetime",
+ "toml_edit",
+]
+
+[[package]]
+name = "toml_datetime"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3ab8ed2edee10b50132aed5f331333428b011c99402b5a534154ed15746f9622"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "toml_edit"
+version = "0.19.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "239410c8609e8125456927e6707163a3b1fdb40561e4b803bc041f466ccfdc13"
+dependencies = [
+ "indexmap",
+ "serde",
+ "serde_spanned",
+ "toml_datetime",
+ "winnow",
 ]
 
 [[package]]
 name = "tracing"
-version = "0.1.37"
+version = "0.1.38"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8"
+checksum = "cf9cf6a813d3f40c88b0b6b6f29a5c95c6cdbf97c1f9cc53fb820200f5ad814d"
 dependencies = [
- "cfg-if",
  "pin-project-lite",
  "tracing-core",
 ]
@@ -1006,7 +1055,16 @@ version = "0.45.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
 dependencies = [
- "windows-targets",
+ "windows-targets 0.42.2",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
+dependencies = [
+ "windows-targets 0.48.0",
 ]
 
 [[package]]
@@ -1015,13 +1073,28 @@ version = "0.42.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071"
 dependencies = [
- "windows_aarch64_gnullvm",
- "windows_aarch64_msvc",
- "windows_i686_gnu",
- "windows_i686_msvc",
- "windows_x86_64_gnu",
- "windows_x86_64_gnullvm",
- "windows_x86_64_msvc",
+ "windows_aarch64_gnullvm 0.42.2",
+ "windows_aarch64_msvc 0.42.2",
+ "windows_i686_gnu 0.42.2",
+ "windows_i686_msvc 0.42.2",
+ "windows_x86_64_gnu 0.42.2",
+ "windows_x86_64_gnullvm 0.42.2",
+ "windows_x86_64_msvc 0.42.2",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5"
+dependencies = [
+ "windows_aarch64_gnullvm 0.48.0",
+ "windows_aarch64_msvc 0.48.0",
+ "windows_i686_gnu 0.48.0",
+ "windows_i686_msvc 0.48.0",
+ "windows_x86_64_gnu 0.48.0",
+ "windows_x86_64_gnullvm 0.48.0",
+ "windows_x86_64_msvc 0.48.0",
 ]
 
 [[package]]
@@ -1030,46 +1103,97 @@ version = "0.42.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
 
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc"
+
 [[package]]
 name = "windows_aarch64_msvc"
 version = "0.42.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
 
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3"
+
 [[package]]
 name = "windows_i686_gnu"
 version = "0.42.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
 
+[[package]]
+name = "windows_i686_gnu"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241"
+
 [[package]]
 name = "windows_i686_msvc"
 version = "0.42.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
 
+[[package]]
+name = "windows_i686_msvc"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00"
+
 [[package]]
 name = "windows_x86_64_gnu"
 version = "0.42.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
 
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1"
+
 [[package]]
 name = "windows_x86_64_gnullvm"
 version = "0.42.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
 
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953"
+
 [[package]]
 name = "windows_x86_64_msvc"
 version = "0.42.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
 
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a"
+
+[[package]]
+name = "winnow"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ae8970b36c66498d8ff1d66685dc86b91b29db0c7739899012f63a63814b4b28"
+dependencies = [
+ "memchr",
+]
+
 [[package]]
 name = "xitca-http"
 version = "0.1.0"
-source = "git+https://github.com/HFQR/xitca-web.git?rev=d3e9a4fb6b16513ff638f43305c9c96cdb3501b2#d3e9a4fb6b16513ff638f43305c9c96cdb3501b2"
+source = "git+https://github.com/HFQR/xitca-web.git?rev=5f5ff12c8c33796c8b76f0f56b3e3dd33f6efdaf#5f5ff12c8c33796c8b76f0f56b3e3dd33f6efdaf"
 dependencies = [
  "futures-core",
  "http",
@@ -1077,8 +1201,9 @@ dependencies = [
  "httpdate",
  "itoa",
  "pin-project-lite",
- "socket2 0.5.1",
+ "socket2 0.5.2",
  "tokio",
+ "tokio-uring",
  "tracing",
  "xitca-io",
  "xitca-router",
@@ -1089,7 +1214,7 @@ dependencies = [
 [[package]]
 name = "xitca-io"
 version = "0.1.0"
-source = "git+https://github.com/HFQR/xitca-web.git?rev=d3e9a4fb6b16513ff638f43305c9c96cdb3501b2#d3e9a4fb6b16513ff638f43305c9c96cdb3501b2"
+source = "git+https://github.com/HFQR/xitca-web.git?rev=5f5ff12c8c33796c8b76f0f56b3e3dd33f6efdaf#5f5ff12c8c33796c8b76f0f56b3e3dd33f6efdaf"
 dependencies = [
  "bytes",
  "tokio",
@@ -1099,7 +1224,7 @@ dependencies = [
 [[package]]
 name = "xitca-postgres"
 version = "0.1.0"
-source = "git+https://github.com/HFQR/xitca-web.git?rev=d3e9a4fb6b16513ff638f43305c9c96cdb3501b2#d3e9a4fb6b16513ff638f43305c9c96cdb3501b2"
+source = "git+https://github.com/HFQR/xitca-web.git?rev=5f5ff12c8c33796c8b76f0f56b3e3dd33f6efdaf#5f5ff12c8c33796c8b76f0f56b3e3dd33f6efdaf"
 dependencies = [
  "fallible-iterator",
  "percent-encoding",
@@ -1115,7 +1240,7 @@ dependencies = [
 [[package]]
 name = "xitca-router"
 version = "0.1.0"
-source = "git+https://github.com/HFQR/xitca-web.git?rev=d3e9a4fb6b16513ff638f43305c9c96cdb3501b2#d3e9a4fb6b16513ff638f43305c9c96cdb3501b2"
+source = "git+https://github.com/HFQR/xitca-web.git?rev=5f5ff12c8c33796c8b76f0f56b3e3dd33f6efdaf#5f5ff12c8c33796c8b76f0f56b3e3dd33f6efdaf"
 dependencies = [
  "xitca-unsafe-collection",
 ]
@@ -1123,9 +1248,9 @@ dependencies = [
 [[package]]
 name = "xitca-server"
 version = "0.1.0"
-source = "git+https://github.com/HFQR/xitca-web.git?rev=d3e9a4fb6b16513ff638f43305c9c96cdb3501b2#d3e9a4fb6b16513ff638f43305c9c96cdb3501b2"
+source = "git+https://github.com/HFQR/xitca-web.git?rev=5f5ff12c8c33796c8b76f0f56b3e3dd33f6efdaf#5f5ff12c8c33796c8b76f0f56b3e3dd33f6efdaf"
 dependencies = [
- "socket2 0.5.1",
+ "socket2 0.5.2",
  "tokio",
  "tokio-uring",
  "tracing",
@@ -1137,12 +1262,12 @@ dependencies = [
 [[package]]
 name = "xitca-service"
 version = "0.1.0"
-source = "git+https://github.com/HFQR/xitca-web.git?rev=d3e9a4fb6b16513ff638f43305c9c96cdb3501b2#d3e9a4fb6b16513ff638f43305c9c96cdb3501b2"
+source = "git+https://github.com/HFQR/xitca-web.git?rev=5f5ff12c8c33796c8b76f0f56b3e3dd33f6efdaf#5f5ff12c8c33796c8b76f0f56b3e3dd33f6efdaf"
 
 [[package]]
 name = "xitca-unsafe-collection"
 version = "0.1.0"
-source = "git+https://github.com/HFQR/xitca-web.git?rev=d3e9a4fb6b16513ff638f43305c9c96cdb3501b2#d3e9a4fb6b16513ff638f43305c9c96cdb3501b2"
+source = "git+https://github.com/HFQR/xitca-web.git?rev=5f5ff12c8c33796c8b76f0f56b3e3dd33f6efdaf#5f5ff12c8c33796c8b76f0f56b3e3dd33f6efdaf"
 dependencies = [
  "bytes",
 ]
@@ -1169,13 +1294,13 @@ dependencies = [
  "xitca-server",
  "xitca-service",
  "xitca-unsafe-collection",
- "xitca-web 0.1.0 (git+https://github.com/HFQR/xitca-web.git?rev=d3e9a4fb6b16513ff638f43305c9c96cdb3501b2)",
+ "xitca-web 0.1.0 (git+https://github.com/HFQR/xitca-web.git?rev=5f5ff12c8c33796c8b76f0f56b3e3dd33f6efdaf)",
 ]
 
 [[package]]
 name = "xitca-web"
 version = "0.1.0"
-source = "git+https://github.com/HFQR/xitca-web.git?rev=d3e9a4fb6b16513ff638f43305c9c96cdb3501b2#d3e9a4fb6b16513ff638f43305c9c96cdb3501b2"
+source = "git+https://github.com/HFQR/xitca-web.git?rev=5f5ff12c8c33796c8b76f0f56b3e3dd33f6efdaf#5f5ff12c8c33796c8b76f0f56b3e3dd33f6efdaf"
 dependencies = [
  "futures-core",
  "pin-project-lite",

+ 10 - 10
frameworks/Rust/xitca-web/Cargo.toml

@@ -6,7 +6,7 @@ edition = "2021"
 [[bin]]
 name = "xitca-web"
 path = "./src/main.rs"
-required-features = ["pg", "serde", "template"]
+required-features = ["io-uring", "pg", "serde", "template"]
 
 [[bin]]
 name = "xitca-web-diesel"
@@ -40,7 +40,7 @@ web = ["xitca-web"]
 # template optional
 template = ["sailfish"]
 # io-uring optional
-io-uring = ["tokio-uring", "xitca-server/io-uring"]
+io-uring = ["tokio-uring", "xitca-http/io-uring", "xitca-server/io-uring"]
 
 [dependencies]
 xitca-http = { version = "0.1", features = ["util-service"] }
@@ -86,13 +86,13 @@ codegen-units = 1
 panic = "abort"
 
 [patch.crates-io]
-xitca-http = { git = "https://github.com/HFQR/xitca-web.git", rev = "d3e9a4fb6b16513ff638f43305c9c96cdb3501b2" }
-xitca-io = { git = "https://github.com/HFQR/xitca-web.git", rev = "d3e9a4fb6b16513ff638f43305c9c96cdb3501b2" }
-xitca-postgres = { git = "https://github.com/HFQR/xitca-web.git", rev = "d3e9a4fb6b16513ff638f43305c9c96cdb3501b2" }
-xitca-router = { git = "https://github.com/HFQR/xitca-web.git", rev = "d3e9a4fb6b16513ff638f43305c9c96cdb3501b2" }
-xitca-server = { git = "https://github.com/HFQR/xitca-web.git", rev = "d3e9a4fb6b16513ff638f43305c9c96cdb3501b2" }
-xitca-service = { git = "https://github.com/HFQR/xitca-web.git", rev = "d3e9a4fb6b16513ff638f43305c9c96cdb3501b2" }
-xitca-unsafe-collection = { git = "https://github.com/HFQR/xitca-web.git", rev = "d3e9a4fb6b16513ff638f43305c9c96cdb3501b2" }
-xitca-web = { git = "https://github.com/HFQR/xitca-web.git", rev = "d3e9a4fb6b16513ff638f43305c9c96cdb3501b2" }
+xitca-http = { git = "https://github.com/HFQR/xitca-web.git", rev = "5f5ff12c8c33796c8b76f0f56b3e3dd33f6efdaf" }
+xitca-io = { git = "https://github.com/HFQR/xitca-web.git", rev = "5f5ff12c8c33796c8b76f0f56b3e3dd33f6efdaf" }
+xitca-postgres = { git = "https://github.com/HFQR/xitca-web.git", rev = "5f5ff12c8c33796c8b76f0f56b3e3dd33f6efdaf" }
+xitca-router = { git = "https://github.com/HFQR/xitca-web.git", rev = "5f5ff12c8c33796c8b76f0f56b3e3dd33f6efdaf" }
+xitca-server = { git = "https://github.com/HFQR/xitca-web.git", rev = "5f5ff12c8c33796c8b76f0f56b3e3dd33f6efdaf" }
+xitca-service = { git = "https://github.com/HFQR/xitca-web.git", rev = "5f5ff12c8c33796c8b76f0f56b3e3dd33f6efdaf" }
+xitca-unsafe-collection = { git = "https://github.com/HFQR/xitca-web.git", rev = "5f5ff12c8c33796c8b76f0f56b3e3dd33f6efdaf" }
+xitca-web = { git = "https://github.com/HFQR/xitca-web.git", rev = "5f5ff12c8c33796c8b76f0f56b3e3dd33f6efdaf" }
 
 mio = { git = "https://github.com/fakeshadow/mio.git", rev = "4272250caed2f460f7be646d0fd25d53d267eb53" }

+ 1 - 1
frameworks/Rust/xitca-web/rust-toolchain.toml

@@ -1,2 +1,2 @@
 [toolchain]
-channel = "nightly-2023-04-03"
+channel = "nightly-2023-04-25"

+ 8 - 3
frameworks/Rust/xitca-web/src/db.rs

@@ -1,4 +1,9 @@
-use std::{cell::RefCell, collections::HashMap, fmt::Write, future::Future};
+use std::{
+    cell::RefCell,
+    collections::HashMap,
+    fmt::Write,
+    future::{Future, IntoFuture},
+};
 
 use futures_util::stream::{FuturesUnordered, TryStreamExt};
 use xitca_postgres::{statement::Statement, AsyncIterator, Postgres, ToSql};
@@ -28,9 +33,9 @@ impl Drop for Client {
 }
 
 pub async fn create(config: &str) -> HandleResult<Client> {
-    let (client, bg_task) = Postgres::new(config.to_string()).connect().await?;
+    let (client, driver) = Postgres::new(config.to_string()).connect().await?;
 
-    tokio::task::spawn_local(bg_task);
+    tokio::task::spawn_local(driver.into_future());
 
     let fortune = client.prepare("SELECT * FROM fortune", &[]).await?.leak();
 

+ 14 - 6
frameworks/Rust/xitca-web/src/main_iou.rs

@@ -2,7 +2,7 @@
 // network io.
 
 #![allow(dead_code)]
-#![feature(type_alias_impl_trait)]
+#![feature(impl_trait_in_assoc_type)]
 
 #[global_allocator]
 static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc;
@@ -168,19 +168,27 @@ where
 
             #[cfg(feature = "io-uring")]
             {
+                use tokio_uring::buf::IoBuf;
+
                 let std = stream.into_std()?;
                 let stream = tokio_uring::net::TcpStream::from_std(std);
 
-                let mut read_buf = vec![0; 4096];
-
                 'io: loop {
-                    let (res, buf) = stream.read(read_buf).await;
+                    let mut buf = paged.into_inner();
+
+                    let len = buf.len();
+                    let rem = buf.capacity() - len;
+
+                    if rem < 4096 {
+                        buf.reserve(4096 - rem);
+                    }
+
+                    let (res, buf) = stream.read(buf.slice(len..)).await;
                     let n = res?;
                     if n == 0 {
                         break;
                     }
-                    read_buf = buf;
-                    paged.get_mut().extend_from_slice(&read_buf[..n]);
+                    paged = PagedBytesMut::from(buf.into_inner());
 
                     request_handler(&mut ctx, &self.service, &mut paged, &mut write_buf).await;
 

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

@@ -3,7 +3,7 @@ FROM rust:latest
 ADD ./ /xitca-web
 WORKDIR /xitca-web
 
-RUN cargo build --release --bin xitca-web --features pg,serde,template
+RUN cargo build --release --bin xitca-web --features io-uring,pg,serde,template
 
 EXPOSE 8080
 

+ 4 - 12
frameworks/TypeScript/nest/benchmark_config.json

@@ -23,12 +23,10 @@
         "versus": "nodejs"
       },
       "mysql": {
-        "json_url": "/json",
         "db_url": "/db",
         "query_url": "/queries?queries=",
         "fortune_url": "/fortunes",
         "update_url": "/updates?queries=",
-        "plaintext_url": "/plaintext",
         "cached_query_url": "/cached-worlds?count=",
         "port": 8080,
         "approach": "Realistic",
@@ -39,16 +37,14 @@
         "orm": "Full",
         "database": "MySQL",
         "database_os": "Linux",
-        "display_name": "nestjs",
+        "display_name": "nestjs mysql",
         "versus": "nodejs"
       },
       "mongo": {
-        "json_url": "/json",
         "db_url": "/db",
         "query_url": "/queries?queries=",
         "fortune_url": "/fortunes",
         "update_url": "/updates?queries=",
-        "plaintext_url": "/plaintext",
         "cached_query_url": "/cached-worlds?count=",
         "port": 8080,
         "approach": "Realistic",
@@ -79,16 +75,14 @@
         "orm": "Full",
         "os": "Linux",
         "database_os": "Linux",
-        "display_name": "nestjs",
+        "display_name": "nestjs fastify",
         "versus": "nodejs"
       },
       "fastify-mysql": {
-        "json_url": "/json",
         "db_url": "/db",
         "query_url": "/queries?queries=",
         "fortune_url": "/fortunes",
         "update_url": "/updates?queries=",
-        "plaintext_url": "/plaintext",
         "cached_query_url": "/cached-worlds?count=",
         "port": 8080,
         "approach": "Realistic",
@@ -99,16 +93,14 @@
         "orm": "Full",
         "os": "Linux",
         "database_os": "Linux",
-        "display_name": "nestjs",
+        "display_name": "nestjs fastify mysql",
         "versus": "nodejs"
       },
       "fastify-mongo": {
-        "json_url": "/json",
         "db_url": "/db",
         "query_url": "/queries?queries=",
         "fortune_url": "/fortunes",
         "update_url": "/updates?queries=",
-        "plaintext_url": "/plaintext",
         "cached_query_url": "/cached-worlds?count=",
         "port": 8080,
         "approach": "Realistic",
@@ -119,7 +111,7 @@
         "orm": "Full",
         "os": "Linux",
         "database_os": "Linux",
-        "display_name": "nestjs",
+        "display_name": "nestjs fastify mongo",
         "versus": "nodejs"
       }
     }