Browse Source

rust: bump ohkami to v0.24 (#10081)

* rust: bump ohkami to v0.24

* update rt_tokio impl
kanarus 1 week ago
parent
commit
72165896d3

File diff suppressed because it is too large
+ 373 - 315
frameworks/Rust/ohkami/Cargo.lock


+ 12 - 13
frameworks/Rust/ohkami/Cargo.toml

@@ -1,20 +1,19 @@
 [package]
 name    = "framework_benchmarks"
-version = "0.21.0"
-edition = "2021"
+version = "0.24.0"
+edition = "2024"
 authors = ["kanarus <[email protected]>"]
 
 [dependencies]
-ohkami       = { version = "0.21" }
-yarte        = { optional = true, version = "0.15" }
-futures-util = { optional = true, version = "0.3"  }
-rand         = { optional = true, version = "0.8", features = ["small_rng"] }
-sqlx         = { optional = true, version = "0.8", features = ["postgres", "tls-native-tls"] }
+ohkami         = { version = "0.24" }
+tokio          = { optional = true, version = "1.47", features = ["rt"] }
+tokio-postgres = { optional = true, version = "0.7" }
+yarte          = { optional = true, version = "0.15" }
+futures-util   = { optional = true, version = "0.3"  }
+rand           = { optional = true, version = "0.8", features = ["small_rng"] }
 
 [features]
-db           = ["yarte", "futures-util", "rand", "sqlx"]
-rt_tokio     = ["ohkami/rt_tokio",     "db", "sqlx/runtime-tokio"]
-rt_async-std = ["ohkami/rt_async-std", "db", "sqlx/runtime-async-std"]
-rt_smol      = ["ohkami/rt_smol"]
-rt_glommio   = ["ohkami/rt_glommio"]
-rt_nio       = ["ohkami/rt_nio"]
+rt_tokio   = ["ohkami/rt_tokio", "tokio", "tokio-postgres", "yarte", "futures-util", "rand"]
+rt_smol    = ["ohkami/rt_smol"]
+rt_glommio = ["ohkami/rt_glommio"]
+rt_nio     = ["ohkami/rt_nio"]

+ 6 - 14
frameworks/Rust/ohkami/README.md

@@ -1,18 +1,10 @@
-# [Ohkami](https://github.com/kana-rus/ohkami) - Intuitive and Declarative Web Framework for Rust
+# [Ohkami](https://github.com/ohkami-rs/ohkami) - A performant, declarative, and runtime-flexible web framework for Rust
 
-## Description
+## Features
 
-> Build web app in intuitive and declarative code
-> - *macro-less and type-safe* APIs for intuitive and declarative code
-> - *multi runtime* support:`tokio`, `async-std`, `worker` (Cloudflare Workers)
-
-- [User Guide](https://docs.rs/ohkami/latest/ohkami/)
-- [API Documentation](https://docs.rs/ohkami/latest/ohkami/)
-- Cargo package: [ohkami](https://crates.io/crates/ohkami)
-
-## Database
-
-PostgreSQL with [sqlx](https://github.com/launchbadge/sqlx)
+> - *macro-less and type-safe* APIs for declarative, ergonomic code
+> - *runtime-flexible* : `tokio`, `smol`, `nio`, `glommio` and `worker` (Cloudflare Workers), `lambda` (AWS Lambda)
+> - good performance, no-network testing, well-structured middlewares, Server-Sent Events, WebSocket, highly integrated OpenAPI document generation, ...
 
 ## Test URLs
 
@@ -38,4 +30,4 @@ PostgreSQL with [sqlx](https://github.com/launchbadge/sqlx)
 
 ### 6. Plaintext
 
-    http://localhost:8000/plaintext
+    http://localhost:8000/plaintext

+ 0 - 21
frameworks/Rust/ohkami/benchmark_config.json

@@ -23,27 +23,6 @@
                 "update_url":     "/updates?q=",
                 "plaintext_url":  "/plaintext"
             },
-            "rt_async-std": {
-                "dockerfile":     "rt_async-std.dockerfile",
-                "display_name":   "Ohkami [async-std]",
-                "framework":      "Ohkami",
-                "webserver":      "Ohkami",
-                "language":       "Rust",
-                "approach":       "Realistic",
-                "classification": "Micro",
-                "database":       "Postgres",
-                "orm":            "Raw",
-                "platform":       "None",
-                "os":             "Linux",
-                "database_os":    "Linux",
-                "port":           8000,
-                "json_url":       "/json",
-                "db_url":         "/db",
-                "query_url":      "/queries?q=",
-                "fortune_url":    "/fortunes",
-                "update_url":     "/updates?q=",
-                "plaintext_url":  "/plaintext"
-            },
             "rt_smol": {
                 "dockerfile":     "rt_smol.dockerfile",
                 "display_name":   "Ohkami [smol]",

+ 0 - 26
frameworks/Rust/ohkami/rt_async-std.dockerfile

@@ -1,26 +0,0 @@
-FROM rust:1.84-slim-bookworm AS builder
-
-RUN apt update && apt install -y --no-install-recommends \
-    pkg-config \
-    libpq-dev libssl-dev \
-    && rm -rf /var/lib/apt/lists/*
-
-COPY ./Cargo.toml    /build/
-COPY ./src/          /build/src/
-COPY ./rt_async-std/ /build/rt_async-std/
-    
-WORKDIR /build/rt_async-std
-ENV RUSTFLAGS="-C target-cpu=native"
-RUN cargo build --release
-
-##########################################################
-
-FROM gcr.io/distroless/cc-debian12
-
-COPY --from=builder /build/rt_async-std/target/release/framework_benchmarks-async-std /app/
-
-EXPOSE 8000
-ENV DATABASE_URL=postgres://benchmarkdbuser:benchmarkdbpass@tfb-database/hello_world
-ENV MAX_CONNECTIONS=56
-ENV MIN_CONNECTIONS=56
-CMD [ "/app/framework_benchmarks-async-std" ]

+ 0 - 14
frameworks/Rust/ohkami/rt_async-std/Cargo.toml

@@ -1,14 +0,0 @@
-[package]
-name    = "framework_benchmarks-async-std"
-version = "0.0.0"
-edition = "2021"
-authors = ["kanarus <[email protected]>"]
-
-[profile.release]
-lto           = true
-panic         = "abort"
-codegen-units = 1
-
-[dependencies]
-framework_benchmarks = { path = "..",      features = ["rt_async-std"] }
-async-std            = { version = "1.13", features = ["attributes"] }

+ 0 - 5
frameworks/Rust/ohkami/rt_async-std/src/main.rs

@@ -1,5 +0,0 @@
-#[async_std::main]
-async fn main() {
-    framework_benchmarks::ohkami().await
-        .howl("0.0.0.0:8000").await
-}

+ 1 - 1
frameworks/Rust/ohkami/rt_glommio.dockerfile

@@ -1,4 +1,4 @@
-FROM rust:1.84-slim-bookworm AS builder
+FROM rust:1.89-slim-bookworm AS builder
 
 RUN apt update && apt install -y --no-install-recommends \
     pkg-config \

+ 2 - 2
frameworks/Rust/ohkami/rt_glommio/Cargo.toml

@@ -1,7 +1,7 @@
 [package]
 name    = "framework_benchmarks-glommio"
 version = "0.0.0"
-edition = "2021"
+edition = "2024"
 authors = ["kanarus <[email protected]>"]
 
 [profile.release]
@@ -12,4 +12,4 @@ codegen-units = 1
 [dependencies]
 framework_benchmarks = { path = "..", features = ["rt_glommio"] }
 glommio              = { version = "0.9" }
-num_cpus             = { version = "1.16" }
+num_cpus             = { version = "1.17" }

+ 7 - 6
frameworks/Rust/ohkami/rt_glommio/src/main.rs

@@ -1,10 +1,11 @@
 use glommio::{LocalExecutorPoolBuilder, PoolPlacement, CpuSet};
 
 fn main() {
-    LocalExecutorPoolBuilder::new(PoolPlacement::MaxSpread(
-        num_cpus::get(), CpuSet::online().ok()
-    )).on_all_shards(|| async {
-        framework_benchmarks::ohkami().await
-            .howl("0.0.0.0:8000").await
-    }).unwrap().join_all();
+    LocalExecutorPoolBuilder::new(PoolPlacement::MaxSpread(num_cpus::get(), CpuSet::online().ok()))
+        .on_all_shards(|| async {
+            framework_benchmarks::ohkami().await
+                .howl("0.0.0.0:8000").await
+        })
+        .unwrap()
+        .join_all();
 }

+ 1 - 1
frameworks/Rust/ohkami/rt_nio.dockerfile

@@ -1,4 +1,4 @@
-FROM rust:1.84-slim-bookworm AS builder
+FROM rust:1.89-slim-bookworm AS builder
 
 RUN apt update && apt install -y --no-install-recommends \
     pkg-config \

+ 1 - 1
frameworks/Rust/ohkami/rt_nio/Cargo.toml

@@ -1,7 +1,7 @@
 [package]
 name    = "framework_benchmarks-nio"
 version = "0.0.0"
-edition = "2021"
+edition = "2024"
 authors = ["kanarus <[email protected]>"]
 
 [profile.release]

+ 1 - 1
frameworks/Rust/ohkami/rt_smol.dockerfile

@@ -1,4 +1,4 @@
-FROM rust:1.84-slim-bookworm AS builder
+FROM rust:1.89-slim-bookworm AS builder
 
 RUN apt update && apt install -y --no-install-recommends \
     pkg-config \

+ 1 - 1
frameworks/Rust/ohkami/rt_smol/Cargo.toml

@@ -1,7 +1,7 @@
 [package]
 name    = "framework_benchmarks-smol"
 version = "0.0.0"
-edition = "2021"
+edition = "2024"
 authors = ["kanarus <[email protected]>"]
 
 [profile.release]

+ 1 - 1
frameworks/Rust/ohkami/rt_tokio.dockerfile

@@ -1,4 +1,4 @@
-FROM rust:1.84-slim-bookworm AS builder
+FROM rust:1.89-slim-bookworm AS builder
 
 RUN apt update && apt install -y --no-install-recommends \
     pkg-config \

File diff suppressed because it is too large
+ 232 - 466
frameworks/Rust/ohkami/rt_tokio/Cargo.lock


+ 3 - 2
frameworks/Rust/ohkami/rt_tokio/Cargo.toml

@@ -1,7 +1,7 @@
 [package]
 name    = "framework_benchmarks-tokio"
 version = "0.0.0"
-edition = "2021"
+edition = "2024"
 authors = ["kanarus <[email protected]>"]
 
 [profile.release]
@@ -11,4 +11,5 @@ codegen-units = 1
 
 [dependencies]
 framework_benchmarks = { path = "..",      features = ["rt_tokio"] }
-tokio                = { version = "1.43", features = ["full"] }
+tokio                = { version = "1.47", features = ["full"] }
+num_cpus             = { version = "1.17" }

+ 38 - 4
frameworks/Rust/ohkami/rt_tokio/src/main.rs

@@ -1,5 +1,39 @@
-#[tokio::main]
-async fn main() {
-    framework_benchmarks::ohkami().await
-        .howl("0.0.0.0:8000").await
+fn runtime() -> tokio::runtime::Runtime {
+    tokio::runtime::Builder::new_current_thread()
+        .enable_all()
+        .build()
+        .unwrap()
+}
+
+async fn serve<ServeFuture: Future>(
+    server: impl FnOnce(tokio::net::TcpListener) -> ServeFuture,
+) -> std::io::Result<()> {
+    println!("start serving !");
+
+    let socket = tokio::net::TcpSocket::new_v4()?;
+    socket.set_reuseport(true)?;
+    socket.set_reuseaddr(true)?;
+    socket.set_nodelay(true)?;
+
+    socket.bind("0.0.0.0:8000".parse().unwrap())?;
+    server(socket.listen(4096)?).await;
+
+    Ok(())
+}
+
+fn main() {
+    for _ in 0..(num_cpus::get() - 1/*for main thread*/) {
+        std::thread::spawn(|| {
+            runtime().block_on(async {
+                serve(|listener| async {
+                    framework_benchmarks::ohkami().await.howl(listener).await
+                }).await.expect("serving error")
+            })
+        });
+    }
+    runtime().block_on(async {
+        serve(|listener| async {
+            framework_benchmarks::ohkami().await.howl(listener).await
+        }).await.expect("serving error")
+    });
 }

+ 1 - 40
frameworks/Rust/ohkami/src/fangs.rs

@@ -5,45 +5,6 @@ pub struct SetServer;
 impl FangAction for SetServer {
     #[inline(always)]
     async fn back<'a>(&'a self, res: &'a mut ohkami::Response) {
-        res.headers.set().Server("ohkami");
-    }
-}
-
-#[cfg(feature = "db")]
-impl crate::Postgres {
-    pub async fn init() -> impl FangAction {
-        #[derive(Clone)]
-        pub struct UsePostgres(crate::Postgres);
-        impl FangAction for UsePostgres {
-            #[inline(always)]
-            async fn fore<'a>(&'a self, req: &'a mut Request) -> Result<(), Response> {
-                Ok(req.memorize(self.0.clone()))
-            }
-        }
-
-        macro_rules! load_env {
-            ($($name:ident as $t:ty)*) => {$(
-                #[allow(non_snake_case)]
-                let $name = ::std::env::var(stringify!($name))
-                    .expect(concat!(
-                        "Failed to load environment variable ",
-                        "`", stringify!($name), "`"
-                    ))
-                    .parse::<$t>()
-                    .unwrap();
-            )*};
-        } load_env! {
-            MAX_CONNECTIONS as u32
-            MIN_CONNECTIONS as u32
-            DATABASE_URL    as String
-        }
-            
-        let pool = sqlx::postgres::PgPoolOptions::new()
-            .max_connections(MAX_CONNECTIONS)
-            .min_connections(MIN_CONNECTIONS)
-            .connect(&DATABASE_URL).await
-            .unwrap();
-            
-        UsePostgres(pool.into())
+        res.headers.set().server("ohkami");
     }
 }

+ 36 - 42
frameworks/Rust/ohkami/src/lib.rs

@@ -1,68 +1,67 @@
 mod fangs;
 mod models;
-#[cfg(feature = "db")] mod postgres;
-#[cfg(feature = "db")] mod templates;
+#[cfg(feature = "rt_tokio")] mod postgres;
+#[cfg(feature = "rt_tokio")] mod templates;
 
 use {
     fangs::SetServer,
     models::Message,
     ohkami::prelude::*,
-    ohkami::format::JSON,
 };
-#[cfg(feature = "db")] use {
+#[cfg(feature = "rt_tokio")] use {
     models::{Fortune, World, WorldsMeta},
     postgres::Postgres,
     templates::FortunesTemplate,
-    ohkami::format::Query,
 };
 
 pub async fn ohkami() -> Ohkami {
-    #[cfg(feature = "db")] {
-        Ohkami::new((
-            SetServer,
-            Postgres::init().await,
-            "/json"     .GET(json_serialization),
-            "/db"       .GET(single_database_query),
-            "/queries"  .GET(multiple_database_query),
-            "/fortunes" .GET(fortunes),
-            "/updates"  .GET(database_updates),
-            "/plaintext".GET(plaintext),
-        ))
-    }
-    #[cfg(not(feature = "db"))] {
-        Ohkami::new((
-            SetServer,
-            "/json"     .GET(json_serialization),
-            "/plaintext".GET(plaintext),
-        ))
-    }
+    Ohkami::new((
+        SetServer,
+        #[cfg(feature = "rt_tokio")]
+        Context::new(Postgres::new().await),
+        
+        "/plaintext".GET(plaintext),
+        "/json".GET(json_serialization),
+        #[cfg(feature = "rt_tokio")]
+        "/db".GET(single_database_query),
+        #[cfg(feature = "rt_tokio")]
+        "/queries".GET(multiple_database_query),
+        #[cfg(feature = "rt_tokio")]
+        "/fortunes".GET(fortunes),
+        #[cfg(feature = "rt_tokio")]
+        "/updates".GET(database_updates),
+    ))
 }
 
-async fn json_serialization() -> JSON<Message> {
-    JSON(Message {
+async fn plaintext() -> &'static str {
+    "Hello, World!"
+}
+
+async fn json_serialization() -> Json<Message> {
+    Json(Message {
         message: "Hello, World!"
     })
 }
 
-#[cfg(feature = "db")]
+#[cfg(feature = "rt_tokio")]
 async fn single_database_query(
     Context(db): Context<'_, Postgres>,
-) -> JSON<World> {
+) -> Json<World> {
     let world = db.select_random_world().await;
-    JSON(world)
+    Json(world)
 }
 
-#[cfg(feature = "db")]
+#[cfg(feature = "rt_tokio")]
 async fn multiple_database_query(
     Query(q): Query<WorldsMeta<'_>>,
     Context(db): Context<'_, Postgres>,
-) -> JSON<Vec<World>> {
+) -> Json<Vec<World>> {
     let n = q.parse();
     let worlds = db.select_n_random_worlds(n).await;
-    JSON(worlds)
+    Json(worlds)
 }
 
-#[cfg(feature = "db")]
+#[cfg(feature = "rt_tokio")]
 async fn fortunes(
     Context(db): Context<'_, Postgres>,
 ) -> FortunesTemplate {
@@ -75,17 +74,12 @@ async fn fortunes(
     FortunesTemplate { fortunes }
 }
 
-#[cfg(feature = "db")]
+#[cfg(feature = "rt_tokio")]
 async fn database_updates(
     Query(q): Query<WorldsMeta<'_>>,
     Context(db): Context<'_, Postgres>,
-) -> JSON<Vec<World>> {
+) -> Json<Vec<World>> {
     let n = q.parse();
-    let mut worlds = db.select_n_random_worlds(n).await;
-    db.update_random_ids_of_worlds(&mut worlds).await;
-    JSON(worlds)
-}
-
-async fn plaintext() -> &'static str {
-    "Hello, World!"
+    let worlds = db.update_randomnumbers_of_n_worlds(n).await;
+    Json(worlds)
 }

+ 2 - 4
frameworks/Rust/ohkami/src/models.rs

@@ -5,19 +5,17 @@ pub struct Message {
     pub message: &'static str,
 }
 
-#[cfg(feature = "db")]
+#[cfg(feature = "rt_tokio")]
 pub use db::*;
-#[cfg(feature = "db")]
+#[cfg(feature = "rt_tokio")]
 mod db {
     use super::*;
 
-    #[derive(sqlx::FromRow)]
     pub struct Fortune {
         pub id:      i32,
         pub message: String,
     }
 
-    #[derive(sqlx::FromRow)]
     #[derive(serde::Serialize)]
     #[allow(non_snake_case)]
     pub struct World {

+ 109 - 42
frameworks/Rust/ohkami/src/postgres.rs

@@ -1,64 +1,131 @@
-#![cfg(feature = "db")]
+#![cfg(feature = "rt_tokio")]
 
-use futures_util::{stream::FuturesUnordered, TryStreamExt};
-use rand::{rngs::SmallRng, SeedableRng, Rng, thread_rng};
 use crate::models::{World, Fortune};
+use std::sync::Arc;
+use futures_util::stream::{StreamExt, FuturesUnordered};
+use rand::{rngs::SmallRng, SeedableRng, Rng, distributions::Uniform, thread_rng};
 
 #[derive(Clone)]
-pub struct Postgres(sqlx::PgPool);
+pub struct Postgres {
+    client:     Arc<tokio_postgres::Client>,
+    statements: TechEmpowerStatements,
+}
 
-impl From<sqlx::PgPool> for Postgres {
-    fn from(pgpool: sqlx::PgPool) -> Self {
-        Self(pgpool)
+#[derive(Clone)]
+struct TechEmpowerStatements {
+    select_world_by_id:  tokio_postgres::Statement,
+    select_all_fortunes: tokio_postgres::Statement,
+    update_worlds:       tokio_postgres::Statement,
+}
+
+impl Postgres {
+    pub async fn new() -> Self {
+        let (client, connection) = tokio_postgres::connect(
+            &std::env::var("DATABASE_URL").unwrap(),
+            tokio_postgres::NoTls
+        ).await.expect("failed to connect database");
+
+        tokio::spawn(async {
+            if let Err(e) = connection.await {
+                eprintln!("error in database connection: {e}");
+            }
+        });
+        
+        let statements = TechEmpowerStatements {
+            select_world_by_id: client
+                .prepare("SELECT id, randomnumber FROM world WHERE id = $1 LIMIT 1")
+                .await
+                .unwrap(),
+            select_all_fortunes: client
+                .prepare("SELECT id, message FROM fortune")
+                .await
+                .unwrap(),
+            update_worlds: client
+                .prepare("\
+                    UPDATE world SET randomnumber = new.randomnumber FROM ( \
+                        SELECT * FROM UNNEST($1::int[], $2::int[]) AS v(id, randomnumber) \
+                    ) AS new WHERE world.id = new.id \
+                ")
+                .await
+                .unwrap(),
+        };
+
+        Self { client: Arc::new(client), statements }
     }
 }
 
 impl Postgres {
-    pub async fn select_random_world(&self) -> World {
-        let mut rng = SmallRng::from_rng(&mut thread_rng()).unwrap();
+    const ID_RANGE: std::ops::Range<i32> = 1..10001;
     
-        sqlx::query_as("SELECT id, randomnumber FROM World WHERE id = $1")
-            .bind((rng.gen::<u32>() % 10_000 + 1) as i32)
-            .fetch_one(&self.0).await
-            .expect("Failed to fetch a world")
+    async fn select_random_world_by_id(&self, id: i32) -> World {
+        let row = self.client
+            .query_one(&self.statements.select_world_by_id, &[&id])
+            .await
+            .expect("failed to fetch a world");
+
+        World {
+            id:           row.get(0),
+            randomnumber: row.get(1),
+        }
     }
-    
-    pub async fn select_all_fortunes(&self) -> Vec<Fortune> {
-        sqlx::query_as("SELECT id, message FROM Fortune")
-            .fetch_all(&self.0).await
-            .expect("Failed to fetch fortunes")
+}
+
+impl Postgres {
+    pub async fn select_random_world(&self) -> World {
+        let mut rng = SmallRng::from_rng(&mut thread_rng()).unwrap();
+        self.select_random_world_by_id(rng.gen_range(Self::ID_RANGE)).await
     }
     
     pub async fn select_n_random_worlds(&self, n: usize) -> Vec<World> {
-        let mut rng = SmallRng::from_rng(&mut thread_rng()).unwrap();
-    
+        let rng = SmallRng::from_rng(&mut thread_rng()).unwrap();
+
         let selects = FuturesUnordered::new();
-        for _ in 0..n {
-            selects.push(
-                sqlx::query_as("SELECT id, randomnumber FROM World WHERE id = $1")
-                    .bind((rng.gen::<u32>() % 10_000 + 1) as i32)
-                    .fetch_one(&self.0)
-            )
+        for id in rng.sample_iter(Uniform::new(Self::ID_RANGE.start, Self::ID_RANGE.end)).take(n) {
+            selects.push(self.select_random_world_by_id(id))
         }
-    
-        selects.try_collect().await.expect("Failed to fetch worlds")
+
+        selects.collect::<Vec<World>>().await
     }
     
-    pub async fn update_random_ids_of_worlds(&self, worlds: &mut Vec<World>) {
-        let mut rng = SmallRng::from_rng(&mut thread_rng()).unwrap();
-    
-        let updates = FuturesUnordered::new();
-        for w in worlds {
-            w.randomnumber = (rng.gen::<u32>() % 10_000 + 1) as i32;
-            updates.push(
-                sqlx::query(
-                    "UPDATE World SET randomnumber = $1 WHERE id = $2")
-                    .bind(w.randomnumber)
-                    .bind(w.id)
-                    .execute(&self.0)
-            )
+    pub async fn select_all_fortunes(&self) -> Vec<Fortune> {
+        let mut rows = std::pin::pin!(self
+            .client
+            .query_raw::<_, _, &[i32; 0]>(&self.statements.select_all_fortunes, &[])
+            .await
+            .expect("failed to fetch fortunes")
+        );
+
+        let mut fortunes = Vec::new();
+        while let Some(row) = rows.next().await.transpose().unwrap() {
+            fortunes.push(Fortune {
+                id:      row.get(0),
+                message: row.get(1),
+            });
         }
+
+        fortunes
+    }
     
-        let _: sqlx::postgres::PgQueryResult = updates.try_collect().await.expect("Failed to fetch worlds");
+    pub async fn update_randomnumbers_of_n_worlds(&self, n: usize) -> Vec<World> {
+        let rng = SmallRng::from_rng(&mut thread_rng()).unwrap();
+
+        let mut worlds = self.select_n_random_worlds(n).await;
+
+        let mut ids = Vec::with_capacity(n);
+        let new_randomnumbers = rng
+            .sample_iter(Uniform::new(Self::ID_RANGE.start, Self::ID_RANGE.end))
+            .take(n)
+            .collect::<Vec<_>>();
+        for i in 0..n {
+            worlds[i].randomnumber = new_randomnumbers[i];
+            ids.push(worlds[i].id);
+        }
+
+        self.client
+            .execute(&self.statements.update_worlds, &[&ids, &new_randomnumbers])
+            .await
+            .expect("failed to update worlds");
+
+        worlds
     }
 }

+ 2 - 1
frameworks/Rust/ohkami/src/templates.rs

@@ -1,4 +1,4 @@
-#![cfg(feature = "db")]
+#![cfg(feature = "rt_tokio")]
 
 use ohkami::{IntoResponse, Response};
 use yarte::Template;
@@ -17,6 +17,7 @@ pub struct FortunesTemplate {
 }
 
 impl IntoResponse for FortunesTemplate {
+    #[inline]
     fn into_response(self) -> Response {
         match Template::call(&self) {
             Ok(template) => Response::OK().with_html(template),

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