Browse Source

[xitca-web] new async runtime variant and header fix (#9322)

* [xitca-web] reduce duplicate code and header fix

* query parse fix

* enable new variant of tokio runtime
fakeshadow 10 months ago
parent
commit
eb5a28687f

+ 1 - 1
frameworks/Rust/xitca-web/.cargo/config.toml

@@ -1,5 +1,5 @@
 [build]
-rustflags = ["-C", "target-cpu=native"]
+rustflags = ["-C", "target-cpu=native", "--cfg", "tokio_unstable"]
 incremental = false
 
 [target.wasm32-wasip1-threads]

+ 56 - 50
frameworks/Rust/xitca-web/Cargo.lock

@@ -117,9 +117,9 @@ checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3"
 
 [[package]]
 name = "cc"
-version = "1.1.24"
+version = "1.1.30"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "812acba72f0a070b003d3697490d2b55b837230ae7c6c6497f05cc2ddbb8d938"
+checksum = "b16803a61b81d9eabb7eae2588776c4c1e584b738ede45fdbb4c972cec1e9945"
 dependencies = [
  "shlex",
 ]
@@ -288,9 +288,9 @@ dependencies = [
 
 [[package]]
 name = "futures-channel"
-version = "0.3.30"
+version = "0.3.31"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78"
+checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10"
 dependencies = [
  "futures-core",
  "futures-sink",
@@ -298,15 +298,15 @@ dependencies = [
 
 [[package]]
 name = "futures-core"
-version = "0.3.30"
+version = "0.3.31"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d"
+checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e"
 
 [[package]]
 name = "futures-macro"
-version = "0.3.30"
+version = "0.3.31"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac"
+checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -315,21 +315,21 @@ dependencies = [
 
 [[package]]
 name = "futures-sink"
-version = "0.3.30"
+version = "0.3.31"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5"
+checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7"
 
 [[package]]
 name = "futures-task"
-version = "0.3.30"
+version = "0.3.31"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004"
+checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988"
 
 [[package]]
 name = "futures-util"
-version = "0.3.30"
+version = "0.3.31"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48"
+checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81"
 dependencies = [
  "futures-core",
  "futures-macro",
@@ -441,9 +441,9 @@ checksum = "9028f49264629065d057f340a86acb84867925865f73bbf8d47b4d149a7e88b8"
 
 [[package]]
 name = "js-sys"
-version = "0.3.70"
+version = "0.3.72"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1868808506b929d7b0cfa8f75951347aa71bb21144b7791bae35d9bccfcfe37a"
+checksum = "6a88f1bda2bd75b0452a14784937d796722fdebfe50df998aeb3f0b7603019a9"
 dependencies = [
  "wasm-bindgen",
 ]
@@ -545,12 +545,9 @@ dependencies = [
 
 [[package]]
 name = "once_cell"
-version = "1.20.1"
+version = "1.20.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "82881c4be219ab5faaf2ad5e5e5ecdff8c66bd7402ca3160975c93b24961afd1"
-dependencies = [
- "portable-atomic",
-]
+checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775"
 
 [[package]]
 name = "parking_lot"
@@ -611,12 +608,6 @@ version = "0.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
 
-[[package]]
-name = "portable-atomic"
-version = "1.9.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cc9c68a3f6da06753e9335d63e27f6b9754dd1920d941135b7ea8224f141adb2"
-
 [[package]]
 name = "postgres-protocol"
 version = "0.6.7"
@@ -666,9 +657,9 @@ dependencies = [
 
 [[package]]
 name = "proc-macro2"
-version = "1.0.86"
+version = "1.0.87"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77"
+checksum = "b3e4daa0dcf6feba26f985457cdf104d4b4256fc5a09547140f3631bb076b19a"
 dependencies = [
  "unicode-ident",
 ]
@@ -943,8 +934,7 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
 [[package]]
 name = "tokio"
 version = "1.40.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e2b070231665d27ad9ec9b8df639893f46727666c6767db40317fbe920a5d998"
+source = "git+https://github.com/tokio-rs/tokio.git?rev=512e9de#512e9decfb683d22f4a145459142542caa0894c9"
 dependencies = [
  "backtrace",
  "bytes",
@@ -1086,9 +1076,9 @@ checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b"
 
 [[package]]
 name = "wasm-bindgen"
-version = "0.2.93"
+version = "0.2.95"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5"
+checksum = "128d1e363af62632b8eb57219c8fd7877144af57558fb2ef0368d0087bddeb2e"
 dependencies = [
  "cfg-if",
  "once_cell",
@@ -1097,9 +1087,9 @@ dependencies = [
 
 [[package]]
 name = "wasm-bindgen-backend"
-version = "0.2.93"
+version = "0.2.95"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b"
+checksum = "cb6dd4d3ca0ddffd1dd1c9c04f94b868c37ff5fac97c30b97cff2d74fce3a358"
 dependencies = [
  "bumpalo",
  "log",
@@ -1112,9 +1102,9 @@ dependencies = [
 
 [[package]]
 name = "wasm-bindgen-macro"
-version = "0.2.93"
+version = "0.2.95"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf"
+checksum = "e79384be7f8f5a9dd5d7167216f022090cf1f9ec128e6e6a482a2cb5c5422c56"
 dependencies = [
  "quote",
  "wasm-bindgen-macro-support",
@@ -1122,9 +1112,9 @@ dependencies = [
 
 [[package]]
 name = "wasm-bindgen-macro-support"
-version = "0.2.93"
+version = "0.2.95"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836"
+checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -1135,15 +1125,15 @@ dependencies = [
 
 [[package]]
 name = "wasm-bindgen-shared"
-version = "0.2.93"
+version = "0.2.95"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484"
+checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d"
 
 [[package]]
 name = "web-sys"
-version = "0.3.70"
+version = "0.3.72"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "26fdeaafd9bd129f65e7c031593c24d62186301e0c72c8978fa1678be7d532c0"
+checksum = "f6488b90108c040df0fe62fa815cbdee25124641df01814dd7282749234c6112"
 dependencies = [
  "js-sys",
  "wasm-bindgen",
@@ -1258,7 +1248,7 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
 [[package]]
 name = "xitca-codegen"
 version = "0.4.0"
-source = "git+http://github.com/HFQR/xitca-web?rev=d3066ba#d3066ba5fc65e89c8a20890dd529f05818c853d6"
+source = "git+http://github.com/HFQR/xitca-web?rev=1de8d9c#1de8d9c079e73f7fd9ba953741302d87e50d831a"
 dependencies = [
  "quote",
  "syn",
@@ -1267,7 +1257,7 @@ dependencies = [
 [[package]]
 name = "xitca-http"
 version = "0.7.0"
-source = "git+http://github.com/HFQR/xitca-web?rev=d3066ba#d3066ba5fc65e89c8a20890dd529f05818c853d6"
+source = "git+http://github.com/HFQR/xitca-web?rev=1de8d9c#1de8d9c079e73f7fd9ba953741302d87e50d831a"
 dependencies = [
  "futures-core",
  "http",
@@ -1314,6 +1304,22 @@ dependencies = [
  "xitca-unsafe-collection",
 ]
 
+[[package]]
+name = "xitca-postgres"
+version = "0.3.0"
+source = "git+http://github.com/HFQR/xitca-web?rev=1de8d9c#1de8d9c079e73f7fd9ba953741302d87e50d831a"
+dependencies = [
+ "fallible-iterator",
+ "futures-core",
+ "percent-encoding",
+ "postgres-protocol",
+ "postgres-types",
+ "tokio",
+ "tracing",
+ "xitca-io",
+ "xitca-unsafe-collection",
+]
+
 [[package]]
 name = "xitca-postgres-diesel"
 version = "0.1.0"
@@ -1324,7 +1330,7 @@ dependencies = [
  "futures-core",
  "scoped-futures",
  "tokio",
- "xitca-postgres",
+ "xitca-postgres 0.2.1",
 ]
 
 [[package]]
@@ -1339,7 +1345,7 @@ dependencies = [
 [[package]]
 name = "xitca-server"
 version = "0.5.0"
-source = "git+http://github.com/HFQR/xitca-web?rev=d3066ba#d3066ba5fc65e89c8a20890dd529f05818c853d6"
+source = "git+http://github.com/HFQR/xitca-web?rev=1de8d9c#1de8d9c079e73f7fd9ba953741302d87e50d831a"
 dependencies = [
  "socket2 0.5.7",
  "tokio",
@@ -1353,7 +1359,7 @@ dependencies = [
 [[package]]
 name = "xitca-service"
 version = "0.3.0"
-source = "git+http://github.com/HFQR/xitca-web?rev=d3066ba#d3066ba5fc65e89c8a20890dd529f05818c853d6"
+source = "git+http://github.com/HFQR/xitca-web?rev=1de8d9c#1de8d9c079e73f7fd9ba953741302d87e50d831a"
 
 [[package]]
 name = "xitca-unsafe-collection"
@@ -1383,7 +1389,7 @@ dependencies = [
  "tokio-uring",
  "xitca-http",
  "xitca-io",
- "xitca-postgres",
+ "xitca-postgres 0.3.0",
  "xitca-postgres-diesel",
  "xitca-server",
  "xitca-service",
@@ -1394,7 +1400,7 @@ dependencies = [
 [[package]]
 name = "xitca-web"
 version = "0.7.0"
-source = "git+http://github.com/HFQR/xitca-web?rev=d3066ba#d3066ba5fc65e89c8a20890dd529f05818c853d6"
+source = "git+http://github.com/HFQR/xitca-web?rev=1de8d9c#1de8d9c079e73f7fd9ba953741302d87e50d831a"
 dependencies = [
  "futures-core",
  "pin-project-lite",

+ 13 - 11
frameworks/Rust/xitca-web/Cargo.toml

@@ -9,9 +9,9 @@ path = "./src/main.rs"
 required-features = ["io-uring", "pg", "router", "template"]
 
 [[bin]]
-name = "xitca-web-iou"
-path = "./src/main_iou.rs"
-required-features = ["io-uring", "perf", "pg", "template"]
+name = "xitca-web-unrealistic"
+path = "./src/main_unrealistic.rs"
+required-features = ["perf", "pg", "template"]
 
 [[bin]]
 name = "xitca-web-wasm"
@@ -34,7 +34,7 @@ pg = ["dep:xitca-postgres"]
 # diesel orm optional
 pg-orm = ["diesel/r2d2"]
 # diesel async orm optional
-pg-orm-async = ["dep:diesel", "dep:diesel-async", "dep:xitca-postgres-diesel", "futures-util"]
+pg-orm-async = ["dep:diesel", "dep:diesel-async", "dep:xitca-postgres-diesel", "dep:futures-util"]
 # http router optional
 router = ["xitca-http/router"]
 # web optional
@@ -64,7 +64,7 @@ serde_json = { version = "1" }
 xitca-web = { version = "0.7", features = ["json"], optional = true }
 
 # raw-pg optional
-xitca-postgres = { version = "0.2", optional = true }
+xitca-postgres = { version = "0.3", optional = true }
 
 # orm optional
 diesel = { version = "2", features = ["postgres"], optional = true }
@@ -100,9 +100,11 @@ xitca-postgres-diesel = { git = "https://github.com/fakeshadow/xitca-postgres-di
 
 diesel-async = { git = "https://github.com/weiznich/diesel_async", rev = "5b8262b" }
 mio = { git = "https://github.com/fakeshadow/mio", rev = "9bae6012b7ecfc6083350785f71a5e8265358178" }
-
-xitca-codegen = { git = "http://github.com/HFQR/xitca-web", rev = "d3066ba" }
-xitca-http = { git = "http://github.com/HFQR/xitca-web", rev = "d3066ba" }
-xitca-server = { git = "http://github.com/HFQR/xitca-web", rev = "d3066ba" }
-xitca-service = { git = "http://github.com/HFQR/xitca-web", rev = "d3066ba" }
-xitca-web = { git = "http://github.com/HFQR/xitca-web", rev = "d3066ba" }
+tokio = { git = "https://github.com/tokio-rs/tokio.git", rev = "512e9de" }
+
+xitca-codegen = { git = "http://github.com/HFQR/xitca-web", rev = "1de8d9c" }
+xitca-http = { git = "http://github.com/HFQR/xitca-web", rev = "1de8d9c" }
+xitca-postgres = { git = "http://github.com/HFQR/xitca-web", rev = "1de8d9c" }
+xitca-server = { git = "http://github.com/HFQR/xitca-web", rev = "1de8d9c" }
+xitca-service = { git = "http://github.com/HFQR/xitca-web", rev = "1de8d9c" }
+xitca-web = { git = "http://github.com/HFQR/xitca-web", rev = "1de8d9c" }

+ 2 - 2
frameworks/Rust/xitca-web/benchmark_config.json

@@ -24,7 +24,7 @@
         "notes": "",
         "versus": ""
       },
-      "iou": {
+      "unrealistic": {
         "json_url": "/json",
         "plaintext_url": "/plaintext",
         "db_url": "/db",
@@ -42,7 +42,7 @@
         "webserver": "xitca-server",
         "os": "Linux",
         "database_os": "Linux",
-        "display_name": "xitca-web [iou]",
+        "display_name": "xitca-web [unrealistic]",
         "notes": "",
         "versus": ""
       },

+ 15 - 18
frameworks/Rust/xitca-web/src/db.rs

@@ -1,18 +1,16 @@
 #[path = "./db_util.rs"]
 mod db_util;
 
-use std::cell::RefCell;
+use core::cell::RefCell;
 
-use xitca_postgres::{
-    iter::AsyncLendingIterator, pipeline::Pipeline, pool::Pool, statement::Statement, Execute, ExecuteMut,
-};
+use xitca_postgres::{iter::AsyncLendingIterator, pipeline::Pipeline, pool::Pool, statement::Statement, Execute};
 
 use super::{
     ser::{Fortune, Fortunes, World},
     util::{HandleResult, DB_URL},
 };
 
-use db_util::{sort_update_params, update_query, Shared, FORTUNE_STMT, WORLD_STMT};
+use db_util::{not_found, sort_update_params, update_query_from_num, Shared, FORTUNE_STMT, WORLD_STMT};
 
 pub struct Client {
     pool: Pool,
@@ -25,7 +23,7 @@ pub async fn create() -> HandleResult<Client> {
         pool: Pool::builder(DB_URL).capacity(1).build()?,
         shared: Default::default(),
         updates: core::iter::once(Box::from(""))
-            .chain((1..=500).map(update_query))
+            .chain((1..=500).map(update_query_from_num))
             .collect(),
     })
 }
@@ -33,10 +31,10 @@ pub async fn create() -> HandleResult<Client> {
 impl Client {
     pub async fn get_world(&self) -> HandleResult<World> {
         let mut conn = self.pool.get().await?;
-        let stmt = WORLD_STMT.execute_mut(&mut conn).await?;
+        let stmt = WORLD_STMT.execute(&mut conn).await?;
         let id = self.shared.borrow_mut().0.gen_id();
         let mut res = stmt.bind([id]).query(&conn.consume()).await?;
-        let row = res.try_next().await?.ok_or("request World does not exist")?;
+        let row = res.try_next().await?.ok_or_else(not_found)?;
         Ok(World::new(row.get(0), row.get(1)))
     }
 
@@ -44,21 +42,20 @@ impl Client {
         let len = num as usize;
 
         let mut conn = self.pool.get().await?;
-        let stmt = WORLD_STMT.execute_mut(&mut conn).await?;
+        let stmt = WORLD_STMT.execute(&mut conn).await?;
 
         let mut res = {
             let (ref mut rng, ref mut buf) = *self.shared.borrow_mut();
             let mut pipe = Pipeline::with_capacity_from_buf(len, buf);
-            (0..num).try_for_each(|_| stmt.bind([rng.gen_id()]).query_mut(&mut pipe))?;
+            (0..num).try_for_each(|_| stmt.bind([rng.gen_id()]).query(&mut pipe))?;
             pipe.query(&conn.consume())?
         };
 
         let mut worlds = Vec::with_capacity(len);
 
         while let Some(mut item) = res.try_next().await? {
-            while let Some(row) = item.try_next().await? {
-                worlds.push(World::new(row.get(0), row.get(1)))
-            }
+            let row = item.try_next().await?.ok_or_else(not_found)?;
+            worlds.push(World::new(row.get(0), row.get(1)));
         }
 
         Ok(worlds)
@@ -69,8 +66,8 @@ impl Client {
 
         let update = self.updates.get(len).ok_or("request num is out of range")?;
         let mut conn = self.pool.get().await?;
-        let world_stmt = WORLD_STMT.execute_mut(&mut conn).await?;
-        let update_stmt = Statement::named(update, &[]).execute_mut(&mut conn).await?;
+        let world_stmt = WORLD_STMT.execute(&mut conn).await?;
+        let update_stmt = Statement::named(update, &[]).execute(&mut conn).await?;
 
         let mut params = Vec::with_capacity(len);
 
@@ -81,9 +78,9 @@ impl Client {
                 let w_id = rng.gen_id();
                 let r_id = rng.gen_id();
                 params.push([w_id, r_id]);
-                world_stmt.bind([w_id]).query_mut(&mut pipe)
+                world_stmt.bind([w_id]).query(&mut pipe)
             })?;
-            update_stmt.bind(sort_update_params(&params)).query_mut(&mut pipe)?;
+            update_stmt.bind(sort_update_params(&params)).query(&mut pipe)?;
             pipe.query(&conn.consume())?
         };
 
@@ -106,7 +103,7 @@ impl Client {
         items.push(Fortune::new(0, "Additional fortune added at request time."));
 
         let mut conn = self.pool.get().await?;
-        let stmt = FORTUNE_STMT.execute_mut(&mut conn).await?;
+        let stmt = FORTUNE_STMT.execute(&mut conn).await?;
         let mut res = stmt.query(&conn.consume()).await?;
 
         while let Some(row) = res.try_next().await? {

+ 25 - 51
frameworks/Rust/xitca-web/src/db_diesel.rs

@@ -1,3 +1,6 @@
+#[path = "./db_util.rs"]
+mod db_util;
+
 use std::{
     io,
     sync::{Arc, Mutex},
@@ -7,9 +10,11 @@ use diesel::{prelude::*, r2d2};
 
 use crate::{
     ser::{Fortune, Fortunes, World},
-    util::{bulk_update_gen, Error, HandleResult, Rand, DB_URL},
+    util::{HandleResult, Rand, DB_URL},
 };
 
+use db_util::{not_found, update_query_from_ids};
+
 pub type Pool = Arc<_Pool>;
 
 pub struct _Pool {
@@ -34,12 +39,6 @@ pub fn create() -> io::Result<Arc<_Pool>> {
         })
 }
 
-#[cold]
-#[inline(never)]
-fn not_found() -> Error {
-    "world not found".into()
-}
-
 impl _Pool {
     pub fn get_world(&self) -> HandleResult<World> {
         use crate::schema::world::dsl::*;
@@ -53,16 +52,12 @@ impl _Pool {
         use crate::schema::world::dsl::*;
 
         let mut conn = self.pool.get()?;
-        (0..num)
-            .map(|_| {
-                let w_id = self.rng.lock().unwrap().gen_id();
-                world
-                    .filter(id.eq(w_id))
-                    .load::<World>(&mut conn)?
-                    .pop()
-                    .ok_or_else(not_found)
-            })
-            .collect()
+        core::iter::repeat_with(|| {
+            let w_id = self.rng.lock().unwrap().gen_id();
+            world.filter(id.eq(w_id)).load(&mut conn)?.pop().ok_or_else(not_found)
+        })
+        .take(num as _)
+        .collect()
     }
 
     pub fn update(&self, num: u16) -> HandleResult<Vec<World>> {
@@ -75,30 +70,20 @@ impl _Pool {
 
         rngs.sort_by(|(a, _), (b, _)| a.cmp(b));
 
-        let mut worlds = {
-            let mut conn = self.pool.get()?;
+        let update_sql = update_query_from_ids(&rngs);
 
-            let worlds = rngs
-                .iter()
-                .map(|(w_id, num)| {
-                    world
-                        .filter(id.eq(w_id))
-                        .load::<World>(&mut conn)?
-                        .pop()
-                        .map(|mut w| {
-                            w.randomnumber = *num;
-                            w
-                        })
-                        .ok_or_else(not_found)
-                })
-                .collect::<HandleResult<Vec<_>>>()?;
-
-            diesel::sql_query(update_query(&rngs)).execute(&mut conn)?;
-
-            worlds
-        };
+        let mut conn = self.pool.get()?;
+
+        let worlds = rngs
+            .into_iter()
+            .map(|(w_id, num)| {
+                let mut w: World = world.filter(id.eq(w_id)).load(&mut conn)?.pop().ok_or_else(not_found)?;
+                w.randomnumber = num;
+                Ok(w)
+            })
+            .collect::<HandleResult<Vec<_>>>()?;
 
-        worlds.sort_by_key(|w| w.id);
+        diesel::sql_query(update_sql).execute(&mut conn)?;
 
         Ok(worlds)
     }
@@ -108,7 +93,7 @@ impl _Pool {
 
         let mut items = {
             let mut conn = self.pool.get()?;
-            fortune.load::<Fortune>(&mut conn)?
+            fortune.load(&mut conn)?
         };
 
         items.push(Fortune::new(0, "Additional fortune added at request time."));
@@ -117,14 +102,3 @@ impl _Pool {
         Ok(Fortunes::new(items))
     }
 }
-
-// diesel does not support high level bulk update api. use raw sql to bypass the limitation.
-// relate discussion: https://github.com/diesel-rs/diesel/discussions/2879
-fn update_query(ids: &[(i32, i32)]) -> String {
-    bulk_update_gen(|query| {
-        use std::fmt::Write;
-        ids.iter().for_each(|(w_id, num)| {
-            write!(query, "({}::int,{}::int),", w_id, num).unwrap();
-        });
-    })
-}

+ 35 - 66
frameworks/Rust/xitca-web/src/db_diesel_async.rs

@@ -1,7 +1,7 @@
-use std::{
-    io,
-    sync::{Arc, Mutex},
-};
+#[path = "./db_util.rs"]
+mod db_util;
+
+use std::{io, sync::Mutex};
 
 use diesel::prelude::*;
 use diesel_async::{
@@ -16,17 +16,17 @@ use xitca_postgres_diesel::AsyncPgConnection;
 
 use crate::{
     ser::{Fortune, Fortunes, World},
-    util::{bulk_update_gen, Error, HandleResult, Rand, DB_URL},
+    util::{HandleResult, Rand, DB_URL},
 };
 
-pub type Pool = Arc<_Pool>;
+use db_util::{not_found, update_query_from_ids};
 
-pub struct _Pool {
+pub struct Pool {
     pool: bb8::Pool<AsyncPgConnection>,
     rng: Mutex<Rand>,
 }
 
-pub async fn create() -> io::Result<Arc<_Pool>> {
+pub async fn create() -> io::Result<Pool> {
     bb8::Pool::builder()
         .max_size(1)
         .min_idle(Some(1))
@@ -34,21 +34,13 @@ pub async fn create() -> io::Result<Arc<_Pool>> {
         .build(AsyncDieselConnectionManager::new(DB_URL))
         .await
         .map_err(io::Error::other)
-        .map(|pool| {
-            Arc::new(_Pool {
-                pool,
-                rng: Mutex::new(Rand::default()),
-            })
+        .map(|pool| Pool {
+            pool,
+            rng: Mutex::new(Rand::default()),
         })
 }
 
-#[cold]
-#[inline(never)]
-fn not_found() -> Error {
-    "world not found".into()
-}
-
-impl _Pool {
+impl Pool {
     pub async fn get_world(&self) -> HandleResult<World> {
         use crate::schema::world::dsl::*;
         {
@@ -66,13 +58,13 @@ impl _Pool {
         {
             let mut conn = self.pool.get().await?;
             let mut rng = self.rng.lock().unwrap();
-            (0..num)
-                .map(|_| {
-                    let w_id = rng.gen_id();
-                    let fut = world.filter(id.eq(w_id)).load::<World>(&mut conn);
-                    async { fut.await?.pop().ok_or_else(not_found) }
-                })
-                .collect::<FuturesUnordered<_>>()
+            core::iter::repeat_with(|| {
+                let w_id = rng.gen_id();
+                let fut = world.filter(id.eq(w_id)).load(&mut conn);
+                async { fut.await?.pop().ok_or_else(not_found) }
+            })
+            .take(num as _)
+            .collect::<FuturesUnordered<_>>()
         }
         .try_collect()
         .await
@@ -81,48 +73,36 @@ impl _Pool {
     pub async fn update(&self, num: u16) -> HandleResult<Vec<World>> {
         use crate::schema::world::dsl::*;
 
-        let mut rngs = Vec::with_capacity(num as _);
-
         let (select_res, update_res) = {
             let mut conn = self.pool.get().await?;
-
             let mut rng = self.rng.lock().unwrap();
 
-            let select = (0..num)
-                .map(|_| {
-                    let w_id = rng.gen_id();
-                    let num = rng.gen_id();
-
-                    rngs.push((w_id, num));
+            let (select, mut rngs) = core::iter::repeat_with(|| {
+                let w_id = rng.gen_id();
+                let num = rng.gen_id();
 
-                    let fut = world.filter(id.eq(w_id)).load::<World>(&mut conn);
+                let fut = world.filter(id.eq(w_id)).load::<World>(&mut conn);
+                let select = async move {
+                    let mut w = fut.await?.pop().ok_or_else(not_found)?;
+                    w.randomnumber = num;
+                    HandleResult::Ok(w)
+                };
 
-                    async move {
-                        fut.await?
-                            .pop()
-                            .map(|mut w| {
-                                w.randomnumber = num;
-                                w
-                            })
-                            .ok_or_else(not_found)
-                    }
-                })
-                .collect::<FuturesUnordered<_>>();
+                (select, (w_id, num))
+            })
+            .take(num as _)
+            .collect::<(FuturesUnordered<_>, Vec<_>)>();
 
             rngs.sort_by(|(a, _), (b, _)| a.cmp(b));
 
-            let update = diesel::sql_query(update_query(&rngs)).execute(&mut conn);
+            let update = diesel::sql_query(update_query_from_ids(&rngs)).execute(&mut conn);
 
             join(select.try_collect::<Vec<_>>(), update)
         }
         .await;
 
         update_res?;
-        let mut worlds = select_res?;
-
-        worlds.sort_by_key(|w| w.id);
-
-        Ok(worlds)
+        select_res
     }
 
     pub async fn tell_fortune(&self) -> HandleResult<Fortunes> {
@@ -130,7 +110,7 @@ impl _Pool {
 
         let mut items = {
             let mut conn = self.pool.get().await?;
-            fortune.load::<Fortune>(&mut conn)
+            fortune.load(&mut conn)
         }
         .await?;
 
@@ -140,14 +120,3 @@ impl _Pool {
         Ok(Fortunes::new(items))
     }
 }
-
-// diesel does not support high level bulk update api. use raw sql to bypass the limitation.
-// relate discussion: https://github.com/diesel-rs/diesel/discussions/2879
-fn update_query(ids: &[(i32, i32)]) -> String {
-    bulk_update_gen(|query| {
-        use std::fmt::Write;
-        ids.iter().for_each(|(w_id, num)| {
-            write!(query, "({}::int,{}::int),", w_id, num).unwrap();
-        });
-    })
-}

+ 19 - 18
frameworks/Rust/xitca-web/src/db_unrealistic.rs

@@ -6,14 +6,14 @@ mod db_util;
 
 use std::cell::RefCell;
 
-use xitca_postgres::{iter::AsyncLendingIterator, pipeline::Pipeline, statement::Statement, Execute, ExecuteMut};
+use xitca_postgres::{iter::AsyncLendingIterator, pipeline::Pipeline, statement::Statement, Execute};
 
 use super::{
     ser::{Fortune, Fortunes, World},
     util::{HandleResult, DB_URL},
 };
 
-use db_util::{sort_update_params, update_query, Shared, FORTUNE_STMT, WORLD_STMT};
+use db_util::{not_found, sort_update_params, update_query_from_num, Shared, FORTUNE_STMT, WORLD_STMT};
 
 pub struct Client {
     cli: xitca_postgres::Client,
@@ -36,7 +36,7 @@ pub async fn create() -> HandleResult<Client> {
 
     let mut updates = vec![Statement::default()];
 
-    for update in (1..=500).map(update_query).into_iter() {
+    for update in (1..=500).map(update_query_from_num).into_iter() {
         let stmt = Statement::named(&update, &[]).execute(&cli).await?.leak();
         updates.push(stmt);
     }
@@ -54,26 +54,28 @@ impl Client {
     pub async fn get_world(&self) -> HandleResult<World> {
         let id = self.shared.borrow_mut().0.gen_id();
         let mut res = self.world.bind([id]).query(&self.cli).await?;
-        let row = res.try_next().await?.ok_or("request World does not exist")?;
+        let row = res.try_next().await?.ok_or_else(not_found)?;
         Ok(World::new(row.get(0), row.get(1)))
     }
 
     pub async fn get_worlds(&self, num: u16) -> HandleResult<Vec<World>> {
         let len = num as usize;
 
-        let mut res = {
-            let (ref mut rng, ref mut buf) = *self.shared.borrow_mut();
-            let mut pipe = Pipeline::with_capacity_from_buf(len, buf);
-            (0..num).try_for_each(|_| self.world.bind([rng.gen_id()]).query_mut(&mut pipe))?;
-            pipe.query(&self.cli)?
+        let mut res = Vec::with_capacity(len);
+
+        {
+            let (ref mut rng, ..) = *self.shared.borrow_mut();
+            for _ in 0..len {
+                let stream = self.world.bind([rng.gen_id()]).query(&self.cli).await?;
+                res.push(stream);
+            }
         };
 
         let mut worlds = Vec::with_capacity(len);
 
-        while let Some(mut item) = res.try_next().await? {
-            while let Some(row) = item.try_next().await? {
-                worlds.push(World::new(row.get(0), row.get(1)))
-            }
+        for mut stream in res {
+            let row = stream.try_next().await?.ok_or_else(not_found)?;
+            worlds.push(World::new(row.get(0), row.get(1)));
         }
 
         Ok(worlds)
@@ -86,16 +88,15 @@ impl Client {
 
         let mut res = {
             let (ref mut rng, ref mut buf) = *self.shared.borrow_mut();
-            let mut pipe = Pipeline::with_capacity_from_buf(len + 1, buf);
+            // unrealistic as all queries are sent with only one sync point.
+            let mut pipe = Pipeline::unsync_with_capacity_from_buf(len + 1, buf);
             (0..num).try_for_each(|_| {
                 let w_id = rng.gen_id();
                 let r_id = rng.gen_id();
                 params.push([w_id, r_id]);
-                self.world.bind([w_id]).query_mut(&mut pipe)
+                self.world.bind([w_id]).query(&mut pipe)
             })?;
-            self.updates[len]
-                .bind(sort_update_params(&params))
-                .query_mut(&mut pipe)?;
+            self.updates[len].bind(sort_update_params(&params)).query(&mut pipe)?;
             pipe.query(&self.cli)?
         };
 

+ 82 - 39
frameworks/Rust/xitca-web/src/db_util.rs

@@ -1,53 +1,96 @@
-use xitca_io::bytes::BytesMut;
-use xitca_postgres::{
-    statement::{Statement, StatementNamed},
-    types::Type,
-};
+use crate::util::Error;
 
-use crate::util::{bulk_update_gen, Rand};
+#[cfg(any(feature = "pg-orm", feature = "pg-orm-async"))]
+// diesel does not support high level bulk update api. use raw sql to bypass the limitation.
+// relate discussion: https://github.com/diesel-rs/diesel/discussions/2879
+pub fn update_query_from_ids(ids: &[(i32, i32)]) -> String {
+    update_query(|query| {
+        use core::fmt::Write;
+        ids.iter().for_each(|(w_id, num)| {
+            write!(query, "({}::int,{}::int),", w_id, num).unwrap();
+        });
+    })
+}
 
-pub(super) type Shared = (Rand, BytesMut);
+fn update_query(func: impl FnOnce(&mut String)) -> String {
+    const PREFIX: &str = "UPDATE world SET randomNumber = w.r FROM (VALUES ";
+    const SUFFIX: &str = ") AS w (i,r) WHERE world.id = w.i";
 
-pub(super) const FORTUNE_STMT: StatementNamed = Statement::named("SELECT * FROM fortune", &[]);
-pub(super) const WORLD_STMT: StatementNamed = Statement::named("SELECT * FROM world WHERE id=$1", &[Type::INT4]);
+    let mut query = String::from(PREFIX);
 
-pub(super) fn update_query(num: usize) -> Box<str> {
-    bulk_update_gen(|query| {
-        use std::fmt::Write;
-        (1..=num).fold((1, query), |(idx, query), _| {
-            write!(query, "(${}::int,${}::int),", idx, idx + 1).unwrap();
-            (idx + 2, query)
-        });
-    })
-    .into_boxed_str()
+    func(&mut query);
+
+    if query.ends_with(',') {
+        query.pop();
+    }
+
+    query.push_str(SUFFIX);
+
+    query
 }
 
-pub(super) fn sort_update_params(params: &[[i32; 2]]) -> impl ExactSizeIterator<Item = i32> {
-    let mut params = params.to_owned();
-    params.sort_by(|a, b| a[0].cmp(&b[0]));
+#[cold]
+#[inline(never)]
+pub fn not_found() -> Error {
+    "request World does not exist".into()
+}
 
-    struct ParamIter<I>(I);
+#[cfg(feature = "pg")]
+pub use pg::*;
 
-    impl<I> Iterator for ParamIter<I>
-    where
-        I: Iterator,
-    {
-        type Item = I::Item;
+#[cfg(feature = "pg")]
+pub mod pg {
+    use xitca_io::bytes::BytesMut;
+    use xitca_postgres::{
+        statement::{Statement, StatementNamed},
+        types::Type,
+    };
 
-        #[inline]
-        fn next(&mut self) -> Option<Self::Item> {
-            self.0.next()
-        }
+    use crate::util::Rand;
 
-        #[inline]
-        fn size_hint(&self) -> (usize, Option<usize>) {
-            self.0.size_hint()
-        }
+    pub type Shared = (Rand, BytesMut);
+
+    pub const FORTUNE_STMT: StatementNamed = Statement::named("SELECT * FROM fortune", &[]);
+    pub const WORLD_STMT: StatementNamed = Statement::named("SELECT * FROM world WHERE id=$1", &[Type::INT4]);
+
+    pub fn update_query_from_num(num: usize) -> Box<str> {
+        super::update_query(|query| {
+            use core::fmt::Write;
+            (1..=num).fold(1, |idx, _| {
+                write!(query, "(${}::int,${}::int),", idx, idx + 1).unwrap();
+                idx + 2
+            });
+        })
+        .into_boxed_str()
     }
 
-    // impl depends on compiler optimization to flat Vec<[T]> to Vec<T> when inferring
-    // it's size hint. possible to cause runtime panic.
-    impl<I> ExactSizeIterator for ParamIter<I> where I: Iterator {}
+    pub fn sort_update_params(params: &[[i32; 2]]) -> impl ExactSizeIterator<Item = i32> {
+        let mut params = params.to_owned();
+        params.sort_by(|a, b| a[0].cmp(&b[0]));
+
+        struct ParamIter<I>(I);
+
+        impl<I> Iterator for ParamIter<I>
+        where
+            I: Iterator,
+        {
+            type Item = I::Item;
 
-    ParamIter(params.into_iter().flatten())
+            #[inline]
+            fn next(&mut self) -> Option<Self::Item> {
+                self.0.next()
+            }
+
+            #[inline]
+            fn size_hint(&self) -> (usize, Option<usize>) {
+                self.0.size_hint()
+            }
+        }
+
+        // impl depends on compiler optimization to flat Vec<[T]> to Vec<T> when inferring
+        // it's size hint. possible to cause runtime panic.
+        impl<I> ExactSizeIterator for ParamIter<I> where I: Iterator {}
+
+        ParamIter(params.into_iter().flatten())
+    }
 }

+ 47 - 51
frameworks/Rust/xitca-web/src/main_iou.rs → frameworks/Rust/xitca-web/src/main_unrealistic.rs

@@ -1,5 +1,4 @@
-// reference of if/how moving from epoll to io-uring(or mixture of the two) make sense for network io.
-// with comment on explaining why some practice are unrealistic
+// unrealistic bench showcase popular tricks for boosting bench score artificially
 
 // custom global memory allocator don't affect real world performance in noticeable amount.
 // in real world they should be used for reason like security, debug/profiling capability etc.
@@ -15,13 +14,16 @@ use std::{convert::Infallible, io};
 
 use xitca_http::{
     bytes::BufMutWriter,
-    h1::dispatcher_uring_unreal::{Dispatcher, Request, Response},
+    h1::dispatcher_unreal::{Dispatcher, Request, Response},
     http::StatusCode,
 };
-use xitca_io::net::io_uring::TcpStream;
+use xitca_io::net::TcpStream;
 use xitca_service::Service;
 
-use self::{ser::Message, util::State};
+use self::{
+    ser::Message,
+    util::{QueryParse, State},
+};
 
 fn main() -> io::Result<()> {
     let addr = "0.0.0.0:8080".parse().unwrap();
@@ -30,36 +32,40 @@ fn main() -> io::Result<()> {
 
     let handle = core::iter::repeat_with(|| {
         std::thread::spawn(move || {
-            tokio_uring::start(async {
-                let socket = tokio::net::TcpSocket::new_v4()?;
-                socket.set_reuseaddr(true)?;
-                // unrealistic due to following reason:
-                // 1. this only works good on unix system.
-                // 2. no resource distribution adjustment between sockets on different threads. causing uneven workload
-                // where some threads are idle while others busy. resulting in overall increased latency
-                socket.set_reuseport(true)?;
-                socket.bind(addr)?;
-                let listener = socket.listen(1024)?;
-
-                let client = db::create().await.unwrap();
-
-                // unrealistic http dispatcher. no spec check. no security feature.
-                let service = Dispatcher::new(handler, State::new(client));
-
-                loop {
-                    match listener.accept().await {
-                        Ok((stream, _)) => {
-                            let stream = stream.into_std()?;
-                            let stream = TcpStream::from_std(stream);
-                            let service = service.clone();
-                            tokio::task::spawn_local(async move {
-                                let _ = service.call(stream).await;
-                            });
-                        }
-                        Err(e) => return Err(e),
-                    };
-                }
-            })
+            tokio::runtime::Builder::new_current_thread()
+                .enable_all()
+                .build_local(&Default::default())
+                .unwrap()
+                .block_on(async {
+                    let socket = tokio::net::TcpSocket::new_v4()?;
+                    socket.set_reuseaddr(true)?;
+                    // unrealistic due to following reason:
+                    // 1. this only works good on unix system.
+                    // 2. no resource distribution adjustment between sockets on different threads. causing uneven workload
+                    // where some threads are idle while others busy. resulting in overall increased latency
+                    socket.set_reuseport(true)?;
+                    socket.bind(addr)?;
+                    let listener = socket.listen(1024)?;
+
+                    let client = db::create().await.unwrap();
+
+                    // unrealistic http dispatcher. no spec check. no security feature.
+                    let service = Dispatcher::new(handler, State::new(client));
+
+                    loop {
+                        match listener.accept().await {
+                            Ok((stream, _)) => {
+                                let stream = stream.into_std()?;
+                                let stream = TcpStream::from_std(stream)?;
+                                let service = service.clone();
+                                tokio::task::spawn_local(async move {
+                                    let _ = service.call(stream).await;
+                                });
+                            }
+                            Err(e) => return Err(e),
+                        };
+                    }
+                })
         })
     })
     .take(cores)
@@ -74,9 +80,9 @@ fn main() -> io::Result<()> {
     Ok(())
 }
 
-async fn handler<'h>(req: Request<'h, '_>, res: Response<'h>, state: &State<db::Client>) -> Response<'h, 3> {
+async fn handler<'h>(req: Request<'h>, res: Response<'h>, state: &State<db::Client>) -> Response<'h, 3> {
     // unrealistic due to no http method check
-    match req.path.unwrap_or("404") {
+    match req.path {
         // unrealistic due to no dynamic path matching
         "/plaintext" => {
             // unrealistic due to no body streaming and no post processing. violating middleware feature of xitca-web
@@ -111,13 +117,13 @@ async fn handler<'h>(req: Request<'h, '_>, res: Response<'h>, state: &State<db::
             let world = state.client.get_world().await.unwrap();
             json_response(res, state, &world)
         }
-        p if p.starts_with("/queries") => {
-            let num = path_param(p);
+        p if p.starts_with("/q") => {
+            let num = p["/queries?q=".len()..].parse_query();
             let worlds = state.client.get_worlds(num).await.unwrap();
             json_response(res, state, &worlds)
         }
-        p if p.starts_with("/updates") => {
-            let num = path_param(p);
+        p if p.starts_with("/u") => {
+            let num = p["/updates?q=".len()..].parse_query();
             let worlds = state.client.update(num).await.unwrap();
             json_response(res, state, &worlds)
         }
@@ -139,13 +145,3 @@ where
     buf.clear();
     res
 }
-
-fn path_param(query: &str) -> u16 {
-    use atoi::FromRadix10;
-    let q = if let Some(pos) = query.find("?q") {
-        u16::from_radix_10(query.split_at(pos + 3).1.as_ref()).0
-    } else {
-        1
-    };
-    q.clamp(1, 500)
-}

+ 2 - 2
frameworks/Rust/xitca-web/src/ser.rs

@@ -8,7 +8,7 @@ use xitca_http::{
     bytes::{BufMutWriter, Bytes},
     http::{
         self,
-        const_header_value::{JSON, TEXT, TEXT_HTML_UTF8},
+        const_header_value::{JSON, TEXT_HTML_UTF8, TEXT_UTF8},
         header::CONTENT_TYPE,
         IntoResponse as _, RequestExt, StatusCode,
     },
@@ -213,7 +213,7 @@ impl<Ext> IntoResponse for Request<Ext> {
 
     fn text_response(self) -> Result<Response, Error> {
         let mut res = self.into_response(const { Bytes::from_static(HELLO_BYTES) });
-        res.headers_mut().insert(CONTENT_TYPE, TEXT);
+        res.headers_mut().insert(CONTENT_TYPE, TEXT_UTF8);
         Ok(res)
     }
 

+ 7 - 25
frameworks/Rust/xitca-web/src/util.rs

@@ -10,34 +10,16 @@ pub trait QueryParse {
 
 impl QueryParse for Option<&str> {
     fn parse_query(self) -> u16 {
-        self.and_then(|this| {
-            use atoi::FromRadix10;
-            this.find('q')
-                .map(|pos| u16::from_radix_10(this.split_at(pos + 2).1.as_ref()).0)
-        })
-        .unwrap_or(1)
-        .clamp(1, 500)
+        self.and_then(|q| q.find('q').map(|pos| q.split_at(pos + 2).1.parse_query()))
+            .unwrap_or(1)
     }
 }
 
-pub fn bulk_update_gen<F>(func: F) -> String
-where
-    F: FnOnce(&mut String),
-{
-    const PREFIX: &str = "UPDATE world SET randomNumber = w.r FROM (VALUES ";
-    const SUFFIX: &str = ") AS w (i,r) WHERE world.id = w.i";
-
-    let mut query = String::from(PREFIX);
-
-    func(&mut query);
-
-    if query.ends_with(',') {
-        query.pop();
+impl QueryParse for &str {
+    fn parse_query(self) -> u16 {
+        use atoi::FromRadix10;
+        u16::from_radix_10(self.as_bytes()).0.clamp(1, 500)
     }
-
-    query.push_str(SUFFIX);
-
-    query
 }
 
 #[allow(clippy::declare_interior_mutable_const)]
@@ -64,7 +46,7 @@ impl<DB> State<DB> {
 }
 
 #[cfg(not(target_arch = "wasm32"))]
-mod non_wasm {
+pub mod non_wasm {
     use rand::{rngs::SmallRng, Rng, SeedableRng};
 
     pub struct Rand(SmallRng);

+ 0 - 10
frameworks/Rust/xitca-web/xitca-web-iou.dockerfile

@@ -1,10 +0,0 @@
-FROM rust:1.81
-
-ADD ./ /xitca-web
-WORKDIR /xitca-web
-
-RUN cargo build --release --bin xitca-web-iou --features io-uring,perf,pg,template
-
-EXPOSE 8080
-
-CMD ./target/release/xitca-web-iou

+ 10 - 0
frameworks/Rust/xitca-web/xitca-web-unrealistic.dockerfile

@@ -0,0 +1,10 @@
+FROM rust:1.81
+
+ADD ./ /xitca-web
+WORKDIR /xitca-web
+
+RUN cargo build --release --bin xitca-web-unrealistic --features perf,pg,template
+
+EXPOSE 8080
+
+CMD ./target/release/xitca-web-unrealistic