Browse Source

Merge branch 'TechEmpower:master' into master

Huang ziquan 7 months ago
parent
commit
a36983e684
31 changed files with 570 additions and 458 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. 1 0
      frameworks/Ruby/rails/app/controllers/hello_world_controller.rb
  9. 345 252
      frameworks/Rust/axum/Cargo.lock
  10. 20 19
      frameworks/Rust/axum/Cargo.toml
  11. 3 5
      frameworks/Rust/axum/README.md
  12. 2 2
      frameworks/Rust/axum/axum.dockerfile
  13. 6 8
      frameworks/Rust/axum/src/common/mod.rs
  14. 4 0
      frameworks/Rust/axum/src/main.rs
  15. 8 10
      frameworks/Rust/axum/src/main_mongo.rs
  16. 10 10
      frameworks/Rust/axum/src/main_mongo_raw.rs
  17. 4 0
      frameworks/Rust/axum/src/main_pg.rs
  18. 4 0
      frameworks/Rust/axum/src/main_pg_pool.rs
  19. 12 12
      frameworks/Rust/axum/src/main_sqlx.rs
  20. 7 7
      frameworks/Rust/axum/src/mongo/database.rs
  21. 6 6
      frameworks/Rust/axum/src/mongo_raw/database.rs
  22. 7 4
      frameworks/Rust/viz/Cargo.toml
  23. 10 4
      frameworks/Rust/viz/src/main.rs
  24. 1 3
      frameworks/Rust/viz/viz-diesel.dockerfile
  25. 1 3
      frameworks/Rust/viz/viz-pg.dockerfile
  26. 1 3
      frameworks/Rust/viz/viz-sqlx.dockerfile
  27. 1 3
      frameworks/Rust/viz/viz.dockerfile
  28. BIN
      frameworks/TypeScript/elysia/bun.lockb
  29. 4 3
      frameworks/TypeScript/elysia/package.json
  30. 57 65
      frameworks/TypeScript/elysia/src/db-handlers.ts
  31. 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.target>21</maven.compiler.target>
         <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>
         <jsoniter.version>0.9.23</jsoniter.version>
     </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.HikariDataSource;
 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;
 
@@ -25,9 +25,9 @@ public class Bootstrap {
     public static void main(String[] args) {
         int cpuNum = Runtime.getRuntime().availableProcessors();
         // 定义服务器接受的消息类型以及各类消息对应的处理器
-        HttpBootstrap bootstrap = new HttpBootstrap();
+        HttpServer bootstrap = new HttpServer();
         bootstrap.configuration()
-                .threadNum(cpuNum)
+                .threadNum(cpuNum + 1)
                 .headerLimiter(0)
                 .readBufferSize(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.Slice;
 import jakarta.servlet.http.HttpServletResponse;
-import org.smartboot.http.server.HttpResponse;
+import tech.smartboot.feat.core.server.HttpResponse;
 
 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;
 
-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 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;
 
-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 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;
 
-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 java.io.IOException;

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

@@ -5,7 +5,7 @@
     <parent>
         <groupId>org.noear</groupId>
         <artifactId>solon-parent</artifactId>
-        <version>3.0.4</version>
+        <version>3.0.5</version>
     </parent>
 
     <groupId>hello</groupId>
@@ -21,7 +21,12 @@
     <dependencies>
         <dependency>
             <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>

+ 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.new(id: 0, message: 'Additional fortune added at request time.')
     @fortunes.sort_by!(&:message)
+    render :fortune
   end
 
   def update

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]
-axum = { version = "0.7.6", default-features = false, features = [
+axum = { version = "0.7.9", default-features = false, features = [
     "json",
     "query",
     "http1",
     "tokio",
 ] }
 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"
-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",
     "snappy-compression",
     "zlib-compression",
 ] }
 num_cpus = "1.16.0"
 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",
     "macros",
     "runtime-tokio",
     "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-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"
-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 }
-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"] }
+quick_cache = "0.6.9"
+mimalloc = "0.1.43"
 
 
 [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)
 
 - 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.
 - 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.
 - Separation of build and deployment containers using multi-stage builds.
 - 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 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).
-- 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:
   - 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 \
     pkg-config libssl-dev \
@@ -18,7 +18,7 @@ ENV POSTGRES_MIN_POOL_SIZE=56
 ENV POSTGRES_MAX_POOL_SIZE=56
 ENV MONGODB_URL=mongodb://tfb-database:27017
 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/
 EXPOSE 8000
 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 =
     "SELECT id, randomnumber FROM world ORDER BY id";
 #[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.
 #[allow(dead_code)]
@@ -41,11 +40,10 @@ pub fn random_id(rng: &mut SmallRng) -> i32 {
     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)]
 #[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))
         .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 common::models::Message;
 use dotenv::dotenv;
+use mimalloc::MiMalloc;
+
+#[global_allocator]
+static GLOBAL: MiMalloc = MiMalloc;
 
 #[cfg(not(feature = "simd-json"))]
 use axum::Json;

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

@@ -14,8 +14,7 @@ use axum::Json;
 #[cfg(feature = "simd-json")]
 use common::simd_json::Json;
 use common::{
-    models::{FortuneInfo, World},
-    random_ids,
+    models::{FortuneInfo, World}, random_id
 };
 use dotenv::dotenv;
 use mongodb::{
@@ -24,6 +23,10 @@ use mongodb::{
 };
 use rand::{rngs::SmallRng, thread_rng, Rng, SeedableRng};
 use yarte::Template;
+use mimalloc::MiMalloc;
+
+#[global_allocator]
+static GLOBAL: MiMalloc = MiMalloc;
 
 use common::{
     get_env,
@@ -58,9 +61,7 @@ async fn queries(
     let q = parse_params(params);
 
     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");
 
     (StatusCode::OK, Json(results))
@@ -73,17 +74,14 @@ async fn updates(
     let q = parse_params(params);
 
     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
         .expect("worlds could not be retrieved");
     let mut updated_worlds: Vec<World> = Vec::with_capacity(q);
 
     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);
     }
 

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

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

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

@@ -7,6 +7,10 @@ use axum::{
 use dotenv::dotenv;
 use rand::{rngs::SmallRng, thread_rng, SeedableRng};
 use yarte::Template;
+use mimalloc::MiMalloc;
+
+#[global_allocator]
+static GLOBAL: MiMalloc = MiMalloc;
 
 #[cfg(not(feature = "simd-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 rand::{rngs::SmallRng, thread_rng, SeedableRng};
 use yarte::Template;
+use mimalloc::MiMalloc;
+
+#[global_allocator]
+static GLOBAL: MiMalloc = MiMalloc;
 
 mod server;
 

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

@@ -12,10 +12,14 @@ use axum::{
     Router,
 };
 use dotenv::dotenv;
-use moka::future::Cache;
+use quick_cache::sync::Cache;
 use rand::{rngs::SmallRng, thread_rng, SeedableRng};
 use sqlx::models::World;
 use yarte::Template;
+use mimalloc::MiMalloc;
+
+#[global_allocator]
+static GLOBAL: MiMalloc = MiMalloc;
 
 #[cfg(not(feature = "simd-json"))]
 use axum::Json;
@@ -55,10 +59,9 @@ async fn queries(
 ) -> impl IntoResponse {
     let mut rng = SmallRng::from_rng(&mut thread_rng()).unwrap();
     let count = parse_params(params);
-    let ids = random_ids(&mut rng, 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)
             .bind(id)
             .fetch_one(&mut *db.acquire().await.unwrap())
@@ -98,10 +101,10 @@ async fn cache(
 ) -> impl IntoResponse {
     let count = parse_params(params);
     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) {
-        worlds.push(cache.get(&id).await);
+        worlds.push(cache.get(&id));
     }
 
     (StatusCode::OK, Json(worlds))
@@ -115,7 +118,7 @@ async fn preload_cache(AppState { db, cache }: &AppState) {
         .expect("error loading 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)]
 struct AppState {
     db: PgPool,
-    cache: Cache<i32, Arc<World>>,
+    cache: Arc<Cache<i32, World>>,
 }
 
 #[tokio::main]
@@ -136,10 +139,7 @@ async fn main() {
 
     let state = AppState {
         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

+ 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 futures_util::{stream::FuturesUnordered, StreamExt, TryStreamExt};
 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);
 
@@ -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 world: World = world_collection
-        .find_one(Some(filter), None)
+        .find_one(filter)
         .await
         .unwrap()
         .expect("expected world, found none");
     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();
 
-    for id in ids {
+    for id in random_ids(rng, count) {
         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 mut fortune_cursor = fortune_collection
-        .find(None, None)
+        .find(doc! {})
         .await
         .expect("fortunes could not be loaded");
 
@@ -99,8 +100,7 @@ pub async fn update_worlds(
     }
 
     db.run_command(
-        doc! {"update": "world", "updates": updates, "ordered": false},
-        None,
+        doc! {"update": "world", "updates": updates, "ordered": false}
     )
     .await
     .expect("could not update worlds");

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

@@ -6,8 +6,9 @@ use mongodb::{
     bson::{doc, RawDocumentBuf},
     Database,
 };
+use rand::rngs::SmallRng;
 
-use crate::common::models::World;
+use crate::common::{models::World, random_ids};
 
 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 raw: RawDocumentBuf = world_collection
-        .find_one(Some(filter), None)
+        .find_one(filter)
         .await
         .unwrap()
         .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();
 
-    for id in ids {
+    for id in random_ids(rng, count) {
         future_worlds.push(find_world_by_id(db.clone(), id));
     }
 
@@ -93,8 +94,7 @@ pub async fn update_worlds(
     }
 
     db.run_command(
-        doc! {"update": "world", "updates": updates, "ordered": false},
-        None,
+        doc! {"update": "world", "updates": updates, "ordered": false}
     )
     .await
     .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"]
 
 [dependencies]
-viz = "0.9"
-hyper = "1.4"
+viz = "0.10"
+hyper = "1.5"
 hyper-util = "0.1"
+http-body-util = "0.1"
 atoi = "2.0"
 serde = { version = "1.0", features = ["derive"] }
+serde_json = "1"
+mime = "0.3"
 rand = { version = "0.8", features = ["small_rng"] }
-thiserror = "1.0"
+thiserror = "2.0"
 futures-util = "0.3"
 
 [target.'cfg(not(unix))'.dependencies]
@@ -50,7 +53,7 @@ sqlx = { version = "0.8", features = [
 diesel = { version = "2.2", default-features = false, features = [
   "i-implement-a-third-party-backend-and-opt-into-breaking-changes",
 ], 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",
   "bb8",
 ], optional = true }

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

@@ -3,7 +3,7 @@
 use serde::Serialize;
 use viz::{
     header::{HeaderValue, SERVER},
-    Error, Request, Response, ResponseExt, Result, Router,
+    Bytes, Error, Request, Response, ResponseExt, Result, Router,
 };
 
 mod server;
@@ -22,9 +22,15 @@ async fn plaintext(_: 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()
         .insert(SERVER, HeaderValue::from_static("Viz"));
     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
 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
 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
 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
 WORKDIR /viz

BIN
frameworks/TypeScript/elysia/bun.lockb


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

@@ -3,7 +3,8 @@
   "version": "0.0.1",
   "module": "src/index.js",
   "devDependencies": {
-    "typescript": "^5.5.4"
+    "@types/bun": "^1.1.14",
+    "typescript": "^5.7.2"
   },
   "scripts": {
     "dev": "bun run --watch src/index.ts",
@@ -12,7 +13,7 @@
     "compile": "bun build --compile --minify --target bun --outfile server src/index.ts"
   },
   "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 { Fortune } from "./types";
 
-function rand() {
-  return Math.ceil(Math.random() * 10000);
+export function rand() {
+	return Math.ceil(Math.random() * 10000);
 }
 
 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()
-  .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 { Fortune, World } from "./types";
+import { rand } from "./db-handlers";
+import type { Fortune, World } from "./types";
 
 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 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[]) => {
-  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