Browse Source

Merge branch 'master' of https://github.com/TechEmpower/FrameworkBenchmarks

jaguililla 7 months ago
parent
commit
11621a472f
78 changed files with 1963 additions and 599 deletions
  1. 1 1
      frameworks/Java/smart-socket/pom.xml
  2. 7 7
      frameworks/Java/smart-socket/src/main/java/org/smartboot/http/Bootstrap.java
  3. 1 1
      frameworks/Java/smart-socket/src/main/java/org/smartboot/http/JsonUtil.java
  4. 5 4
      frameworks/Java/smart-socket/src/main/java/org/smartboot/http/MultipleQueriesHandler.java
  5. 4 3
      frameworks/Java/smart-socket/src/main/java/org/smartboot/http/SingleQueryHandler.java
  6. 5 4
      frameworks/Java/smart-socket/src/main/java/org/smartboot/http/UpdateHandler.java
  7. 7 2
      frameworks/Java/solon/pom.xml
  8. 19 0
      frameworks/Java/tio-http-server/.dockerignore
  9. 3 0
      frameworks/Java/tio-http-server/.gitignore
  10. 114 0
      frameworks/Java/tio-http-server/README.md
  11. 227 0
      frameworks/Java/tio-http-server/api/tio-server-benchmark.md
  12. 29 0
      frameworks/Java/tio-http-server/benchmark_config.json
  13. 19 0
      frameworks/Java/tio-http-server/config.toml
  14. 209 0
      frameworks/Java/tio-http-server/pom.xml
  15. 66 0
      frameworks/Java/tio-http-server/src/main/java/com/litongjava/tio/http/server/MainApp.java
  16. 12 0
      frameworks/Java/tio-http-server/src/main/java/com/litongjava/tio/http/server/config/EhCachePluginConfig.java
  17. 22 0
      frameworks/Java/tio-http-server/src/main/java/com/litongjava/tio/http/server/config/EnjoyEngineConfig.java
  18. 31 0
      frameworks/Java/tio-http-server/src/main/java/com/litongjava/tio/http/server/config/MysqlDbConfig.java
  19. 41 0
      frameworks/Java/tio-http-server/src/main/java/com/litongjava/tio/http/server/controller/CacheController.java
  20. 127 0
      frameworks/Java/tio-http-server/src/main/java/com/litongjava/tio/http/server/controller/DbController.java
  21. 40 0
      frameworks/Java/tio-http-server/src/main/java/com/litongjava/tio/http/server/controller/IndexController.java
  22. 23 0
      frameworks/Java/tio-http-server/src/main/java/com/litongjava/tio/http/server/model/Fortune.java
  23. 12 0
      frameworks/Java/tio-http-server/src/main/java/com/litongjava/tio/http/server/model/Message.java
  24. 32 0
      frameworks/Java/tio-http-server/src/main/java/com/litongjava/tio/http/server/model/World.java
  25. 50 0
      frameworks/Java/tio-http-server/src/main/java/com/litongjava/tio/http/server/services/CacheName.java
  26. 17 0
      frameworks/Java/tio-http-server/src/main/java/com/litongjava/tio/http/server/services/CacheNameService.java
  27. 31 0
      frameworks/Java/tio-http-server/src/main/java/com/litongjava/tio/http/server/utils/BeanConverterUtils.java
  28. 36 0
      frameworks/Java/tio-http-server/src/main/java/com/litongjava/tio/http/server/utils/RandomUtils.java
  29. 9 0
      frameworks/Java/tio-http-server/src/main/resources/app.properties
  30. 9 0
      frameworks/Java/tio-http-server/src/main/resources/ehcache.xml
  31. 52 0
      frameworks/Java/tio-http-server/src/main/resources/logback.xml
  32. 20 0
      frameworks/Java/tio-http-server/src/main/resources/templates/fortunes.html
  33. 15 0
      frameworks/Java/tio-http-server/src/test/java/com/litongjava/tio/http/server/MainAppTest.java
  34. 9 0
      frameworks/Java/tio-http-server/tio-server-native.dockerfile
  35. 19 0
      frameworks/Java/tio-http-server/tio-server.dockerfile
  36. 2 2
      frameworks/PHP/flight/composer.json
  37. 2 3
      frameworks/PHP/flight/index.php
  38. 25 23
      frameworks/PHP/webman/app/controller/Index.php
  39. 0 33
      frameworks/PHP/webman/support/bootstrap/db/Raw.php
  40. 8 5
      frameworks/PHP/workerman/Mysql.php
  41. 3 2
      frameworks/PHP/workerman/Pgsql.php
  42. 0 1
      frameworks/PHP/workerman/benchmark_config.json
  43. 2 7
      frameworks/Ruby/rack-sequel/Gemfile.lock
  44. 1 1
      frameworks/Ruby/rack/Gemfile.lock
  45. 1 1
      frameworks/Ruby/rails/Gemfile.lock
  46. 1 0
      frameworks/Ruby/rails/app/controllers/hello_world_controller.rb
  47. 1 1
      frameworks/Ruby/roda-sequel/Gemfile.lock
  48. 1 1
      frameworks/Ruby/sinatra/Gemfile
  49. 26 22
      frameworks/Ruby/sinatra/Gemfile.lock
  50. 345 252
      frameworks/Rust/axum/Cargo.lock
  51. 20 19
      frameworks/Rust/axum/Cargo.toml
  52. 3 5
      frameworks/Rust/axum/README.md
  53. 2 2
      frameworks/Rust/axum/axum.dockerfile
  54. 6 8
      frameworks/Rust/axum/src/common/mod.rs
  55. 4 0
      frameworks/Rust/axum/src/main.rs
  56. 8 10
      frameworks/Rust/axum/src/main_mongo.rs
  57. 10 10
      frameworks/Rust/axum/src/main_mongo_raw.rs
  58. 4 0
      frameworks/Rust/axum/src/main_pg.rs
  59. 4 0
      frameworks/Rust/axum/src/main_pg_pool.rs
  60. 12 12
      frameworks/Rust/axum/src/main_sqlx.rs
  61. 7 7
      frameworks/Rust/axum/src/mongo/database.rs
  62. 6 6
      frameworks/Rust/axum/src/mongo_raw/database.rs
  63. 7 4
      frameworks/Rust/viz/Cargo.toml
  64. 10 4
      frameworks/Rust/viz/src/main.rs
  65. 1 3
      frameworks/Rust/viz/viz-diesel.dockerfile
  66. 1 3
      frameworks/Rust/viz/viz-pg.dockerfile
  67. 1 3
      frameworks/Rust/viz/viz-sqlx.dockerfile
  68. 1 3
      frameworks/Rust/viz/viz.dockerfile
  69. 6 6
      frameworks/TypeScript/ditsmod/benchmark_config.json
  70. 2 2
      frameworks/TypeScript/ditsmod/package.json
  71. 2 4
      frameworks/TypeScript/ditsmod/src/app/app.module.ts
  72. 5 11
      frameworks/TypeScript/ditsmod/src/app/init.extension.ts
  73. 12 15
      frameworks/TypeScript/ditsmod/src/app/one.controller.ts
  74. 1 1
      frameworks/TypeScript/ditsmod/src/main.ts
  75. BIN
      frameworks/TypeScript/elysia/bun.lockb
  76. 4 3
      frameworks/TypeScript/elysia/package.json
  77. 57 65
      frameworks/TypeScript/elysia/src/db-handlers.ts
  78. 26 17
      frameworks/TypeScript/elysia/src/postgres.ts

+ 1 - 1
frameworks/Java/smart-socket/pom.xml

@@ -11,7 +11,7 @@
         <maven.compiler.source>21</maven.compiler.source>
         <maven.compiler.source>21</maven.compiler.source>
         <maven.compiler.target>21</maven.compiler.target>
         <maven.compiler.target>21</maven.compiler.target>
         <log4j.version>2.17.1</log4j.version>
         <log4j.version>2.17.1</log4j.version>
-        <smartservlet.version>2.5</smartservlet.version>
+        <smartservlet.version>2.7</smartservlet.version>
         <hikaricp.version>5.0.0</hikaricp.version>
         <hikaricp.version>5.0.0</hikaricp.version>
         <jsoniter.version>0.9.23</jsoniter.version>
         <jsoniter.version>0.9.23</jsoniter.version>
     </properties>
     </properties>

+ 7 - 7
frameworks/Java/smart-socket/src/main/java/org/smartboot/http/Bootstrap.java

@@ -11,11 +11,11 @@ package org.smartboot.http;
 import com.zaxxer.hikari.HikariConfig;
 import com.zaxxer.hikari.HikariConfig;
 import com.zaxxer.hikari.HikariDataSource;
 import com.zaxxer.hikari.HikariDataSource;
 import org.smartboot.Message;
 import org.smartboot.Message;
-import org.smartboot.http.server.HttpBootstrap;
-import org.smartboot.http.server.HttpRequest;
-import org.smartboot.http.server.HttpResponse;
-import org.smartboot.http.server.HttpServerHandler;
-import org.smartboot.http.server.handler.HttpRouteHandler;
+import tech.smartboot.feat.core.server.HttpRequest;
+import tech.smartboot.feat.core.server.HttpResponse;
+import tech.smartboot.feat.core.server.HttpServer;
+import tech.smartboot.feat.core.server.HttpServerHandler;
+import tech.smartboot.feat.core.server.handler.HttpRouteHandler;
 
 
 import javax.sql.DataSource;
 import javax.sql.DataSource;
 
 
@@ -25,9 +25,9 @@ public class Bootstrap {
     public static void main(String[] args) {
     public static void main(String[] args) {
         int cpuNum = Runtime.getRuntime().availableProcessors();
         int cpuNum = Runtime.getRuntime().availableProcessors();
         // 定义服务器接受的消息类型以及各类消息对应的处理器
         // 定义服务器接受的消息类型以及各类消息对应的处理器
-        HttpBootstrap bootstrap = new HttpBootstrap();
+        HttpServer bootstrap = new HttpServer();
         bootstrap.configuration()
         bootstrap.configuration()
-                .threadNum(cpuNum)
+                .threadNum(cpuNum + 1)
                 .headerLimiter(0)
                 .headerLimiter(0)
                 .readBufferSize(1024 * 4)
                 .readBufferSize(1024 * 4)
                 .writeBufferSize(1024 * 4);
                 .writeBufferSize(1024 * 4);

+ 1 - 1
frameworks/Java/smart-socket/src/main/java/org/smartboot/http/JsonUtil.java

@@ -5,7 +5,7 @@ import com.jsoniter.output.JsonStreamPool;
 import com.jsoniter.spi.JsonException;
 import com.jsoniter.spi.JsonException;
 import com.jsoniter.spi.Slice;
 import com.jsoniter.spi.Slice;
 import jakarta.servlet.http.HttpServletResponse;
 import jakarta.servlet.http.HttpServletResponse;
-import org.smartboot.http.server.HttpResponse;
+import tech.smartboot.feat.core.server.HttpResponse;
 
 
 import java.io.IOException;
 import java.io.IOException;
 
 

+ 5 - 4
frameworks/Java/smart-socket/src/main/java/org/smartboot/http/MultipleQueriesHandler.java

@@ -1,9 +1,10 @@
 package org.smartboot.http;
 package org.smartboot.http;
 
 
-import org.smartboot.http.common.utils.NumberUtils;
-import org.smartboot.http.server.HttpRequest;
-import org.smartboot.http.server.HttpResponse;
-import org.smartboot.http.server.HttpServerHandler;
+
+import tech.smartboot.feat.core.common.utils.NumberUtils;
+import tech.smartboot.feat.core.server.HttpRequest;
+import tech.smartboot.feat.core.server.HttpResponse;
+import tech.smartboot.feat.core.server.HttpServerHandler;
 
 
 import javax.sql.DataSource;
 import javax.sql.DataSource;
 import java.io.IOException;
 import java.io.IOException;

+ 4 - 3
frameworks/Java/smart-socket/src/main/java/org/smartboot/http/SingleQueryHandler.java

@@ -1,8 +1,9 @@
 package org.smartboot.http;
 package org.smartboot.http;
 
 
-import org.smartboot.http.server.HttpRequest;
-import org.smartboot.http.server.HttpResponse;
-import org.smartboot.http.server.HttpServerHandler;
+
+import tech.smartboot.feat.core.server.HttpRequest;
+import tech.smartboot.feat.core.server.HttpResponse;
+import tech.smartboot.feat.core.server.HttpServerHandler;
 
 
 import javax.sql.DataSource;
 import javax.sql.DataSource;
 import java.io.IOException;
 import java.io.IOException;

+ 5 - 4
frameworks/Java/smart-socket/src/main/java/org/smartboot/http/UpdateHandler.java

@@ -1,9 +1,10 @@
 package org.smartboot.http;
 package org.smartboot.http;
 
 
-import org.smartboot.http.common.utils.NumberUtils;
-import org.smartboot.http.server.HttpRequest;
-import org.smartboot.http.server.HttpResponse;
-import org.smartboot.http.server.HttpServerHandler;
+
+import tech.smartboot.feat.core.common.utils.NumberUtils;
+import tech.smartboot.feat.core.server.HttpRequest;
+import tech.smartboot.feat.core.server.HttpResponse;
+import tech.smartboot.feat.core.server.HttpServerHandler;
 
 
 import javax.sql.DataSource;
 import javax.sql.DataSource;
 import java.io.IOException;
 import java.io.IOException;

+ 7 - 2
frameworks/Java/solon/pom.xml

@@ -5,7 +5,7 @@
     <parent>
     <parent>
         <groupId>org.noear</groupId>
         <groupId>org.noear</groupId>
         <artifactId>solon-parent</artifactId>
         <artifactId>solon-parent</artifactId>
-        <version>3.0.4</version>
+        <version>3.0.5</version>
     </parent>
     </parent>
 
 
     <groupId>hello</groupId>
     <groupId>hello</groupId>
@@ -21,7 +21,12 @@
     <dependencies>
     <dependencies>
         <dependency>
         <dependency>
             <groupId>org.noear</groupId>
             <groupId>org.noear</groupId>
-            <artifactId>solon-web</artifactId>
+            <artifactId>solon-lib</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.noear</groupId>
+            <artifactId>solon-boot-smarthttp</artifactId>
         </dependency>
         </dependency>
 
 
         <dependency>
         <dependency>

+ 19 - 0
frameworks/Java/tio-http-server/.dockerignore

@@ -0,0 +1,19 @@
+.github
+.git
+.DS_Store
+docs
+kubernetes
+node_modules
+/.svelte-kit
+/package
+.env
+.env.*
+vite.config.js.timestamp-*
+vite.config.ts.timestamp-*
+__pycache__
+.env
+_old
+uploads
+.ipynb_checkpoints
+**/*.db
+_test

+ 3 - 0
frameworks/Java/tio-http-server/.gitignore

@@ -0,0 +1,3 @@
+/target/
+logs
+.settings

+ 114 - 0
frameworks/Java/tio-http-server/README.md

@@ -0,0 +1,114 @@
+# t-io Benchmarking Test
+
+This is the tio-server portion of a [benchmarking test suite](../) comparing a variety of web development platforms.
+
+## Controller
+
+These implementations use the tio-server's controller.
+
+### Plaintext Test
+
+* [Plaintext test source](src/main/java/com/litongjava/tio/http/server/controller/IndexController.java)
+
+### JSON Serialization Test
+
+* [JSON test source](src/main/java/com/litongjava/tio/http/server/controller/IndexController.java)
+
+### Database Query Test
+
+* [Database Query test source](src/main/java/com/litongjava/tio/http/server/controller/DbController.java))
+
+### Database Queries Test
+
+* [Database Queries test source](src/main/java/com/litongjava/tio/http/server/controller/DbController.java))
+
+### Database Update Test
+
+* [Database Update test source](src/main/java/com/litongjava/tio/http/server/controller/DbController.java))
+
+### Template rendering Test
+
+* [Template rendering test source](src/main/java/com/litongjava/tio/http/server/controller/DbController.java))
+
+### Cache Query Test
+* [Cache query test source](src/main/java/com/litongjava/tio/http/server/controller/CacheController.java))
+
+
+## Versions
+3.7.3.v20231218-RELEASE (https://gitee.com/litongjava/t-io)
+
+## Test URLs
+
+All implementations use the same URLs.
+
+### Plaintext Test
+
+    http://localhost:8080/plaintext
+
+### JSON Encoding Test
+
+    http://localhost:8080/json
+
+### Database Query Test
+
+    http://localhost:8080/db
+
+### Database Queries Test
+
+    http://localhost:8080/queries?queries=5
+
+### Cache Query Test
+
+    http://localhost:8080/cacheQuery?queries=10000
+
+### Template rendering Test
+
+    http://localhost:8080/fortunes
+    
+### Database Update Test
+
+    http://localhost:8080/updates?queries=5
+
+ ## Hot to run
+ ### install mysql 8
+ - 1.please instal mysql 8.0.32,example cmd
+ ```
+ docker run --restart=always -d --name mysql_8 --hostname mysql \
+-p 3306:3306 \
+-e 'MYSQL_ROOT_PASSWORD=robot_123456#' -e 'MYSQL_ROOT_HOST=%' -e 'MYSQL_DATABASE=hello_world' \
+mysql/mysql-server:8.0.32 \
+--character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci --lower_case_table_names=1
+ ```
+ - 2.create database schema hello_world
+ - 3.create tablle,[example](sql/hello_world.sql)
+ - 4.import data
+ 
+ ### docker 
+ ```
+ docker build -t tio-server-benchmark -f tio-server.dockerfile .
+```
+The run is to specify the mysql database
+```
+docker run --rm -p 8080:8080 \
+-e JDBC_URL="jdbc:mysql://192.168.3.9/hello_world" \
+-e JDBC_USER="root" \
+-e JDBC_PSWD="robot_123456#" \
+tio-server-benchmark
+```
+
+### windows
+
+-windows
+```
+D:\java\jdk1.8.0_121\bin\java -jar target\tio-server-benchmark-1.0.jar --JDBC_URL=jdbc:mysql://192.168.3.9/hello_world?useSSL=false --JDBC_USER=root --JDBC_PSWD=robot_123456#
+```
+or 
+```
+set JDBC_URL=jdbc:mysql://192.168.3.9/hello_world
+set jdbc.user=root
+set JDBC_PSWD=robot_123456#
+D:\java\jdk1.8.0_121\bin\java -jar target\tio-server-benchmark-1.0.jar
+```
+
+
+

+ 227 - 0
frameworks/Java/tio-http-server/api/tio-server-benchmark.md

@@ -0,0 +1,227 @@
+---
+title: tio-server-benchmark v1.0.0
+language_tabs:
+  - shell: Shell
+  - http: HTTP
+  - javascript: JavaScript
+  - ruby: Ruby
+  - python: Python
+  - php: PHP
+  - java: Java
+  - go: Go
+toc_footers: []
+includes: []
+search: true
+code_clipboard: true
+highlight_theme: darkula
+headingLevel: 2
+generator: "@tarslib/widdershins v4.0.17"
+
+---
+
+# tio-server-benchmark
+
+> v1.0.0
+
+Base URLs:
+
+# Authentication
+
+# Default
+
+## GET plaintext
+
+GET /plaintext
+
+> 返回示例
+
+> 200 Response
+
+```json
+{}
+```
+
+### 返回结果
+
+|状态码|状态码含义|说明|数据模型|
+|---|---|---|---|
+|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|成功|Inline|
+
+### 返回数据结构
+
+## GET json
+
+GET /json
+
+> 返回示例
+
+> 200 Response
+
+```json
+{}
+```
+
+### 返回结果
+
+|状态码|状态码含义|说明|数据模型|
+|---|---|---|---|
+|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|成功|Inline|
+
+### 返回数据结构
+
+## GET db
+
+GET /db
+
+### 请求参数
+
+|名称|位置|类型|必选|说明|
+|---|---|---|---|---|
+|id|query|string| 否 |none|
+
+> 返回示例
+
+> 200 Response
+
+```json
+{
+  "id": 0,
+  "randomNumber": 0
+}
+```
+
+### 返回结果
+
+|状态码|状态码含义|说明|数据模型|
+|---|---|---|---|
+|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|成功|Inline|
+
+### 返回数据结构
+
+状态码 **200**
+
+|名称|类型|必选|约束|中文名|说明|
+|---|---|---|---|---|---|
+|» id|integer|true|none||none|
+|» randomNumber|integer|true|none||none|
+
+## GET updates
+
+GET /updates
+
+### 请求参数
+
+|名称|位置|类型|必选|说明|
+|---|---|---|---|---|
+|queries|query|string| 否 |none|
+
+> 返回示例
+
+> 成功
+
+```json
+[
+  {
+    "id": 28,
+    "randomNumber": 5399,
+    "randomnumber": 1498
+  }
+]
+```
+
+### 返回结果
+
+|状态码|状态码含义|说明|数据模型|
+|---|---|---|---|
+|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|成功|Inline|
+
+### 返回数据结构
+
+状态码 **200**
+
+|名称|类型|必选|约束|中文名|说明|
+|---|---|---|---|---|---|
+|» id|integer|false|none||none|
+|» randomNumber|integer|false|none||none|
+|» randomnumber|integer|false|none||none|
+
+## GET fortunes
+
+GET /fortunes
+
+> 返回示例
+
+> 200 Response
+
+```json
+{}
+```
+
+### 返回结果
+
+|状态码|状态码含义|说明|数据模型|
+|---|---|---|---|
+|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|成功|Inline|
+
+### 返回数据结构
+
+## GET cacheQuery
+
+GET /cacheQuery
+
+### 请求参数
+
+|名称|位置|类型|必选|说明|
+|---|---|---|---|---|
+|queries|query|string| 否 |none|
+
+> 返回示例
+
+> 200 Response
+
+```json
+[
+  {
+    "id": 0,
+    "randomNumber": 0
+  }
+]
+```
+
+### 返回结果
+
+|状态码|状态码含义|说明|数据模型|
+|---|---|---|---|
+|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|成功|Inline|
+
+### 返回数据结构
+
+状态码 **200**
+
+|名称|类型|必选|约束|中文名|说明|
+|---|---|---|---|---|---|
+|» id|integer|false|none||none|
+|» randomNumber|integer|false|none||none|
+
+## GET cacheList
+
+GET /cacheList
+
+> 返回示例
+
+> 200 Response
+
+```json
+{}
+```
+
+### 返回结果
+
+|状态码|状态码含义|说明|数据模型|
+|---|---|---|---|
+|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|成功|Inline|
+
+### 返回数据结构
+
+# 数据模型
+

+ 29 - 0
frameworks/Java/tio-http-server/benchmark_config.json

@@ -0,0 +1,29 @@
+{
+  "framework": "tio-server",
+  "tests": [{
+    "default": {
+      "plaintext_url": "/plaintext",
+      "json_url": "/json",
+      "db_url": "/db",
+      "query_url": "/queries?queries=",
+      "fortune_url": "/fortunes",
+      "update_url": "/updates?queries=",
+      "cached_query_url" : "/cachedQuery?queries=",
+      "port": 8080,
+      "approach": "Realistic",
+      "classification": "Micro",
+      "database": "MySQL",
+      "framework": "tio-server",
+      "language": "Java",
+      "flavor": "None",
+      "orm": "Raw",
+      "platform": "t-io",
+      "webserver": "None",
+      "os": "Linux",
+      "database_os": "Linux",
+      "display_name": "tio-server",
+      "notes": "tio-server",
+      "versus": "t-io"
+    }
+  }]
+}

+ 19 - 0
frameworks/Java/tio-http-server/config.toml

@@ -0,0 +1,19 @@
+[framework]
+name = "t-io"
+
+[main]
+urls.plaintext = "/plaintext"
+urls.json = "/json"
+urls.query = "/queries?queries="
+urls.update = "/updates?queries="
+urls.fortune = "/fortunes"
+urls.cached_query = "/cachedQuery?queries="
+approach = "Realistic"
+classification = "Micro"
+database = "None"
+database_os = "Linux"
+os = "Linux"
+orm = "Raw"
+platform = "t-io"
+webserver = "None"
+versus = "t-io"

+ 209 - 0
frameworks/Java/tio-http-server/pom.xml

@@ -0,0 +1,209 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <groupId>com.litongjava</groupId>
+  <artifactId>tio-http-server-benchmark</artifactId>
+  <version>1.0</version>
+  <name>${project.artifactId}</name>
+  <properties>
+    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    <java.version>1.8</java.version>
+    <maven.compiler.source>${java.version}</maven.compiler.source>
+    <maven.compiler.target>${java.version}</maven.compiler.target>
+    <graalvm.version>23.1.1</graalvm.version>
+    <main.class>com.litongjava.tio.http.server.MainApp</main.class>
+  </properties>
+  <dependencies>
+    <dependency>
+      <groupId>com.litongjava</groupId>
+      <artifactId>tio-http-server</artifactId>
+      <version>3.7.3.v20240919-RELEASE</version>
+    </dependency>
+    <dependency>
+      <groupId>com.litongjava</groupId>
+      <artifactId>java-db</artifactId>
+      <version>1.2.6</version>
+    </dependency>
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <version>4.12</version>
+      <scope>test</scope>
+    </dependency>
+
+
+    <!-- https://mvnrepository.com/artifact/com.jfinal/activerecord -->
+    <!--
+    <dependency>
+      <groupId>com.jfinal</groupId>
+      <artifactId>activerecord</artifactId>
+      <version>5.1.6</version>
+    </dependency>
+    -->
+
+
+    <dependency>
+      <groupId>com.alibaba.fastjson2</groupId>
+      <artifactId>fastjson2</artifactId>
+      <version>2.0.52</version>
+    </dependency>
+
+    <dependency>
+      <groupId>net.sf.ehcache</groupId>
+      <artifactId>ehcache-core</artifactId>
+      <version>2.6.11</version>
+    </dependency>
+
+    <dependency>
+      <groupId>mysql</groupId>
+      <artifactId>mysql-connector-java</artifactId>
+      <version>5.1.46</version>
+    </dependency>
+
+    <dependency>
+      <groupId>com.zaxxer</groupId>
+      <artifactId>HikariCP</artifactId>
+      <version>4.0.3</version>
+    </dependency>
+
+  </dependencies>
+  <profiles>
+    <!-- 开发环境 -->
+    <profile>
+      <id>development</id>
+      <activation>
+        <activeByDefault>true</activeByDefault>
+      </activation>
+      <dependencies>
+        <dependency>
+          <groupId>ch.qos.logback</groupId>
+          <artifactId>logback-classic</artifactId>
+          <version>1.2.13</version>
+        </dependency>
+      </dependencies>
+    </profile>
+
+    <!-- 生产环境 -->
+    <profile>
+      <id>production</id>
+      <dependencies>
+        <dependency>
+          <groupId>ch.qos.logback</groupId>
+          <artifactId>logback-classic</artifactId>
+          <version>1.2.13</version>
+        </dependency>
+      </dependencies>
+      <build>
+        <plugins>
+          <plugin>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-maven-plugin</artifactId>
+            <version>2.7.4</version>
+            <configuration>
+              <mainClass>${main.class}</mainClass>
+              <excludeGroupIds>org.projectlombok</excludeGroupIds>
+            </configuration>
+            <!-- 设置执行目标 -->
+            <executions>
+              <execution>
+                <goals>
+                  <goal>repackage</goal>
+                </goals>
+              </execution>
+            </executions>
+          </plugin>
+        </plugins>
+      </build>
+    </profile>
+    <!-- assembly -->
+    <profile>
+      <id>assembly</id>
+      <dependencies>
+        <dependency>
+          <groupId>ch.qos.logback</groupId>
+          <artifactId>logback-classic</artifactId>
+          <version>1.2.13</version>
+        </dependency>
+      </dependencies>
+      <build>
+        <plugins>
+          <plugin>
+            <groupId>org.apache.maven.plugins</groupId>
+            <artifactId>maven-jar-plugin</artifactId>
+            <version>3.2.0</version>
+          </plugin>
+          <plugin>
+            <groupId>org.apache.maven.plugins</groupId>
+            <artifactId>maven-assembly-plugin</artifactId>
+            <version>3.1.1</version>
+            <configuration>
+              <archive>
+                <manifest>
+                  <mainClass>${main.class}</mainClass>
+                </manifest>
+              </archive>
+              <descriptorRefs>
+                <descriptorRef>jar-with-dependencies</descriptorRef>
+              </descriptorRefs>
+              <appendAssemblyId>false</appendAssemblyId>
+            </configuration>
+            <executions>
+              <execution>
+                <id>make-assembly</id>
+                <phase>package</phase>
+                <goals>
+                  <goal>single</goal>
+                </goals>
+              </execution>
+            </executions>
+          </plugin>
+        </plugins>
+      </build>
+    </profile>
+    <profile>
+      <id>native</id>
+      <dependencies>
+        <!-- GraalVM 环境使用 jdk log -->
+        <dependency>
+          <groupId>org.slf4j</groupId>
+          <artifactId>slf4j-jdk14</artifactId>
+          <version>1.7.31</version>
+        </dependency>
+        <!-- GraalVM -->
+        <dependency>
+          <groupId>org.graalvm.sdk</groupId>
+          <artifactId>graal-sdk</artifactId>
+          <version>${graalvm.version}</version>
+          <scope>provided</scope>
+        </dependency>
+      </dependencies>
+      <build>
+        <finalName>${project.artifactId}</finalName>
+        <plugins>
+          <plugin>
+            <groupId>org.graalvm.nativeimage</groupId>
+            <artifactId>native-image-maven-plugin</artifactId>
+            <version>21.2.0</version>
+            <executions>
+              <execution>
+                <goals>
+                  <goal>native-image</goal>
+                </goals>
+                <phase>package</phase>
+              </execution>
+            </executions>
+            <configuration>
+              <skip>false</skip>
+              <imageName>${project.artifactId}</imageName>
+              <mainClass>${main.class}</mainClass>
+              <buildArgs>
+                -H:+RemoveSaturatedTypeFlows
+                --allow-incomplete-classpath
+                --no-fallback
+              </buildArgs>
+            </configuration>
+          </plugin>
+        </plugins>
+      </build>
+    </profile>
+  </profiles>
+</project>

+ 66 - 0
frameworks/Java/tio-http-server/src/main/java/com/litongjava/tio/http/server/MainApp.java

@@ -0,0 +1,66 @@
+package com.litongjava.tio.http.server;
+
+import com.litongjava.tio.http.common.HttpConfig;
+import com.litongjava.tio.http.common.handler.ITioHttpRequestHandler;
+import com.litongjava.tio.http.server.config.EhCachePluginConfig;
+import com.litongjava.tio.http.server.config.EnjoyEngineConfig;
+import com.litongjava.tio.http.server.config.MysqlDbConfig;
+import com.litongjava.tio.http.server.controller.CacheController;
+import com.litongjava.tio.http.server.controller.DbController;
+import com.litongjava.tio.http.server.controller.IndexController;
+import com.litongjava.tio.http.server.handler.DefaultHttpRequestDispatcher;
+import com.litongjava.tio.http.server.router.DefaultHttpReqeustRouter;
+import com.litongjava.tio.http.server.router.HttpRequestRouter;
+import com.litongjava.tio.server.ServerTioConfig;
+import com.litongjava.tio.utils.environment.EnvUtils;
+
+public class MainApp {
+
+  public static void main(String[] args) {
+    long start = System.currentTimeMillis();
+    EnvUtils.load();
+    // add route
+    IndexController controller = new IndexController();
+
+    HttpRequestRouter simpleHttpRoutes = new DefaultHttpReqeustRouter();
+    simpleHttpRoutes.add("/", controller::index);
+    simpleHttpRoutes.add("/plaintext", controller::plaintext);
+    simpleHttpRoutes.add("/json", controller::json);
+
+    DbController dbQueryController = new DbController();
+    simpleHttpRoutes.add("/db", dbQueryController::db);
+    simpleHttpRoutes.add("/queries", dbQueryController::queries);
+    simpleHttpRoutes.add("/updates", dbQueryController::updates);
+    simpleHttpRoutes.add("/fortunes", dbQueryController::fortunes);
+
+    CacheController cacheController = new CacheController();
+    simpleHttpRoutes.add("/cachedQuery", cacheController::cachedQuery);
+
+    // config server
+    HttpConfig httpConfig = new HttpConfig(8080, null, null, null);
+    httpConfig.setUseSession(false);
+    httpConfig.setWelcomeFile(null);
+    httpConfig.setCheckHost(false);
+    httpConfig.setCompatible1_0(false);
+
+    ITioHttpRequestHandler requestHandler = new DefaultHttpRequestDispatcher(httpConfig, simpleHttpRoutes);
+    HttpServerStarter httpServerStarter = new HttpServerStarter(httpConfig, requestHandler);
+    ServerTioConfig serverTioConfig = httpServerStarter.getServerTioConfig();
+    // close Heartbeat
+    serverTioConfig.setHeartbeatTimeout(0);
+    serverTioConfig.statOn = false;
+    // start server
+    try {
+      new MysqlDbConfig().init();
+      new EnjoyEngineConfig().engine();
+      new EhCachePluginConfig().ehCachePlugin();
+      httpServerStarter.start();
+      long end = System.currentTimeMillis();
+      System.out.println((end - start) + "ms");
+    } catch (Exception e) {
+      e.printStackTrace();
+      System.exit(1);
+    }
+
+  }
+}

+ 12 - 0
frameworks/Java/tio-http-server/src/main/java/com/litongjava/tio/http/server/config/EhCachePluginConfig.java

@@ -0,0 +1,12 @@
+  package com.litongjava.tio.http.server.config;
+
+import com.litongjava.ehcache.EhCachePlugin;
+
+public class EhCachePluginConfig {
+
+  public EhCachePlugin ehCachePlugin() {
+    EhCachePlugin ehCachePlugin = new EhCachePlugin();
+    ehCachePlugin.start();
+    return ehCachePlugin;
+  }
+}

+ 22 - 0
frameworks/Java/tio-http-server/src/main/java/com/litongjava/tio/http/server/config/EnjoyEngineConfig.java

@@ -0,0 +1,22 @@
+package com.litongjava.tio.http.server.config;
+
+import com.jfinal.template.Engine;
+
+public class EnjoyEngineConfig {
+
+  private final String RESOURCE_BASE_PATH = "/templates/";
+
+  public Engine engine() {
+    Engine engine = Engine.use();
+    engine.setBaseTemplatePath(RESOURCE_BASE_PATH);
+    engine.setToClassPathSourceFactory();
+    // 支持模板热加载,绝大多数生产环境下也建议配置成 true,除非是极端高性能的场景
+    // engine.setDevMode(true);
+    // 配置极速模式,性能提升 13%
+    Engine.setFastMode(true);
+    // jfinal 4.9.02 新增配置:支持中文表达式、中文变量名、中文方法名、中文模板函数名
+    Engine.setChineseExpression(true);
+    return engine;
+  }
+
+}

+ 31 - 0
frameworks/Java/tio-http-server/src/main/java/com/litongjava/tio/http/server/config/MysqlDbConfig.java

@@ -0,0 +1,31 @@
+package com.litongjava.tio.http.server.config;
+
+import com.litongjava.db.activerecord.ActiveRecordPlugin;
+import com.litongjava.db.activerecord.OrderedFieldContainerFactory;
+import com.litongjava.db.hikaricp.HikariCpPlugin;
+import com.litongjava.tio.utils.environment.EnvUtils;
+
+public class MysqlDbConfig {
+
+  public void init() {
+    // start active recored
+    String jdbcUrl = EnvUtils.get("JDBC_URL");
+    // String jdbcUrl = "jdbc:mysql://192.168.3.9/hello_world";
+
+    String jdbcUser = EnvUtils.get("JDBC_USER");
+    // String jdbcUser = "root";
+
+    String jdbcPswd = EnvUtils.get("JDBC_PSWD");
+    // String jdbcPswd = "robot_123456#";
+    HikariCpPlugin hikariCpPlugin = new HikariCpPlugin(jdbcUrl, jdbcUser, jdbcPswd);
+
+    ActiveRecordPlugin arp = new ActiveRecordPlugin(hikariCpPlugin);
+    arp.setContainerFactory(new OrderedFieldContainerFactory());
+
+    // arp.setShowSql(true);
+
+    hikariCpPlugin.start();
+    boolean start = arp.start();
+    System.out.println("db started:" + start);
+  }
+}

+ 41 - 0
frameworks/Java/tio-http-server/src/main/java/com/litongjava/tio/http/server/controller/CacheController.java

@@ -0,0 +1,41 @@
+package com.litongjava.tio.http.server.controller;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+import com.alibaba.fastjson2.JSON;
+import com.litongjava.db.activerecord.Db;
+import com.litongjava.db.activerecord.Record;
+import com.litongjava.tio.http.common.HeaderName;
+import com.litongjava.tio.http.common.HeaderValue;
+import com.litongjava.tio.http.common.HttpRequest;
+import com.litongjava.tio.http.common.HttpResponse;
+import com.litongjava.tio.http.server.utils.RandomUtils;
+
+public class CacheController {
+  // private Logger log = LoggerFactory.getLogger(this.getClass());
+
+  public HttpResponse cachedQuery(HttpRequest request) {
+    String queries = request.getParam("queries");
+    List<Map<String, Object>> recordMaps = RandomUtils.randomWorldNumbers()
+        // limit
+        .limit(RandomUtils.parseQueryCount(queries)) // 限制查询数量
+        .mapToObj(id -> findByIdWithCache("world", id)) // 使用 mapToObj 将 int 映射为对象
+        .filter(Objects::nonNull) // 过滤掉 null 值
+        .map(Record::toMap) // 将每个 Record 对象转换为 Map
+        .collect(Collectors.toList()); // 收集到 List
+
+    HttpResponse httpResponse = new HttpResponse(request);
+    httpResponse.addHeader(HeaderName.Content_Type, HeaderValue.Content_Type.TEXT_PLAIN_JSON);
+    httpResponse.setBody(JSON.toJSONBytes(recordMaps));
+    return httpResponse;
+
+  }
+
+  private Record findByIdWithCache(String tableName, int id) {
+    String sql = "SELECT id, randomNumber FROM world WHERE id = ?";
+    return Db.findFirstByCache(tableName, id, sql, id);
+  }
+}

+ 127 - 0
frameworks/Java/tio-http-server/src/main/java/com/litongjava/tio/http/server/controller/DbController.java

@@ -0,0 +1,127 @@
+package com.litongjava.tio.http.server.controller;
+
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+import com.alibaba.fastjson2.JSON;
+import com.jfinal.template.Engine;
+import com.jfinal.template.Template;
+import com.litongjava.db.activerecord.Db;
+import com.litongjava.db.activerecord.Record;
+import com.litongjava.ehcache.EhCache;
+import com.litongjava.tio.http.common.HeaderName;
+import com.litongjava.tio.http.common.HeaderValue;
+import com.litongjava.tio.http.common.HttpRequest;
+import com.litongjava.tio.http.common.HttpResponse;
+import com.litongjava.tio.http.server.model.Fortune;
+import com.litongjava.tio.http.server.util.Resps;
+import com.litongjava.tio.http.server.utils.BeanConverterUtils;
+import com.litongjava.tio.http.server.utils.RandomUtils;
+
+public class DbController {
+
+  public HttpResponse db(HttpRequest request) {
+    Integer id = request.getInt("id");
+    if (id == null) {
+      id = RandomUtils.randomWorldNumber();
+    }
+
+    //System.out.println("id:" + id);
+    HttpResponse httpResponse = new HttpResponse(request);
+
+    // int id = 11;
+    // String sql="SELECT id, randomNumber FROM world WHERE id = ?";
+
+    Record recored = Db.findById("world", id);
+    if (recored != null) {
+      httpResponse.setBody(JSON.toJSONBytes(recored.toMap()));
+    } else {
+      httpResponse.setBody("{}".getBytes());
+    }
+
+    httpResponse.addHeader(HeaderName.Content_Type, HeaderValue.Content_Type.TEXT_PLAIN_JSON);
+
+    return httpResponse;
+  }
+
+  // @GetMapping("/queries")
+  public HttpResponse queries(HttpRequest request) {
+    String queries = request.getParam("queries");
+    List<Map<String, Object>> recordMaps = RandomUtils.randomWorldNumbers()
+        // limit
+        .limit(RandomUtils.parseQueryCount(queries)) // 限制查询数量
+        .mapToObj(id -> Db.findById("world", id)) // 使用 mapToObj 将 int 映射为对象
+        .filter(Objects::nonNull) // 过滤掉 null 值
+        .map(Record::toMap) // 将每个 Record 对象转换为 Map
+        .collect(Collectors.toList()); // 收集到 List
+
+    HttpResponse httpResponse = new HttpResponse(request);
+    httpResponse.addHeader(HeaderName.Content_Type, HeaderValue.Content_Type.TEXT_PLAIN_JSON);
+    httpResponse.setBody(JSON.toJSONBytes(recordMaps));
+    return httpResponse;
+  }
+
+//@GetMapping("/updates")
+  public HttpResponse updates(HttpRequest request) {
+    String queries = request.getParam("queries");
+
+    EhCache.removeAll("world");
+
+    List<Map<String, Object>> updatedRecords = RandomUtils.randomWorldNumbers()// random numbers
+        // limit
+        .limit(RandomUtils.parseQueryCount(queries))
+        // map
+        .mapToObj(id -> Db.findById("world", id))
+        // not null
+        .filter(Objects::nonNull).map(record -> {
+          int currentRandomNumber = record.getInt("randomNumber"); // "randomnumber"
+          int newRandomNumber;
+          do {
+            newRandomNumber = RandomUtils.randomWorldNumber();
+          } while (newRandomNumber == currentRandomNumber);
+
+          record.set("randomnumber", newRandomNumber);
+          Db.update("world", "id", record); // update
+          return record;
+        })
+        // tomap
+        .map(Record::toMap)
+        // to List
+        .collect(Collectors.toList());
+
+    HttpResponse httpResponse = new HttpResponse(request);
+    httpResponse.addHeader(HeaderName.Content_Type, HeaderValue.Content_Type.TEXT_PLAIN_JSON);
+    httpResponse.setBody(JSON.toJSONBytes(updatedRecords));
+    return httpResponse;
+  }
+
+  public HttpResponse fortunes(HttpRequest request) throws IllegalAccessException, InstantiationException {
+    List<Record> records = Db.find("SELECT * FROM fortune"); 
+
+    List<Fortune> fortunes = new ArrayList<>(records.size());
+    for (Record record : records) {
+      fortunes.add(BeanConverterUtils.toBean(record.toMap(), Fortune.class));
+    }
+    // 添加额外的 Fortune
+    fortunes.add(new Fortune(0L, "Additional fortune added at request time."));
+
+    // 按照消息排序
+    fortunes.sort(Comparator.comparing(Fortune::getMessage));
+
+    Map<String, Object> viewData = new HashMap<>();
+    viewData.put("fortunes", fortunes);
+
+    // 转换为 HTML
+    Engine engine = Engine.use();
+    String filename = "fortunes.html";
+    Template template = engine.getTemplate(filename);
+    String html = template.renderToString(viewData);
+
+    return Resps.html(request, html);
+  }
+}

+ 40 - 0
frameworks/Java/tio-http-server/src/main/java/com/litongjava/tio/http/server/controller/IndexController.java

@@ -0,0 +1,40 @@
+package com.litongjava.tio.http.server.controller;
+
+import com.alibaba.fastjson2.JSON;
+import com.litongjava.tio.http.common.HeaderName;
+import com.litongjava.tio.http.common.HeaderValue;
+import com.litongjava.tio.http.common.HttpRequest;
+import com.litongjava.tio.http.common.HttpResponse;
+import com.litongjava.tio.http.server.model.Message;
+import com.litongjava.tio.http.server.util.Resps;
+
+/**
+ * ab -k -n1000000 -c10 http://127.0.0.1:8080/json 
+ * ab -k -n1000000 -c10 http://127.0.0.1:8080/plaintext
+ */
+public class IndexController {
+  private static final String HELLO_WORLD = "Hello, World!";
+
+  private static final byte[] HELLO_WORLD_BYTES = HELLO_WORLD.getBytes();
+
+  public HttpResponse index(HttpRequest request) {
+    return Resps.txt(request, "tio-server");
+  }
+
+  public HttpResponse plaintext(HttpRequest request) {
+    // 更高性能的写法
+    HttpResponse ret = new HttpResponse(request);
+    ret.setBody(HELLO_WORLD_BYTES);
+    ret.addHeader(HeaderName.Content_Type, HeaderValue.Content_Type.TEXT_PLAIN_TXT);
+    return ret;
+  }
+
+  // 在IndexController中添加
+  public HttpResponse json(HttpRequest request) {
+    // 更高性能的写法
+    HttpResponse ret = new HttpResponse(request);
+    ret.setBody(JSON.toJSONString(new Message(HELLO_WORLD)).getBytes());
+    ret.addHeader(HeaderName.Content_Type, HeaderValue.Content_Type.TEXT_PLAIN_JSON);
+    return ret;
+  }
+}

+ 23 - 0
frameworks/Java/tio-http-server/src/main/java/com/litongjava/tio/http/server/model/Fortune.java

@@ -0,0 +1,23 @@
+package com.litongjava.tio.http.server.model;
+
+public final class Fortune {
+
+  public Long id;
+  public String message;
+
+  public Fortune() {
+  }
+
+  public Fortune(Long id, String message) {
+    this.id = id;
+    this.message = message;
+  }
+
+  public Long getId() {
+    return id;
+  }
+
+  public String getMessage() {
+    return message;
+  }
+}

+ 12 - 0
frameworks/Java/tio-http-server/src/main/java/com/litongjava/tio/http/server/model/Message.java

@@ -0,0 +1,12 @@
+package com.litongjava.tio.http.server.model;
+public final class Message {
+	private final String message;
+
+	public Message(String message) {
+		this.message = message;
+	}
+
+	public String getMessage() {
+		return message;
+	}
+}

+ 32 - 0
frameworks/Java/tio-http-server/src/main/java/com/litongjava/tio/http/server/model/World.java

@@ -0,0 +1,32 @@
+package com.litongjava.tio.http.server.model;
+
+public final class World {
+
+  public int id;
+  public int randomnumber;
+
+  protected World() {
+  }
+
+  public World(int id, int randomnumber) {
+    this.id = id;
+    this.randomnumber = randomnumber;
+  }
+
+  public int getId() {
+    return id;
+  }
+
+  public void setId(int id) {
+    this.id = id;
+  }
+
+  public int getRandomnumber() {
+    return randomnumber;
+  }
+
+  public void setRandomnumber(int randomnumber) {
+    this.randomnumber = randomnumber;
+  }
+
+}

+ 50 - 0
frameworks/Java/tio-http-server/src/main/java/com/litongjava/tio/http/server/services/CacheName.java

@@ -0,0 +1,50 @@
+package com.litongjava.tio.http.server.services;
+
+public class CacheName {
+  // `cacheName`(缓存名称)
+  private String name;
+  // `timeToLiveSeconds`(生存时间)和`timeToIdleSeconds`(闲置时间)。
+  private Long timeToLiveSeconds;
+  private Long timeToIdleSeconds;
+
+  public CacheName() {
+  }
+
+  public CacheName(String name, Long timeToLiveSeconds, Long timeToIdleSeconds) {
+    super();
+    this.name = name;
+    this.timeToLiveSeconds = timeToLiveSeconds;
+    this.timeToIdleSeconds = timeToIdleSeconds;
+  }
+
+  public String getName() {
+    return name;
+  }
+
+  public void setName(String name) {
+    this.name = name;
+  }
+
+  public Long getTimeToLiveSeconds() {
+    return timeToLiveSeconds;
+  }
+
+  public void setTimeToLiveSeconds(Long timeToLiveSeconds) {
+    this.timeToLiveSeconds = timeToLiveSeconds;
+  }
+
+  public Long getTimeToIdleSeconds() {
+    return timeToIdleSeconds;
+  }
+
+  public void setTimeToIdleSeconds(Long timeToIdleSeconds) {
+    this.timeToIdleSeconds = timeToIdleSeconds;
+  }
+
+  @Override
+  public String toString() {
+    return "CacheName [name=" + name + ", timeToLiveSeconds=" + timeToLiveSeconds + ", timeToIdleSeconds="
+        + timeToIdleSeconds + "]";
+  }
+
+}

+ 17 - 0
frameworks/Java/tio-http-server/src/main/java/com/litongjava/tio/http/server/services/CacheNameService.java

@@ -0,0 +1,17 @@
+package com.litongjava.tio.http.server.services;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.litongjava.model.time.Time;
+
+public class CacheNameService {
+  private CacheName demo = new CacheName("world", null, Time.MINUTE_1 * 10);
+
+  public List<CacheName> cacheNames() {
+    List<CacheName> list = new ArrayList<>();
+    list.add(demo);
+    return list;
+  }
+
+}

+ 31 - 0
frameworks/Java/tio-http-server/src/main/java/com/litongjava/tio/http/server/utils/BeanConverterUtils.java

@@ -0,0 +1,31 @@
+package com.litongjava.tio.http.server.utils;
+
+import java.lang.reflect.Field;
+import java.util.Map;
+
+public class BeanConverterUtils {
+
+  /**
+   * Map to to bean 
+   */
+  public static <T> T toBean(Map<String, Object> map, Class<T> beanClass)
+      throws IllegalAccessException, InstantiationException {
+
+    T bean = beanClass.newInstance(); // 创建 Bean 的实例
+
+    for (Field field : beanClass.getDeclaredFields()) {
+      field.setAccessible(true); // 确保私有字段也可以访问
+
+      if (map.containsKey(field.getName())) {
+        Object value = map.get(field.getName());
+
+        // 如果字段类型与值类型兼容,则设置字段的值
+        if (value != null && field.getType().isAssignableFrom(value.getClass())) {
+          field.set(bean, value);
+        }
+      }
+    }
+
+    return bean;
+  }
+}

+ 36 - 0
frameworks/Java/tio-http-server/src/main/java/com/litongjava/tio/http/server/utils/RandomUtils.java

@@ -0,0 +1,36 @@
+package com.litongjava.tio.http.server.utils;
+
+import java.util.concurrent.ThreadLocalRandom;
+import java.util.stream.IntStream;
+
+public class RandomUtils {
+
+  private static final int MIN_WORLD_NUMBER = 1;
+  private static final int MAX_WORLD_NUMBER_PLUS_ONE = 10_001;
+//  private static final int MAX_WORLD_NUMBER_PLUS_ONE = 30;
+
+  public static int randomWorldNumber() {
+    return ThreadLocalRandom.current().nextInt(MIN_WORLD_NUMBER, MAX_WORLD_NUMBER_PLUS_ONE);
+  }
+
+  public static IntStream randomWorldNumbers() {
+    return ThreadLocalRandom.current().ints(MIN_WORLD_NUMBER, MAX_WORLD_NUMBER_PLUS_ONE)
+        // distinct() allows us to avoid using Hibernate's first-level cache in
+        // the JPA-based implementation. Using a cache like that would bypass
+        // querying the database, which would violate the test requirements.
+        .distinct();
+  }
+
+  public static int parseQueryCount(String textValue) {
+    if (textValue == null) {
+      return 1;
+    }
+    int parsedValue;
+    try {
+      parsedValue = Integer.parseInt(textValue);
+    } catch (NumberFormatException e) {
+      return 1;
+    }
+    return Math.min(500, Math.max(1, parsedValue));
+  }
+}

+ 9 - 0
frameworks/Java/tio-http-server/src/main/resources/app.properties

@@ -0,0 +1,9 @@
+http.response.header.showServer=true
+
+#JDBC_URL=jdbc:mysql://192.168.3.9/hello_world?useSSL=false&allowPublicKeyRetrieval=true
+#JDBC_USER=root
+#JDBC_PSWD=robot_123456#
+
+JDBC_URL=jdbc:mysql://tfb-database/hello_world
+JDBC_USER=benchmarkdbuser
+JDBC_PSWD=benchmarkdbpass

+ 9 - 0
frameworks/Java/tio-http-server/src/main/resources/ehcache.xml

@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd" updateCheck="false">
+
+  <diskStore path="java.io.tmpdir/EhCache" />
+
+  <defaultCache eternal="false" maxElementsInMemory="10000" overflowToDisk="false" diskPersistent="false"
+                timeToIdleSeconds="1800" timeToLiveSeconds="259200" memoryStoreEvictionPolicy="LRU" />
+</ehcache>

+ 52 - 0
frameworks/Java/tio-http-server/src/main/resources/logback.xml

@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<configuration>
+  <!--定义日志文件的存储地址 勿在 LogBack 的配置中使用相对路径 -->
+  <property name="LOG_HOME" value="logs" />
+  <!--格式化输出:%d表示日期,%-6level:日志级别从左显示6个字符宽度,%m:日志消息,%n是换行符 -->
+  <property name="CONSOLE_LOG_PATTERN" value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-6level%logger{0}.%M:%L - %m%n" />
+
+  <!-- 控制台输出 -->
+  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
+    <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
+      <pattern>${CONSOLE_LOG_PATTERN}</pattern>
+    </encoder>
+  </appender>
+
+  <!-- 按照每天生成日志文件 -->
+  <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
+    <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
+      <pattern>${CONSOLE_LOG_PATTERN}</pattern>
+    </encoder>
+    <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+      <!--日志文件输出的文件名 -->
+      <fileNamePattern>${LOG_HOME}/project-name-%d{yyyy-MM-dd}.log</fileNamePattern>
+      <!--日志文件保留天数 -->
+      <maxHistory>180</maxHistory>
+    </rollingPolicy>
+    <!--日志文件最大的大小 -->
+    <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
+      <maxFileSize>10MB</maxFileSize>
+    </triggeringPolicy>
+  </appender>
+  
+  <!--专为 spring 定制 -->
+  <logger name="org.springframework" level="info" />
+  <!-- show parameters for hibernate sql 专为 Hibernate 定制 -->
+  <logger name="org.hibernate.type.descriptor.sql.BasicBinder" level="TRACE" />
+  <logger name="org.hibernate.type.descriptor.sql.BasicExtractor" level="DEBUG" />
+  <logger name="org.hibernate.SQL" level="DEBUG" />
+  <logger name="org.hibernate.engine.QueryParameters" level="DEBUG" />
+  <logger name="org.hibernate.engine.query.HQLQueryPlan" level="DEBUG" />
+
+  <!--myibatis log configure -->
+  <logger name="com.apache.ibatis" level="TRACE" />
+  <logger name="java.sql.Connection" level="DEBUG" />
+  <logger name="java.sql.Statement" level="DEBUG" />
+  <logger name="java.sql.PreparedStatement" level="DEBUG" />
+
+  <!-- 日志输出级别 和输出源 -->
+  <root level="info">
+    <appender-ref ref="STDOUT" />
+    <appender-ref ref="FILE" />
+  </root>
+</configuration>

+ 20 - 0
frameworks/Java/tio-http-server/src/main/resources/templates/fortunes.html

@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <title>Fortunes</title>
+</head>
+<body>
+<table>
+  <tr>
+    <th>id</th>
+    <th>message</th>
+  </tr>
+  #for(fortune : fortunes)
+  <tr>
+    <td>#(fortune.id)</td>
+    <td>#escape(fortune.message)</td>
+  </tr>
+  #end
+</table>
+</body>
+</html>

+ 15 - 0
frameworks/Java/tio-http-server/src/test/java/com/litongjava/tio/http/server/MainAppTest.java

@@ -0,0 +1,15 @@
+package com.litongjava.tio.http.server;
+
+import org.junit.Test;
+
+import com.litongjava.tio.utils.environment.EnvUtils;
+
+public class MainAppTest {
+
+  @Test
+  public void test() {
+    boolean boolean1 = EnvUtils.getBoolean("native", false);
+    System.out.println(boolean1);
+  }
+
+}

+ 9 - 0
frameworks/Java/tio-http-server/tio-server-native.dockerfile

@@ -0,0 +1,9 @@
+FROM litongjava/maven:3.8.8-graalvm-jdk-21-slim
+WORKDIR /t-io
+COPY pom.xml pom.xml
+COPY src src
+RUN mvn package -Pnative -q
+
+EXPOSE 8080
+
+CMD ["/t-io/target/tio-http-server-benchmark", " --native=true"]

+ 19 - 0
frameworks/Java/tio-http-server/tio-server.dockerfile

@@ -0,0 +1,19 @@
+FROM litongjava/maven:3.8.8-jdk8u391 AS builder
+WORKDIR /app
+
+COPY pom.xml pom.xml
+RUN mvn dependency:go-offline  -q
+
+COPY src src
+RUN mvn package -Passembly -q
+RUN ls -l && ls -l target
+
+FROM litongjava/jre:8u391-stable-slim
+
+WORKDIR /app
+
+COPY --from=builder /app/target/tio-http-server-benchmark-1.0.jar /app/target/tio-http-server-benchmark-1.0.jar
+
+EXPOSE 8080
+
+CMD ["java","-jar", "/app/target/tio-http-server-benchmark-1.0.jar"]

+ 2 - 2
frameworks/PHP/flight/composer.json

@@ -1,5 +1,5 @@
 {
 {
     "require": {
     "require": {
-        "mikecao/flight": "^2.0"
+        "mikecao/flight": "^3.0"
     }
     }
-}
+}

+ 2 - 3
frameworks/PHP/flight/index.php

@@ -13,9 +13,8 @@ Flight::route('/json', function() {
 // Plaintext test
 // Plaintext test
 Flight::route('/plaintext', function() {
 Flight::route('/plaintext', function() {
 	Flight::response()
 	Flight::response()
-		->header('Content-Type', 'text/plain')
-		->write('Hello, World!')
-		->send();
+	->header('Content-Type', 'text/plain');
+	echo 'Hello, World!';
 });
 });
 
 
 // DB test
 // DB test

+ 25 - 23
frameworks/PHP/webman/app/controller/Index.php

@@ -5,7 +5,10 @@ use support\Request;
 use support\bootstrap\Date;
 use support\bootstrap\Date;
 use support\bootstrap\db\Raw as Db;
 use support\bootstrap\db\Raw as Db;
 use support\Response;
 use support\Response;
-use PDO;
+use function json_encode;
+use function max;
+use function min;
+use function mt_rand;
 
 
 class Index
 class Index
 {
 {
@@ -29,7 +32,7 @@ class Index
     public function db()
     public function db()
     {
     {
         $statement = Db::$random;
         $statement = Db::$random;
-        $statement->execute([\mt_rand(1, 10000)]);
+        $statement->execute([mt_rand(1, 10000)]);
 
 
         return new Response(200, [
         return new Response(200, [
             'Content-Type' => 'application/json',
             'Content-Type' => 'application/json',
@@ -63,14 +66,11 @@ class Index
     {
     {
         $statement = Db::$random;
         $statement = Db::$random;
 
 
-        $query_count = 1;
-        if ((int) $q > 1) {
-            $query_count = \min($q, 500);
-        }
+        $query_count = min(max((int) $q, 1), 500);
 
 
         $arr = [];
         $arr = [];
         while ($query_count--) {
         while ($query_count--) {
-            $statement->execute([\mt_rand(1, 10000)]);
+            $statement->execute([mt_rand(1, 10000)]);
             $arr[] = $statement->fetch();
             $arr[] = $statement->fetch();
         }
         }
 
 
@@ -82,29 +82,31 @@ class Index
 
 
     public function updates(Request $request, $q = 1)
     public function updates(Request $request, $q = 1)
     {
     {
-        $random = Db::$random;
+        static $updates = [];
 
 
-        $query_count = 1;
-        if ((int) $q > 1) {
-            $query_count = \min($q, 500);
+        $random = Db::$random;
+        $pdo = Db::$pdo;
+        $count = min(max((int) $q, 1), 500);
+
+        $worlds = $keys = $values = [];
+        for ($i = 0; $i < $count; ++ $i) {
+            $values[] = $keys[] = $id = mt_rand(1, 10000);
+            $random->execute([$id]);
+            $row = $random->fetch();
+            $values[] = $row['randomNumber'] = mt_rand(1, 10000);
+            $worlds[] = $row;
         }
         }
-
-        $worlds = [];
-
-        while ($query_count--) {
-            $random->execute([\mt_rand(1, 10000)]);
-            $world = $random->fetch();
-            $world['randomNumber'] = \mt_rand(1, 10000);
-
-            $worlds[] = $world;
+        if (!isset($updates[$count])) {
+            $sql = 'UPDATE World SET randomNumber = CASE id' . str_repeat(' WHEN ?::INTEGER THEN ?::INTEGER ', $count) . 'END WHERE id IN (' . str_repeat('?::INTEGER,', $count - 1) . '?::INTEGER)';
+            $updates[$count] = $pdo->prepare($sql);
         }
         }
-
-        Db::update($worlds);
+        $updates[$count]->execute([...$values, ...$keys]);
 
 
         return new Response(200, [
         return new Response(200, [
             'Content-Type' => 'application/json',
             'Content-Type' => 'application/json',
             'Date'         => Date::$date
             'Date'         => Date::$date
-        ], \json_encode($worlds));
+        ], json_encode($worlds));
+
     }
     }
 
 
 
 

+ 0 - 33
frameworks/PHP/webman/support/bootstrap/db/Raw.php

@@ -31,11 +31,6 @@ class Raw implements Bootstrap
 
 
     public static PDOStatement $random;
     public static PDOStatement $random;
 
 
-    /**
-     * @var PDOStatement[]
-     */
-    public static array $update;
-
     /**
     /**
      * @param Worker $worker
      * @param Worker $worker
      *
      *
@@ -53,32 +48,4 @@ class Raw implements Bootstrap
         self::$pdo = $pdo;
         self::$pdo = $pdo;
     }
     }
 
 
-    /**
-     * Postgres bulk update
-     *
-     * @param array $worlds
-     * @return void
-     */
-    public static function update(array $worlds)
-    {
-        $rows = count($worlds);
-
-        if (!isset(self::$update[$rows])) {
-            $sql = 'UPDATE world SET randomNumber = CASE id'
-                . str_repeat(' WHEN ?::INTEGER THEN ?::INTEGER ', $rows)
-                . 'END WHERE id IN ('
-                . str_repeat('?::INTEGER,', $rows - 1) . '?::INTEGER)';
-
-            self::$update[$rows] = self::$pdo->prepare($sql);
-        }
-
-        $val = [];
-        $keys = [];
-        foreach ($worlds as $world) {
-            $val[] = $keys[] = $world['id'];
-            $val[] = $world['randomNumber'];
-        }
-
-        self::$update[$rows]->execute([...$val, ...$keys]);
-    }
 }
 }

+ 8 - 5
frameworks/PHP/workerman/Mysql.php

@@ -34,9 +34,10 @@ class Mysql
     {
     {
         $count = min(max((int) $request->get('q'), 1), 500);
         $count = min(max((int) $request->get('q'), 1), 500);
         $arr = [];
         $arr = [];
+        $world = $this->world;
         while ($count--) {
         while ($count--) {
-            $this->world->execute([mt_rand(1, 10000)]);
-            $arr[] = $this->world->fetch();
+            $world->execute([mt_rand(1, 10000)]);
+            $arr[] = $world->fetch();
         }
         }
         return $arr;
         return $arr;
     }
     }
@@ -45,11 +46,13 @@ class Mysql
     {
     {
         $count = min(max((int) $request->get('q'), 1), 500);
         $count = min(max((int) $request->get('q'), 1), 500);
         $arr = [];
         $arr = [];
+        $world = $this->world;
+        $update = $this->update;
         while ($count--) {
         while ($count--) {
             $id = mt_rand(1, 10000);
             $id = mt_rand(1, 10000);
-            $this->world->execute([$id]);
-            $item = $this->world->fetch();
-            $this->update->execute(
+            $world->execute([$id]);
+            $item = $world->fetch();
+            $update->execute(
                 [$item['randomNumber'] = mt_rand(1, 10000), $id]
                 [$item['randomNumber'] = mt_rand(1, 10000), $id]
             );
             );
             $arr[] = $item;
             $arr[] = $item;

+ 3 - 2
frameworks/PHP/workerman/Pgsql.php

@@ -31,10 +31,11 @@ class Pgsql extends Mysql
         $queries = $request->get('q');
         $queries = $request->get('q');
         $worlds = $keys = $values = [];
         $worlds = $keys = $values = [];
         $count = min(max((int) $queries, 1), 500);
         $count = min(max((int) $queries, 1), 500);
+        $random = $this->random;
         for ($i = 0; $i < $count; ++ $i) {
         for ($i = 0; $i < $count; ++ $i) {
             $values[] = $keys[] = $id = mt_rand(1, 10000);
             $values[] = $keys[] = $id = mt_rand(1, 10000);
-            $this->random->execute([$id]);
-            $row = $this->random->fetch();
+            $random->execute([$id]);
+            $row = $random->fetch();
             $values[] = $row['randomNumber'] = mt_rand(1, 10000);
             $values[] = $row['randomNumber'] = mt_rand(1, 10000);
             $worlds[] = $row;
             $worlds[] = $row;
         }
         }

+ 0 - 1
frameworks/PHP/workerman/benchmark_config.json

@@ -1,6 +1,5 @@
 {
 {
   "framework": "workerman",
   "framework": "workerman",
-  "maintainers": ["walkor"],
   "tests": [{
   "tests": [{
     "default": {
     "default": {
       "dockerfile": "workerman-jit.dockerfile",
       "dockerfile": "workerman-jit.dockerfile",

+ 2 - 7
frameworks/Ruby/rack-sequel/Gemfile.lock

@@ -3,14 +3,10 @@ GEM
   specs:
   specs:
     base64 (0.2.0)
     base64 (0.2.0)
     bigdecimal (3.1.8)
     bigdecimal (3.1.8)
-    json (2.8.2)
+    json (2.9.1)
     kgio (2.11.4)
     kgio (2.11.4)
     mysql2 (0.5.6)
     mysql2 (0.5.6)
     nio4r (2.7.4)
     nio4r (2.7.4)
-    oj (3.16.7)
-      bigdecimal (>= 3.0)
-      ostruct (>= 0.2)
-    ostruct (0.6.1)
     passenger (6.0.23)
     passenger (6.0.23)
       rack (>= 1.6.13)
       rack (>= 1.6.13)
       rackup
       rackup
@@ -42,9 +38,8 @@ DEPENDENCIES
   base64
   base64
   jdbc-mysql (~> 5.1)
   jdbc-mysql (~> 5.1)
   jdbc-postgres (~> 9.4)
   jdbc-postgres (~> 9.4)
-  json (~> 2.0)
+  json (~> 2.8)
   mysql2 (~> 0.4)
   mysql2 (~> 0.4)
-  oj (~> 3.14)
   passenger (~> 6.0)
   passenger (~> 6.0)
   pg (~> 1.5)
   pg (~> 1.5)
   puma (~> 6.4)
   puma (~> 6.4)

+ 1 - 1
frameworks/Ruby/rack/Gemfile.lock

@@ -53,7 +53,7 @@ GEM
     io-endpoint (0.14.0)
     io-endpoint (0.14.0)
     io-event (1.7.3)
     io-event (1.7.3)
     io-stream (0.6.1)
     io-stream (0.6.1)
-    json (2.8.2)
+    json (2.9.1)
     kgio (2.11.4)
     kgio (2.11.4)
     language_server-protocol (3.17.0.3)
     language_server-protocol (3.17.0.3)
     localhost (1.3.1)
     localhost (1.3.1)

+ 1 - 1
frameworks/Ruby/rails/Gemfile.lock

@@ -139,7 +139,7 @@ GEM
     irb (1.14.1)
     irb (1.14.1)
       rdoc (>= 4.0.0)
       rdoc (>= 4.0.0)
       reline (>= 0.4.2)
       reline (>= 0.4.2)
-    json (2.8.1)
+    json (2.9.1)
     localhost (1.3.1)
     localhost (1.3.1)
     logger (1.6.2)
     logger (1.6.2)
     loofah (2.23.1)
     loofah (2.23.1)

+ 1 - 0
frameworks/Ruby/rails/app/controllers/hello_world_controller.rb

@@ -30,6 +30,7 @@ class HelloWorldController < ApplicationController
     @fortunes = Fortune.all.to_a
     @fortunes = Fortune.all.to_a
     @fortunes << Fortune.new(id: 0, message: 'Additional fortune added at request time.')
     @fortunes << Fortune.new(id: 0, message: 'Additional fortune added at request time.')
     @fortunes.sort_by!(&:message)
     @fortunes.sort_by!(&:message)
+    render :fortune
   end
   end
 
 
   def update
   def update

+ 1 - 1
frameworks/Ruby/roda-sequel/Gemfile.lock

@@ -4,7 +4,7 @@ GEM
     base64 (0.2.0)
     base64 (0.2.0)
     bigdecimal (3.1.8)
     bigdecimal (3.1.8)
     erubi (1.13.0)
     erubi (1.13.0)
-    json (2.8.1)
+    json (2.9.1)
     kgio (2.11.4)
     kgio (2.11.4)
     mysql2 (0.5.6)
     mysql2 (0.5.6)
     nio4r (2.7.4)
     nio4r (2.7.4)

+ 1 - 1
frameworks/Ruby/sinatra/Gemfile

@@ -1,6 +1,6 @@
 source 'https://rubygems.org'
 source 'https://rubygems.org'
 
 
-gem 'activerecord', '~> 7.2', require: 'active_record'
+gem 'activerecord', '~> 8.0', require: 'active_record'
 gem 'json', '~> 2.8'
 gem 'json', '~> 2.8'
 gem 'sinatra', '~> 4.0', require: 'sinatra/base'
 gem 'sinatra', '~> 4.0', require: 'sinatra/base'
 
 

+ 26 - 22
frameworks/Ruby/sinatra/Gemfile.lock

@@ -1,14 +1,15 @@
 GEM
 GEM
   remote: https://rubygems.org/
   remote: https://rubygems.org/
   specs:
   specs:
-    activemodel (7.2.1.1)
-      activesupport (= 7.2.1.1)
-    activerecord (7.2.1.1)
-      activemodel (= 7.2.1.1)
-      activesupport (= 7.2.1.1)
+    activemodel (8.0.1)
+      activesupport (= 8.0.1)
+    activerecord (8.0.1)
+      activemodel (= 8.0.1)
+      activesupport (= 8.0.1)
       timeout (>= 0.4.0)
       timeout (>= 0.4.0)
-    activesupport (7.2.1.1)
+    activesupport (8.0.1)
       base64
       base64
+      benchmark (>= 0.3)
       bigdecimal
       bigdecimal
       concurrent-ruby (~> 1.0, >= 1.3.1)
       concurrent-ruby (~> 1.0, >= 1.3.1)
       connection_pool (>= 2.2.5)
       connection_pool (>= 2.2.5)
@@ -18,63 +19,66 @@ GEM
       minitest (>= 5.1)
       minitest (>= 5.1)
       securerandom (>= 0.3)
       securerandom (>= 0.3)
       tzinfo (~> 2.0, >= 2.0.5)
       tzinfo (~> 2.0, >= 2.0.5)
+      uri (>= 0.13.1)
     agoo (2.15.13)
     agoo (2.15.13)
     base64 (0.2.0)
     base64 (0.2.0)
+    benchmark (0.4.0)
     bigdecimal (3.1.8)
     bigdecimal (3.1.8)
     concurrent-ruby (1.3.4)
     concurrent-ruby (1.3.4)
     connection_pool (2.4.1)
     connection_pool (2.4.1)
     drb (2.2.1)
     drb (2.2.1)
     i18n (1.14.6)
     i18n (1.14.6)
       concurrent-ruby (~> 1.0)
       concurrent-ruby (~> 1.0)
-    json (2.8.1)
+    json (2.9.1)
     kgio (2.11.4)
     kgio (2.11.4)
-    logger (1.6.1)
-    minitest (5.25.1)
+    logger (1.6.4)
+    minitest (5.25.4)
     mustermann (3.0.3)
     mustermann (3.0.3)
       ruby2_keywords (~> 0.0.1)
       ruby2_keywords (~> 0.0.1)
     mysql2 (0.5.6)
     mysql2 (0.5.6)
     nio4r (2.7.4)
     nio4r (2.7.4)
-    passenger (6.0.23)
+    passenger (6.0.24)
       rack (>= 1.6.13)
       rack (>= 1.6.13)
-      rackup
+      rackup (>= 2.0.0)
       rake (>= 12.3.3)
       rake (>= 12.3.3)
-    pg (1.5.8)
+    pg (1.5.9)
     puma (6.5.0)
     puma (6.5.0)
       nio4r (~> 2.0)
       nio4r (~> 2.0)
     rack (3.1.8)
     rack (3.1.8)
-    rack-protection (4.0.0)
+    rack-protection (4.1.1)
       base64 (>= 0.1.0)
       base64 (>= 0.1.0)
+      logger (>= 1.6.0)
       rack (>= 3.0.0, < 4)
       rack (>= 3.0.0, < 4)
     rack-session (2.0.0)
     rack-session (2.0.0)
       rack (>= 3.0.0)
       rack (>= 3.0.0)
-    rackup (2.1.0)
+    rackup (2.2.1)
       rack (>= 3)
       rack (>= 3)
-      webrick (~> 1.8)
     raindrops (0.20.1)
     raindrops (0.20.1)
     rake (13.2.1)
     rake (13.2.1)
     ruby2_keywords (0.0.5)
     ruby2_keywords (0.0.5)
-    securerandom (0.3.1)
-    sinatra (4.0.0)
+    securerandom (0.4.1)
+    sinatra (4.1.1)
+      logger (>= 1.6.0)
       mustermann (~> 3.0)
       mustermann (~> 3.0)
       rack (>= 3.0.0, < 4)
       rack (>= 3.0.0, < 4)
-      rack-protection (= 4.0.0)
+      rack-protection (= 4.1.1)
       rack-session (>= 2.0.0, < 3)
       rack-session (>= 2.0.0, < 3)
       tilt (~> 2.0)
       tilt (~> 2.0)
-    tilt (2.4.0)
-    timeout (0.4.1)
+    tilt (2.5.0)
+    timeout (0.4.3)
     tzinfo (2.0.6)
     tzinfo (2.0.6)
       concurrent-ruby (~> 1.0)
       concurrent-ruby (~> 1.0)
     unicorn (6.1.0)
     unicorn (6.1.0)
       kgio (~> 2.6)
       kgio (~> 2.6)
       raindrops (~> 0.7)
       raindrops (~> 0.7)
-    webrick (1.8.2)
+    uri (1.0.2)
 
 
 PLATFORMS
 PLATFORMS
   ruby
   ruby
   x86_64-darwin-23
   x86_64-darwin-23
 
 
 DEPENDENCIES
 DEPENDENCIES
-  activerecord (~> 7.2)
+  activerecord (~> 8.0)
   agoo
   agoo
   json (~> 2.8)
   json (~> 2.8)
   mysql2 (~> 0.5)
   mysql2 (~> 0.5)

File diff suppressed because it is too large
+ 345 - 252
frameworks/Rust/axum/Cargo.lock


+ 20 - 19
frameworks/Rust/axum/Cargo.toml

@@ -39,48 +39,49 @@ simd-json = [
 ]
 ]
 
 
 [dependencies]
 [dependencies]
-axum = { version = "0.7.6", default-features = false, features = [
+axum = { version = "0.7.9", default-features = false, features = [
     "json",
     "json",
     "query",
     "query",
     "http1",
     "http1",
     "tokio",
     "tokio",
 ] }
 ] }
 deadpool = { version = "0.12.1", features = ["rt_tokio_1", "serde", "managed"] }
 deadpool = { version = "0.12.1", features = ["rt_tokio_1", "serde", "managed"] }
-deadpool-postgres = { version = "0.14.0", features = ["rt_tokio_1", "serde"] }
+deadpool-postgres = { version = "0.14.1", features = ["rt_tokio_1", "serde"] }
 dotenv = "0.15.0"
 dotenv = "0.15.0"
-futures = "0.3.30"
-futures-util = "0.3.30"
-mongodb = { version = "2.8.0", features = [
+futures = "0.3.31"
+futures-util = "0.3.31"
+mongodb = { version = "3.1.1", features = [
     "zstd-compression",
     "zstd-compression",
     "snappy-compression",
     "snappy-compression",
     "zlib-compression",
     "zlib-compression",
 ] }
 ] }
 num_cpus = "1.16.0"
 num_cpus = "1.16.0"
 rand = { version = "0.8.5", features = ["small_rng"] }
 rand = { version = "0.8.5", features = ["small_rng"] }
-serde = { version = "1.0.196", features = ["derive"] }
-serde_json = "1.0.127"
-sqlx = { version = "0.7.3", features = [
+serde = { version = "1.0.216", features = ["derive"] }
+serde_json = "1.0.134"
+sqlx = { version = "0.8.2", features = [
     "postgres",
     "postgres",
     "macros",
     "macros",
     "runtime-tokio",
     "runtime-tokio",
     "tls-rustls",
     "tls-rustls",
 ] }
 ] }
-tokio = { version = "1.39.3", features = ["full"] }
+tokio = { version = "1.42.0", features = ["full"] }
 tokio-pg-mapper = { version = "0.2.0" }
 tokio-pg-mapper = { version = "0.2.0" }
 tokio-pg-mapper-derive = { version = "0.2.0" }
 tokio-pg-mapper-derive = { version = "0.2.0" }
-tokio-postgres = { version = "0.7.11" }
-tower = { version = "0.5.0", features = ["util"] }
-tower-http = { version = "0.5.2", features = ["set-header"] }
+tokio-postgres = { version = "0.7.12" }
+tower = { version = "0.5.2", features = ["util"] }
+tower-http = { version = "0.6.2", features = ["set-header"] }
 yarte = "0.15.7"
 yarte = "0.15.7"
-simd-json = { version = "0.13.8", optional = true }
-axum-core = { version = "0.4.3", optional = true }
+simd-json = { version = "0.14.3", optional = true }
+axum-core = { version = "0.4.5", optional = true }
 mime = { version = "0.3.17", optional = true }
 mime = { version = "0.3.17", optional = true }
-bytes = { version = "1.5.0", optional = true }
-serde_path_to_error = { version = "0.1.15", optional = true }
-moka = { version = "0.12.8", features = ["future"] }
-socket2 = "0.5.7"
-hyper = { version = "1.4", features = ["server", "http1"] }
+bytes = { version = "1.9.0", optional = true }
+serde_path_to_error = { version = "0.1.16", optional = true }
+socket2 = "0.5.8"
+hyper = { version = "1.5", features = ["server", "http1"] }
 hyper-util = { version = "0.1", features = ["tokio", "server-auto", "http1"] }
 hyper-util = { version = "0.1", features = ["tokio", "server-auto", "http1"] }
+quick_cache = "0.6.9"
+mimalloc = "0.1.43"
 
 
 
 
 [profile.release]
 [profile.release]

+ 3 - 5
frameworks/Rust/axum/README.md

@@ -27,10 +27,11 @@ built with Tokio, Tower, and Hyper.
 ## Notable Points (both performance and build)
 ## Notable Points (both performance and build)
 
 
 - Use of `async`.
 - Use of `async`.
-- Use of most recent versions of Rust, `axum` and dependencies.
+- Use of the most recent versions of Rust, `axum` and dependencies.
 - (Disabled by default) Compile-time swap-in of `simd-json` instead of `serde_json` for faster JSON serialization.
 - (Disabled by default) Compile-time swap-in of `simd-json` instead of `serde_json` for faster JSON serialization.
 - Release binaries are stripped and compiled with CPU native.
 - Release binaries are stripped and compiled with CPU native.
-- Sockets configured with TCP_NODELAY and to support an increased number of pending connections.
+- Sockets configured with `TCP_NODELAY` and to support an increased number of pending connections.
+- For very simple benchmarks, use of a separate, single-threaded Tokio runtime for each thread.
 - Server configured to serve HTTP/1 only, with no need for websockets.
 - Server configured to serve HTTP/1 only, with no need for websockets.
 - Separation of build and deployment containers using multi-stage builds.
 - Separation of build and deployment containers using multi-stage builds.
 - Deployment into Google's minimal `distroless-cc` container.
 - Deployment into Google's minimal `distroless-cc` container.
@@ -39,8 +40,5 @@ built with Tokio, Tower, and Hyper.
 - Use of PostgreSQL prepared statements cache (where supported).
 - Use of PostgreSQL prepared statements cache (where supported).
 - Use of PostgreSQL arrays to execute multi-row database updates with a single `UPDATE` query.
 - Use of PostgreSQL arrays to execute multi-row database updates with a single `UPDATE` query.
   - This is permitted by the [test requirements](https://github.com/TechEmpower/FrameworkBenchmarks/wiki/Project-Information-Framework-Tests-Overview#database-updates), step (ix).
   - This is permitted by the [test requirements](https://github.com/TechEmpower/FrameworkBenchmarks/wiki/Project-Information-Framework-Tests-Overview#database-updates), step (ix).
-- In version 0.7.6 (as yet unreleased), a native API to set TCP_NODELAY will be included.
-  - https://github.com/tokio-rs/axum/pull/2653/
-  - https://github.com/tokio-rs/axum/issues/2521
 - More performance improvements are to be expected in version 0.8:
 - More performance improvements are to be expected in version 0.8:
   - https://github.com/tokio-rs/axum/issues/1827
   - https://github.com/tokio-rs/axum/issues/1827

+ 2 - 2
frameworks/Rust/axum/axum.dockerfile

@@ -1,4 +1,4 @@
-FROM docker.io/rust:1.80-slim-bookworm AS builder
+FROM docker.io/rust:1.83-slim-bookworm AS builder
 
 
 RUN apt-get update && apt-get install -y --no-install-recommends \
 RUN apt-get update && apt-get install -y --no-install-recommends \
     pkg-config libssl-dev \
     pkg-config libssl-dev \
@@ -18,7 +18,7 @@ ENV POSTGRES_MIN_POOL_SIZE=56
 ENV POSTGRES_MAX_POOL_SIZE=56
 ENV POSTGRES_MAX_POOL_SIZE=56
 ENV MONGODB_URL=mongodb://tfb-database:27017
 ENV MONGODB_URL=mongodb://tfb-database:27017
 ENV MONGODB_MIN_POOL_SIZE=28
 ENV MONGODB_MIN_POOL_SIZE=28
-ENV MONGODB_MAX_POOL_SIZE=14
+ENV MONGODB_MAX_POOL_SIZE=28
 COPY --from=builder /build/target/release/axum* /app/
 COPY --from=builder /build/target/release/axum* /app/
 EXPOSE 8000
 EXPOSE 8000
 CMD ["/app/axum"]
 CMD ["/app/axum"]

+ 6 - 8
frameworks/Rust/axum/src/common/mod.rs

@@ -17,10 +17,9 @@ pub const SELECT_WORLD_BY_ID: &str =
 pub const SELECT_ALL_CACHED_WORLDS: &str =
 pub const SELECT_ALL_CACHED_WORLDS: &str =
     "SELECT id, randomnumber FROM world ORDER BY id";
     "SELECT id, randomnumber FROM world ORDER BY id";
 #[allow(dead_code)]
 #[allow(dead_code)]
-pub const UPDATE_WORLDS: &str = "WITH vals AS (SELECT * FROM UNNEST($1::int[], $2::int[]) AS v(id, rnum))
-    UPDATE world SET randomnumber = new.rnum FROM
-  (SELECT w.id, v.rnum FROM world w INNER JOIN vals v ON v.id = w.id ORDER BY w.id FOR UPDATE) AS new
-  WHERE world.id = new.id";
+pub const UPDATE_WORLDS: &str = r#"UPDATE world SET randomnumber = new.rnum FROM
+    (SELECT * FROM UNNEST($1::int[], $2::int[]) AS v(id, rnum) ORDER BY 1) AS new
+WHERE world.id = new.id"#;
 
 
 /// Return the value of an environment variable.
 /// Return the value of an environment variable.
 #[allow(dead_code)]
 #[allow(dead_code)]
@@ -41,11 +40,10 @@ pub fn random_id(rng: &mut SmallRng) -> i32 {
     rng.gen_range(1..10_001)
     rng.gen_range(1..10_001)
 }
 }
 
 
-/// Generate vector of integers in the range 1 to 10,000 (inclusive)
+/// Generate an iterator of integers in the range 1 to 10,000 (inclusive)
 #[allow(dead_code)]
 #[allow(dead_code)]
 #[inline(always)]
 #[inline(always)]
-pub fn random_ids(rng: &mut SmallRng, count: usize) -> Vec<i32> {
+pub fn random_ids(rng: &mut SmallRng, count: usize) -> impl Iterator<Item = i32> + use<'_> {
     rng.sample_iter(Uniform::new(1, 10_001))
     rng.sample_iter(Uniform::new(1, 10_001))
         .take(count)
         .take(count)
-        .collect()
-}
+}

+ 4 - 0
frameworks/Rust/axum/src/main.rs

@@ -4,6 +4,10 @@ mod server;
 use axum::{http::StatusCode, response::IntoResponse, routing::get, Router};
 use axum::{http::StatusCode, response::IntoResponse, routing::get, Router};
 use common::models::Message;
 use common::models::Message;
 use dotenv::dotenv;
 use dotenv::dotenv;
+use mimalloc::MiMalloc;
+
+#[global_allocator]
+static GLOBAL: MiMalloc = MiMalloc;
 
 
 #[cfg(not(feature = "simd-json"))]
 #[cfg(not(feature = "simd-json"))]
 use axum::Json;
 use axum::Json;

+ 8 - 10
frameworks/Rust/axum/src/main_mongo.rs

@@ -14,8 +14,7 @@ use axum::Json;
 #[cfg(feature = "simd-json")]
 #[cfg(feature = "simd-json")]
 use common::simd_json::Json;
 use common::simd_json::Json;
 use common::{
 use common::{
-    models::{FortuneInfo, World},
-    random_ids,
+    models::{FortuneInfo, World}, random_id
 };
 };
 use dotenv::dotenv;
 use dotenv::dotenv;
 use mongodb::{
 use mongodb::{
@@ -24,6 +23,10 @@ use mongodb::{
 };
 };
 use rand::{rngs::SmallRng, thread_rng, Rng, SeedableRng};
 use rand::{rngs::SmallRng, thread_rng, Rng, SeedableRng};
 use yarte::Template;
 use yarte::Template;
+use mimalloc::MiMalloc;
+
+#[global_allocator]
+static GLOBAL: MiMalloc = MiMalloc;
 
 
 use common::{
 use common::{
     get_env,
     get_env,
@@ -58,9 +61,7 @@ async fn queries(
     let q = parse_params(params);
     let q = parse_params(params);
 
 
     let mut rng = SmallRng::from_rng(&mut thread_rng()).unwrap();
     let mut rng = SmallRng::from_rng(&mut thread_rng()).unwrap();
-    let ids = random_ids(&mut rng, q);
-
-    let worlds = find_worlds(db, ids).await;
+    let worlds = find_worlds(db, &mut rng, q).await;
     let results = worlds.expect("worlds could not be retrieved");
     let results = worlds.expect("worlds could not be retrieved");
 
 
     (StatusCode::OK, Json(results))
     (StatusCode::OK, Json(results))
@@ -73,17 +74,14 @@ async fn updates(
     let q = parse_params(params);
     let q = parse_params(params);
 
 
     let mut rng = SmallRng::from_rng(&mut thread_rng()).unwrap();
     let mut rng = SmallRng::from_rng(&mut thread_rng()).unwrap();
-    let ids = random_ids(&mut rng, q);
 
 
-    let worlds = find_worlds(db.clone(), ids)
+    let worlds = find_worlds(db.clone(), &mut  rng, q)
         .await
         .await
         .expect("worlds could not be retrieved");
         .expect("worlds could not be retrieved");
     let mut updated_worlds: Vec<World> = Vec::with_capacity(q);
     let mut updated_worlds: Vec<World> = Vec::with_capacity(q);
 
 
     for mut world in worlds {
     for mut world in worlds {
-        let random_number = (rng.gen::<u32>() % 10_000 + 1) as i32;
-
-        world.random_number = random_number;
+        world.random_number = random_id(&mut rng);
         updated_worlds.push(world);
         updated_worlds.push(world);
     }
     }
 
 

+ 10 - 10
frameworks/Rust/axum/src/main_mongo_raw.rs

@@ -2,7 +2,7 @@ mod common;
 mod mongo_raw;
 mod mongo_raw;
 mod server;
 mod server;
 
 
-use common::{models::World, random_id, random_ids};
+use common::{models::World, random_id};
 use mongo_raw::database::{
 use mongo_raw::database::{
     find_world_by_id, find_worlds, update_worlds, DatabaseConnection,
     find_world_by_id, find_worlds, update_worlds, DatabaseConnection,
 };
 };
@@ -17,6 +17,11 @@ use axum::{
     extract::Query, http::StatusCode, response::IntoResponse, routing::get, Router,
     extract::Query, http::StatusCode, response::IntoResponse, routing::get, Router,
 };
 };
 
 
+use mimalloc::MiMalloc;
+
+#[global_allocator]
+static GLOBAL: MiMalloc = MiMalloc;
+
 #[cfg(not(feature = "simd-json"))]
 #[cfg(not(feature = "simd-json"))]
 use axum::Json;
 use axum::Json;
 #[cfg(feature = "simd-json")]
 #[cfg(feature = "simd-json")]
@@ -27,7 +32,7 @@ use mongodb::{
     options::{ClientOptions, Compressor},
     options::{ClientOptions, Compressor},
     Client,
     Client,
 };
 };
-use rand::{rngs::SmallRng, thread_rng, Rng, SeedableRng};
+use rand::{rngs::SmallRng, thread_rng, SeedableRng};
 
 
 async fn db(DatabaseConnection(db): DatabaseConnection) -> impl IntoResponse {
 async fn db(DatabaseConnection(db): DatabaseConnection) -> impl IntoResponse {
     let mut rng = SmallRng::from_rng(&mut thread_rng()).unwrap();
     let mut rng = SmallRng::from_rng(&mut thread_rng()).unwrap();
@@ -48,9 +53,7 @@ async fn queries(
     let q = parse_params(params);
     let q = parse_params(params);
 
 
     let mut rng = SmallRng::from_rng(&mut thread_rng()).unwrap();
     let mut rng = SmallRng::from_rng(&mut thread_rng()).unwrap();
-    let ids = random_ids(&mut rng, q);
-
-    let worlds = find_worlds(db, ids).await;
+    let worlds = find_worlds(db, &mut rng, q).await;
     let results = worlds.expect("worlds could not be retrieved");
     let results = worlds.expect("worlds could not be retrieved");
 
 
     (StatusCode::OK, Json(results))
     (StatusCode::OK, Json(results))
@@ -64,16 +67,13 @@ async fn updates(
 
 
     let mut rng = SmallRng::from_rng(&mut thread_rng()).unwrap();
     let mut rng = SmallRng::from_rng(&mut thread_rng()).unwrap();
 
 
-    let ids = random_ids(&mut rng, q);
-    let worlds = find_worlds(db.clone(), ids)
+    let worlds = find_worlds(db.clone(), &mut rng, q)
         .await
         .await
         .expect("worlds could not be retrieved");
         .expect("worlds could not be retrieved");
     let mut updated_worlds: Vec<World> = Vec::with_capacity(q);
     let mut updated_worlds: Vec<World> = Vec::with_capacity(q);
 
 
     for mut world in worlds {
     for mut world in worlds {
-        let random_number = (rng.gen::<u32>() % 10_000 + 1) as i32;
-
-        world.random_number = random_number;
+        world.random_number = random_id(&mut rng);
         updated_worlds.push(world);
         updated_worlds.push(world);
     }
     }
 
 

+ 4 - 0
frameworks/Rust/axum/src/main_pg.rs

@@ -7,6 +7,10 @@ use axum::{
 use dotenv::dotenv;
 use dotenv::dotenv;
 use rand::{rngs::SmallRng, thread_rng, SeedableRng};
 use rand::{rngs::SmallRng, thread_rng, SeedableRng};
 use yarte::Template;
 use yarte::Template;
+use mimalloc::MiMalloc;
+
+#[global_allocator]
+static GLOBAL: MiMalloc = MiMalloc;
 
 
 #[cfg(not(feature = "simd-json"))]
 #[cfg(not(feature = "simd-json"))]
 use axum::Json;
 use axum::Json;

+ 4 - 0
frameworks/Rust/axum/src/main_pg_pool.rs

@@ -15,6 +15,10 @@ use dotenv::dotenv;
 use futures_util::{stream::FuturesUnordered, TryStreamExt};
 use futures_util::{stream::FuturesUnordered, TryStreamExt};
 use rand::{rngs::SmallRng, thread_rng, SeedableRng};
 use rand::{rngs::SmallRng, thread_rng, SeedableRng};
 use yarte::Template;
 use yarte::Template;
+use mimalloc::MiMalloc;
+
+#[global_allocator]
+static GLOBAL: MiMalloc = MiMalloc;
 
 
 mod server;
 mod server;
 
 

+ 12 - 12
frameworks/Rust/axum/src/main_sqlx.rs

@@ -12,10 +12,14 @@ use axum::{
     Router,
     Router,
 };
 };
 use dotenv::dotenv;
 use dotenv::dotenv;
-use moka::future::Cache;
+use quick_cache::sync::Cache;
 use rand::{rngs::SmallRng, thread_rng, SeedableRng};
 use rand::{rngs::SmallRng, thread_rng, SeedableRng};
 use sqlx::models::World;
 use sqlx::models::World;
 use yarte::Template;
 use yarte::Template;
+use mimalloc::MiMalloc;
+
+#[global_allocator]
+static GLOBAL: MiMalloc = MiMalloc;
 
 
 #[cfg(not(feature = "simd-json"))]
 #[cfg(not(feature = "simd-json"))]
 use axum::Json;
 use axum::Json;
@@ -55,10 +59,9 @@ async fn queries(
 ) -> impl IntoResponse {
 ) -> impl IntoResponse {
     let mut rng = SmallRng::from_rng(&mut thread_rng()).unwrap();
     let mut rng = SmallRng::from_rng(&mut thread_rng()).unwrap();
     let count = parse_params(params);
     let count = parse_params(params);
-    let ids = random_ids(&mut rng, count);
     let mut worlds: Vec<World> = Vec::with_capacity(count);
     let mut worlds: Vec<World> = Vec::with_capacity(count);
 
 
-    for id in &ids {
+    for id in random_ids(&mut rng, count) {
         let world: World = ::sqlx::query_as(common::SELECT_WORLD_BY_ID)
         let world: World = ::sqlx::query_as(common::SELECT_WORLD_BY_ID)
             .bind(id)
             .bind(id)
             .fetch_one(&mut *db.acquire().await.unwrap())
             .fetch_one(&mut *db.acquire().await.unwrap())
@@ -98,10 +101,10 @@ async fn cache(
 ) -> impl IntoResponse {
 ) -> impl IntoResponse {
     let count = parse_params(params);
     let count = parse_params(params);
     let mut rng = SmallRng::from_rng(&mut thread_rng()).unwrap();
     let mut rng = SmallRng::from_rng(&mut thread_rng()).unwrap();
-    let mut worlds: Vec<Option<Arc<World>>> = Vec::with_capacity(count);
-
+    let mut worlds: Vec<Option<World>> = Vec::with_capacity(count);
+    
     for id in random_ids(&mut rng, count) {
     for id in random_ids(&mut rng, count) {
-        worlds.push(cache.get(&id).await);
+        worlds.push(cache.get(&id));
     }
     }
 
 
     (StatusCode::OK, Json(worlds))
     (StatusCode::OK, Json(worlds))
@@ -115,7 +118,7 @@ async fn preload_cache(AppState { db, cache }: &AppState) {
         .expect("error loading worlds");
         .expect("error loading worlds");
 
 
     for world in worlds {
     for world in worlds {
-        cache.insert(world.id, Arc::new(world)).await;
+        cache.insert(world.id, world);
     }
     }
 }
 }
 
 
@@ -123,7 +126,7 @@ async fn preload_cache(AppState { db, cache }: &AppState) {
 #[derive(Clone)]
 #[derive(Clone)]
 struct AppState {
 struct AppState {
     db: PgPool,
     db: PgPool,
-    cache: Cache<i32, Arc<World>>,
+    cache: Arc<Cache<i32, World>>,
 }
 }
 
 
 #[tokio::main]
 #[tokio::main]
@@ -136,10 +139,7 @@ async fn main() {
 
 
     let state = AppState {
     let state = AppState {
         db: create_pool(database_url, max_pool_size, min_pool_size).await,
         db: create_pool(database_url, max_pool_size, min_pool_size).await,
-        cache: Cache::builder()
-        .initial_capacity(10000)
-        .max_capacity(10000)
-        .build()
+        cache: Arc::new(Cache::new(10_000))
     };
     };
 
 
     // Prime the cache with CachedWorld objects
     // Prime the cache with CachedWorld objects

+ 7 - 7
frameworks/Rust/axum/src/mongo/database.rs

@@ -3,8 +3,9 @@ use std::{convert::Infallible, io};
 use axum::{async_trait, extract::FromRequestParts, http::request::Parts};
 use axum::{async_trait, extract::FromRequestParts, http::request::Parts};
 use futures_util::{stream::FuturesUnordered, StreamExt, TryStreamExt};
 use futures_util::{stream::FuturesUnordered, StreamExt, TryStreamExt};
 use mongodb::{bson::doc, Database};
 use mongodb::{bson::doc, Database};
+use rand::rngs::SmallRng;
 
 
-use crate::common::models::{Fortune, World};
+use crate::common::{models::{Fortune, World}, random_ids};
 
 
 pub struct DatabaseConnection(pub Database);
 pub struct DatabaseConnection(pub Database);
 
 
@@ -45,17 +46,17 @@ pub async fn find_world_by_id(db: Database, id: i32) -> Result<World, MongoError
     let filter = doc! { "_id": id as f32 };
     let filter = doc! { "_id": id as f32 };
 
 
     let world: World = world_collection
     let world: World = world_collection
-        .find_one(Some(filter), None)
+        .find_one(filter)
         .await
         .await
         .unwrap()
         .unwrap()
         .expect("expected world, found none");
         .expect("expected world, found none");
     Ok(world)
     Ok(world)
 }
 }
 
 
-pub async fn find_worlds(db: Database, ids: Vec<i32>) -> Result<Vec<World>, MongoError> {
+pub async fn find_worlds(db: Database, rng: &mut SmallRng, count: usize) -> Result<Vec<World>, MongoError> {
     let future_worlds = FuturesUnordered::new();
     let future_worlds = FuturesUnordered::new();
 
 
-    for id in ids {
+    for id in random_ids(rng, count) {
         future_worlds.push(find_world_by_id(db.clone(), id));
         future_worlds.push(find_world_by_id(db.clone(), id));
     }
     }
 
 
@@ -67,7 +68,7 @@ pub async fn fetch_fortunes(db: Database) -> Result<Vec<Fortune>, MongoError> {
     let fortune_collection = db.collection::<Fortune>("fortune");
     let fortune_collection = db.collection::<Fortune>("fortune");
 
 
     let mut fortune_cursor = fortune_collection
     let mut fortune_cursor = fortune_collection
-        .find(None, None)
+        .find(doc! {})
         .await
         .await
         .expect("fortunes could not be loaded");
         .expect("fortunes could not be loaded");
 
 
@@ -99,8 +100,7 @@ pub async fn update_worlds(
     }
     }
 
 
     db.run_command(
     db.run_command(
-        doc! {"update": "world", "updates": updates, "ordered": false},
-        None,
+        doc! {"update": "world", "updates": updates, "ordered": false}
     )
     )
     .await
     .await
     .expect("could not update worlds");
     .expect("could not update worlds");

+ 6 - 6
frameworks/Rust/axum/src/mongo_raw/database.rs

@@ -6,8 +6,9 @@ use mongodb::{
     bson::{doc, RawDocumentBuf},
     bson::{doc, RawDocumentBuf},
     Database,
     Database,
 };
 };
+use rand::rngs::SmallRng;
 
 
-use crate::common::models::World;
+use crate::common::{models::World, random_ids};
 
 
 pub struct DatabaseConnection(pub Database);
 pub struct DatabaseConnection(pub Database);
 
 
@@ -48,7 +49,7 @@ pub async fn find_world_by_id(db: Database, id: i32) -> Result<World, MongoError
     let filter = doc! { "_id": id as f32 };
     let filter = doc! { "_id": id as f32 };
 
 
     let raw: RawDocumentBuf = world_collection
     let raw: RawDocumentBuf = world_collection
-        .find_one(Some(filter), None)
+        .find_one(filter)
         .await
         .await
         .unwrap()
         .unwrap()
         .expect("expected world, found none");
         .expect("expected world, found none");
@@ -69,10 +70,10 @@ pub async fn find_world_by_id(db: Database, id: i32) -> Result<World, MongoError
     })
     })
 }
 }
 
 
-pub async fn find_worlds(db: Database, ids: Vec<i32>) -> Result<Vec<World>, MongoError> {
+pub async fn find_worlds(db: Database, rng: &mut SmallRng, count: usize) -> Result<Vec<World>, MongoError> {
     let future_worlds = FuturesUnordered::new();
     let future_worlds = FuturesUnordered::new();
 
 
-    for id in ids {
+    for id in random_ids(rng, count) {
         future_worlds.push(find_world_by_id(db.clone(), id));
         future_worlds.push(find_world_by_id(db.clone(), id));
     }
     }
 
 
@@ -93,8 +94,7 @@ pub async fn update_worlds(
     }
     }
 
 
     db.run_command(
     db.run_command(
-        doc! {"update": "world", "updates": updates, "ordered": false},
-        None,
+        doc! {"update": "world", "updates": updates, "ordered": false}
     )
     )
     .await
     .await
     .expect("could not update worlds");
     .expect("could not update worlds");

+ 7 - 4
frameworks/Rust/viz/Cargo.toml

@@ -24,13 +24,16 @@ path = "src/main_diesel.rs"
 required-features = ["diesel", "diesel-async", "sailfish"]
 required-features = ["diesel", "diesel-async", "sailfish"]
 
 
 [dependencies]
 [dependencies]
-viz = "0.9"
-hyper = "1.4"
+viz = "0.10"
+hyper = "1.5"
 hyper-util = "0.1"
 hyper-util = "0.1"
+http-body-util = "0.1"
 atoi = "2.0"
 atoi = "2.0"
 serde = { version = "1.0", features = ["derive"] }
 serde = { version = "1.0", features = ["derive"] }
+serde_json = "1"
+mime = "0.3"
 rand = { version = "0.8", features = ["small_rng"] }
 rand = { version = "0.8", features = ["small_rng"] }
-thiserror = "1.0"
+thiserror = "2.0"
 futures-util = "0.3"
 futures-util = "0.3"
 
 
 [target.'cfg(not(unix))'.dependencies]
 [target.'cfg(not(unix))'.dependencies]
@@ -50,7 +53,7 @@ sqlx = { version = "0.8", features = [
 diesel = { version = "2.2", default-features = false, features = [
 diesel = { version = "2.2", default-features = false, features = [
   "i-implement-a-third-party-backend-and-opt-into-breaking-changes",
   "i-implement-a-third-party-backend-and-opt-into-breaking-changes",
 ], optional = true }
 ], optional = true }
-diesel-async = { git = "https://github.com/weiznich/diesel_async.git", rev = "74867bd", version = "0.4", default-features = false, features = [
+diesel-async = { version = "0.5", default-features = false, features = [
   "postgres",
   "postgres",
   "bb8",
   "bb8",
 ], optional = true }
 ], optional = true }

+ 10 - 4
frameworks/Rust/viz/src/main.rs

@@ -3,7 +3,7 @@
 use serde::Serialize;
 use serde::Serialize;
 use viz::{
 use viz::{
     header::{HeaderValue, SERVER},
     header::{HeaderValue, SERVER},
-    Error, Request, Response, ResponseExt, Result, Router,
+    Bytes, Error, Request, Response, ResponseExt, Result, Router,
 };
 };
 
 
 mod server;
 mod server;
@@ -22,9 +22,15 @@ async fn plaintext(_: Request) -> Result<Response> {
 }
 }
 
 
 async fn json(_: Request) -> Result<Response> {
 async fn json(_: Request) -> Result<Response> {
-    let mut res = Response::json(Message {
-        message: "Hello, World!",
-    })?;
+    let mut res = Response::with(
+        http_body_util::Full::new(Bytes::from(
+            serde_json::to_vec(&Message {
+                message: "Hello, World!",
+            })
+            .unwrap(),
+        )),
+        mime::APPLICATION_JSON.as_ref(),
+    );
     res.headers_mut()
     res.headers_mut()
         .insert(SERVER, HeaderValue::from_static("Viz"));
         .insert(SERVER, HeaderValue::from_static("Viz"));
     Ok(res)
     Ok(res)

+ 1 - 3
frameworks/Rust/viz/viz-diesel.dockerfile

@@ -1,6 +1,4 @@
-FROM rust:1.79
-
-RUN apt-get update -yqq && apt-get install -yqq cmake g++
+FROM rust:1.83
 
 
 ADD ./ /viz
 ADD ./ /viz
 WORKDIR /viz
 WORKDIR /viz

+ 1 - 3
frameworks/Rust/viz/viz-pg.dockerfile

@@ -1,6 +1,4 @@
-FROM rust:1.79
-
-RUN apt-get update -yqq && apt-get install -yqq cmake g++
+FROM rust:1.83
 
 
 ADD ./ /viz
 ADD ./ /viz
 WORKDIR /viz
 WORKDIR /viz

+ 1 - 3
frameworks/Rust/viz/viz-sqlx.dockerfile

@@ -1,6 +1,4 @@
-FROM rust:1.79
-
-RUN apt-get update -yqq && apt-get install -yqq cmake g++
+FROM rust:1.83
 
 
 ADD ./ /viz
 ADD ./ /viz
 WORKDIR /viz
 WORKDIR /viz

+ 1 - 3
frameworks/Rust/viz/viz.dockerfile

@@ -1,6 +1,4 @@
-FROM rust:1.79
-
-RUN apt-get update -yqq && apt-get install -yqq cmake g++
+FROM rust:1.83
 
 
 ADD ./ /viz
 ADD ./ /viz
 WORKDIR /viz
 WORKDIR /viz

+ 6 - 6
frameworks/TypeScript/ditsmod/benchmark_config.json

@@ -18,7 +18,7 @@
         "webserver": "None",
         "webserver": "None",
         "os": "Linux",
         "os": "Linux",
         "database_os": "Linux",
         "database_os": "Linux",
-        "display_name": "ditsmod",
+        "display_name": "ditsmod v3.0",
         "notes": "Simplified use of Dependency Injection (no request level injector).",
         "notes": "Simplified use of Dependency Injection (no request level injector).",
         "versus": "nodejs"
         "versus": "nodejs"
       },
       },
@@ -41,7 +41,7 @@
         "webserver": "None",
         "webserver": "None",
         "os": "Linux",
         "os": "Linux",
         "database_os": "Linux",
         "database_os": "Linux",
-        "display_name": "ditsmod [postgres]",
+        "display_name": "ditsmod v3.0 [postgres]",
         "notes": "Simplified use of Dependency Injection (no request level injector).",
         "notes": "Simplified use of Dependency Injection (no request level injector).",
         "versus": "nodejs"
         "versus": "nodejs"
       },
       },
@@ -64,7 +64,7 @@
         "webserver": "None",
         "webserver": "None",
         "os": "Linux",
         "os": "Linux",
         "database_os": "Linux",
         "database_os": "Linux",
-        "display_name": "ditsmod [mysql]",
+        "display_name": "ditsmod v3.0 [mysql]",
         "notes": "Simplified use of Dependency Injection (no request level injector).",
         "notes": "Simplified use of Dependency Injection (no request level injector).",
         "versus": "nodejs"
         "versus": "nodejs"
       },
       },
@@ -84,7 +84,7 @@
         "webserver": "None",
         "webserver": "None",
         "os": "Linux",
         "os": "Linux",
         "database_os": "Linux",
         "database_os": "Linux",
-        "display_name": "ditsmod on bun",
+        "display_name": "ditsmod-bun v3.0",
         "notes": "Simplified use of Dependency Injection (no request level injector).",
         "notes": "Simplified use of Dependency Injection (no request level injector).",
         "versus": "bun"
         "versus": "bun"
       },
       },
@@ -107,7 +107,7 @@
         "webserver": "None",
         "webserver": "None",
         "os": "Linux",
         "os": "Linux",
         "database_os": "Linux",
         "database_os": "Linux",
-        "display_name": "ditsmod on bun [postgres]",
+        "display_name": "ditsmod-bun v3.0 [postgres]",
         "notes": "Simplified use of Dependency Injection (no request level injector).",
         "notes": "Simplified use of Dependency Injection (no request level injector).",
         "versus": "bun"
         "versus": "bun"
       },
       },
@@ -130,7 +130,7 @@
         "webserver": "None",
         "webserver": "None",
         "os": "Linux",
         "os": "Linux",
         "database_os": "Linux",
         "database_os": "Linux",
-        "display_name": "ditsmod on bun [mysql]",
+        "display_name": "ditsmod-bun v3.0 [mysql]",
         "notes": "Simplified use of Dependency Injection (no request level injector).",
         "notes": "Simplified use of Dependency Injection (no request level injector).",
         "versus": "bun"
         "versus": "bun"
       }
       }

+ 2 - 2
frameworks/TypeScript/ditsmod/package.json

@@ -14,8 +14,8 @@
   "author": "Костя Третяк",
   "author": "Костя Третяк",
   "license": "MIT",
   "license": "MIT",
   "dependencies": {
   "dependencies": {
-    "@ditsmod/core": "~2.55.0",
-    "@ditsmod/routing": "~2.3.0",
+    "@ditsmod/core": "^3.0.0-alpha.2",
+    "@ditsmod/routing": "^3.0.0-alpha.2",
     "handlebars": "^4.7.8",
     "handlebars": "^4.7.8",
     "lru-cache": "^11.0.0",
     "lru-cache": "^11.0.0",
     "mariadb": "^3.3.1",
     "mariadb": "^3.3.1",

+ 2 - 4
frameworks/TypeScript/ditsmod/src/app/app.module.ts

@@ -2,15 +2,13 @@ import { Providers, rootModule } from '@ditsmod/core';
 import { PRE_ROUTER_EXTENSIONS, RoutingModule } from '@ditsmod/routing';
 import { PRE_ROUTER_EXTENSIONS, RoutingModule } from '@ditsmod/routing';
 
 
 import { OneController } from './one.controller.js';
 import { OneController } from './one.controller.js';
-import { DbService } from './db.service.js';
 import { InitExtension } from './init.extension.js';
 import { InitExtension } from './init.extension.js';
 import { DB_INIT_EXTENSIONS } from './tokens.js';
 import { DB_INIT_EXTENSIONS } from './tokens.js';
-import { ModelService } from './types.js';
 
 
 @rootModule({
 @rootModule({
   imports: [RoutingModule],
   imports: [RoutingModule],
-  providersPerApp: new Providers().passThrough(DbService).passThrough(ModelService).useLogConfig({ level: 'off' }),
-  extensions: [{ extension: InitExtension, groupToken: DB_INIT_EXTENSIONS, nextToken: PRE_ROUTER_EXTENSIONS }],
+  providersPerApp: new Providers().useLogConfig({ level: 'off' }),
+  extensions: [{ extension: InitExtension, group: DB_INIT_EXTENSIONS, beforeGroup: PRE_ROUTER_EXTENSIONS }],
   controllers: [OneController],
   controllers: [OneController],
 })
 })
 export class AppModule {}
 export class AppModule {}

+ 5 - 11
frameworks/TypeScript/ditsmod/src/app/init.extension.ts

@@ -5,18 +5,12 @@ import { ModelService } from './types.js';
 
 
 @injectable()
 @injectable()
 export class InitExtension implements Extension<void> {
 export class InitExtension implements Extension<void> {
-  #inited: boolean;
-
   constructor(
   constructor(
     private perAppService: PerAppService,
     private perAppService: PerAppService,
     private logger: Logger,
     private logger: Logger,
   ) {}
   ) {}
 
 
-  async init(): Promise<void> {
-    if (this.#inited) {
-      return;
-    }
-
+  async stage1(): Promise<void> {
     const dbType = process.env.DATABASE as 'mysql' | 'postgres';
     const dbType = process.env.DATABASE as 'mysql' | 'postgres';
 
 
     if (dbType == 'mysql') {
     if (dbType == 'mysql') {
@@ -28,13 +22,13 @@ export class InitExtension implements Extension<void> {
     } else {
     } else {
       this.logger.log('warn', `Unknown database "${dbType}"`);
       this.logger.log('warn', `Unknown database "${dbType}"`);
     }
     }
-
-    this.#inited = true;
   }
   }
 
 
   protected async setDbService(useClass: Class) {
   protected async setDbService(useClass: Class) {
-    const injector = this.perAppService.injector.resolveAndCreateChild([{ token: ModelService, useClass }]);
-    const dbService = injector.pull(DbService) as DbService;
+    const dbService = this.perAppService.injector
+      .resolveAndCreateChild([DbService, { token: ModelService, useClass }])
+      .get(DbService) as DbService;
+
     await dbService.setWorldsToCache();
     await dbService.setWorldsToCache();
     this.perAppService.providers.push({ token: DbService, useValue: dbService });
     this.perAppService.providers.push({ token: DbService, useValue: dbService });
   }
   }

+ 12 - 15
frameworks/TypeScript/ditsmod/src/app/one.controller.ts

@@ -1,4 +1,5 @@
-import { AnyObj, controller, RequestContext, SingletonRequestContext, route } from '@ditsmod/core';
+import { AnyObj, controller, RequestContext, SingletonRequestContext, optional } from '@ditsmod/core';
+import { route } from '@ditsmod/routing';
 import Handlebars from 'handlebars';
 import Handlebars from 'handlebars';
 
 
 import { DbService } from './db.service.js';
 import { DbService } from './db.service.js';
@@ -27,9 +28,9 @@ const tmpl = Handlebars.compile(
   ].join(''),
   ].join(''),
 );
 );
 
 
-@controller({ isSingleton: true })
+@controller({ scope: 'module' })
 export class OneController {
 export class OneController {
-  constructor(private dbService: DbService) {}
+  constructor(@optional() private dbService: DbService) {}
 
 
   @route('GET', 'db')
   @route('GET', 'db')
   async getSingleQuery(ctx: RequestContext) {
   async getSingleQuery(ctx: RequestContext) {
@@ -61,28 +62,24 @@ export class OneController {
     const fortunes = await this.dbService.findAllFortunes();
     const fortunes = await this.dbService.findAllFortunes();
     fortunes.push(additionalFortune);
     fortunes.push(additionalFortune);
     fortunes.sort(compare);
     fortunes.sort(compare);
-    ctx.nodeRes.setHeader('Server', 'Ditsmod');
-    ctx.nodeRes.setHeader('Content-Type', 'text/html; charset=utf-8');
-    ctx.nodeRes.end(tmpl({ fortunes }));
+    ctx.rawRes.setHeader('Server', 'Ditsmod');
+    ctx.rawRes.setHeader('Content-Type', 'text/html; charset=utf-8');
+    ctx.rawRes.end(tmpl({ fortunes }));
   }
   }
 
 
   @route('GET', 'plaintext')
   @route('GET', 'plaintext')
   getHello(ctx: SingletonRequestContext) {
   getHello(ctx: SingletonRequestContext) {
-    ctx.nodeRes.setHeader('Server', 'Ditsmod');
-    ctx.nodeRes.setHeader('Content-Type', 'text/plain; charset=utf-8');
-    ctx.nodeRes.end('Hello, World!');
+    ctx.rawRes.setHeader('Server', 'Ditsmod');
+    ctx.rawRes.setHeader('Content-Type', 'text/plain; charset=utf-8');
+    ctx.rawRes.end('Hello, World!');
   }
   }
 
 
   @route('GET', 'json')
   @route('GET', 'json')
   getJson(ctx: SingletonRequestContext) {
   getJson(ctx: SingletonRequestContext) {
-    ctx.nodeRes.setHeader('Server', 'Ditsmod');
-    ctx.nodeRes.setHeader('Content-Type', 'application/json; charset=utf-8');
-    ctx.nodeRes.end(JSON.stringify({ message: 'Hello, World!' }));
+    this.sendJson(ctx, { message: 'Hello, World!' });
   }
   }
 
 
   protected sendJson(ctx: RequestContext, value: AnyObj) {
   protected sendJson(ctx: RequestContext, value: AnyObj) {
-    ctx.nodeRes.setHeader('Server', 'Ditsmod');
-    ctx.nodeRes.setHeader('Content-Type', 'application/json; charset=utf-8');
-    ctx.nodeRes.end(JSON.stringify(value));
+    ctx.setHeader('Server', 'Ditsmod').sendJson(value);
   }
   }
 }
 }

+ 1 - 1
frameworks/TypeScript/ditsmod/src/main.ts

@@ -13,6 +13,6 @@ if (numCpus > 1 && cluster.isPrimary) {
   }
   }
 } else {
 } else {
   const serverOptions: ServerOptions = { keepAlive: true, keepAliveTimeout: 0 };
   const serverOptions: ServerOptions = { keepAlive: true, keepAliveTimeout: 0 };
-  const app = await new Application().bootstrap(AppModule, { serverOptions });
+  const app = await Application.create(AppModule, { serverOptions });
   app.server.listen(8080, '0.0.0.0');
   app.server.listen(8080, '0.0.0.0');
 }
 }

BIN
frameworks/TypeScript/elysia/bun.lockb


+ 4 - 3
frameworks/TypeScript/elysia/package.json

@@ -3,7 +3,8 @@
   "version": "0.0.1",
   "version": "0.0.1",
   "module": "src/index.js",
   "module": "src/index.js",
   "devDependencies": {
   "devDependencies": {
-    "typescript": "^5.5.4"
+    "@types/bun": "^1.1.14",
+    "typescript": "^5.7.2"
   },
   },
   "scripts": {
   "scripts": {
     "dev": "bun run --watch src/index.ts",
     "dev": "bun run --watch src/index.ts",
@@ -12,7 +13,7 @@
     "compile": "bun build --compile --minify --target bun --outfile server src/index.ts"
     "compile": "bun build --compile --minify --target bun --outfile server src/index.ts"
   },
   },
   "dependencies": {
   "dependencies": {
-    "elysia": "^1.1.16",
-    "postgres": "^3.4.4"
+    "elysia": "^1.2.9",
+    "postgres": "^3.4.5"
   }
   }
 }
 }

+ 57 - 65
frameworks/TypeScript/elysia/src/db-handlers.ts

@@ -2,74 +2,66 @@ import { Elysia, t } from "elysia";
 import * as db from "./postgres";
 import * as db from "./postgres";
 import { Fortune } from "./types";
 import { Fortune } from "./types";
 
 
-function rand() {
-  return Math.ceil(Math.random() * 10000);
+export function rand() {
+	return Math.ceil(Math.random() * 10000);
 }
 }
 
 
 function parseQueriesNumber(q?: string) {
 function parseQueriesNumber(q?: string) {
-  return Math.min(parseInt(q || "1") || 1, 500);
-}
-
-function renderTemplate(fortunes: Fortune[]) {
-  const n = fortunes.length;
-
-  let html = "";
-  for (let i = 0; i < n; i++) {
-    html += `<tr><td>${fortunes[i].id}</td><td>${Bun.escapeHTML(
-      fortunes[i].message,
-    )}</td></tr>`;
-  }
-
-  return `<!DOCTYPE html><html><head><title>Fortunes</title></head><body><table><tr><th>id</th><th>message</th></tr>${html}</table></body></html>`;
+	// NaN is falsy, fallback to one.
+	return Math.min(+q! || 1, 500);
 }
 }
 
 
 export const dbHandlers = new Elysia()
 export const dbHandlers = new Elysia()
-  .headers({
-    server: "Elysia",
-  })
-  .get("/db", () => db.find(rand()))
-  .get("/fortunes", async (c) => {
-    const fortunes = await db.fortunes();
-
-    fortunes.push({
-      id: 0,
-      message: "Additional fortune added at request time.",
-    });
-
-    fortunes.sort((a, b) => {
-      if (a.message < b.message) return -1;
-
-      return 1;
-    });
-
-    c.set.headers["content-type"] = "text/html; charset=utf-8";
-
-    return renderTemplate(fortunes);
-  })
-  .get("/queries", (c) => {
-    const num = parseQueriesNumber(c.query.queries);
-    const worldPromises = new Array(num);
-
-    for (let i = 0; i < num; i++) {
-      worldPromises[i] = db.find(rand());
-    }
-
-    return Promise.all(worldPromises);
-  })
-  .get("/updates", async (c) => {
-    const num = parseQueriesNumber(c.query.queries);
-    const worldPromises = new Array(num);
-
-    for (let i = 0; i < num; i++) {
-      worldPromises[i] = db.find(rand());
-    }
-
-    const worlds = await Promise.all(worldPromises);
-
-    for (let i = 0; i < num; i++) {
-      worlds[i].randomNumber = rand();
-    }
-
-    await db.bulkUpdate(worlds);
-    return worlds;
-  });
+	.headers({
+		server: "Elysia",
+	})
+	// ? Mark as async for Promise result to prevent double Elysia's mapResponse execution
+	.get("/db", async () => db.find(rand()))
+	.get("/fortunes", async (c) => {
+		const fortunes = await db.fortunes();
+
+		fortunes.push({
+			id: 0,
+			message: "Additional fortune added at request time.",
+		});
+
+		fortunes.sort((a, b) => {
+			if (a.message < b.message) return -1;
+
+			return 1;
+		});
+
+		c.set.headers["content-type"] = "text/html; charset=utf-8";
+
+		const n = fortunes.length;
+
+		let html = "";
+		for (let i = 0; i < n; i++) {
+			html += `<tr><td>${fortunes[i].id}</td><td>${Bun.escapeHTML(
+				fortunes[i].message,
+			)}</td></tr>`;
+		}
+
+		return `<!DOCTYPE html><html><head><title>Fortunes</title></head><body><table><tr><th>id</th><th>message</th></tr>${html}</table></body></html>`;
+	})
+	// ? Mark as async for Promise result to prevent double Elysia's mapResponse execution
+	.get("/queries", async (c) => {
+		const num = parseQueriesNumber(c.query.queries);
+		const worldPromises = new Array(num);
+
+		for (let i = 0; i < num; i++) worldPromises[i] = db.find(rand());
+
+		return Promise.all(worldPromises);
+	})
+	.get("/updates", async (c) => {
+		const num = parseQueriesNumber(c.query.queries);
+		const worldPromises = new Array(num);
+
+		for (let i = 0; i < num; i++)
+			worldPromises[i] = db.findThenRand(rand());
+
+		const worlds = await Promise.all(worldPromises);
+
+		await db.bulkUpdate(worlds);
+		return worlds;
+	});

+ 26 - 17
frameworks/TypeScript/elysia/src/postgres.ts

@@ -1,30 +1,39 @@
 import postgres from "postgres";
 import postgres from "postgres";
-import { Fortune, World } from "./types";
+import { rand } from "./db-handlers";
+import type { Fortune, World } from "./types";
 
 
 const sql = postgres({
 const sql = postgres({
-  host: "tfb-database",
-  user: "benchmarkdbuser",
-  password: "benchmarkdbpass",
-  database: "hello_world",
-  max: 1,
+	host: "tfb-database",
+	user: "benchmarkdbuser",
+	password: "benchmarkdbpass",
+	database: "hello_world",
+	max: 1,
 });
 });
 
 
 export const fortunes = () => sql<Fortune[]>`SELECT id, message FROM fortune`;
 export const fortunes = () => sql<Fortune[]>`SELECT id, message FROM fortune`;
 
 
 export const find = (id: number) =>
 export const find = (id: number) =>
-  sql<World[]>`SELECT id, randomNumber FROM world WHERE id = ${id}`.then(
-    (arr) => arr[0],
-  );
+	sql<World[]>`SELECT id, randomNumber FROM world WHERE id = ${id}`.then(
+		(arr) => arr[0],
+	);
+
+export const findThenRand = (id: number) =>
+	sql<World[]>`SELECT id, randomNumber FROM world WHERE id = ${id}`.then(
+		(arr) => {
+			arr[0].randomNumber = rand();
+			return arr[0];
+		},
+	);
 
 
 export const bulkUpdate = (worlds: World[]) => {
 export const bulkUpdate = (worlds: World[]) => {
-  worlds = worlds.toSorted((a, b) => a.id - b.id);
+	worlds = worlds.toSorted((a, b) => a.id - b.id);
 
 
-  const values = new Array(worlds.length);
-  for (let i = 0; i < worlds.length; i++) {
-    values[i] = [worlds[i].id, worlds[i].randomNumber];
-  }
+	const values = new Array(worlds.length);
+	for (let i = 0; i < worlds.length; i++) {
+		values[i] = [worlds[i].id, worlds[i].randomNumber];
+	}
 
 
-  return sql`UPDATE world SET randomNumber = (update_data.randomNumber)::int
-	FROM (VALUES ${sql(values)}) AS update_data (id, randomNumber)
-	WHERE world.id = (update_data.id)::int`;
+	return sql`UPDATE world SET randomNumber = (update_data.randomNumber)::int
+		FROM (VALUES ${sql(values)}) AS update_data (id, randomNumber)
+		WHERE world.id = (update_data.id)::int`;
 };
 };

Some files were not shown because too many files changed in this diff