diff --git a/frameworks/Rust/xitca-web/.cargo/config.toml b/frameworks/Rust/xitca-web/.cargo/config.toml index 7d3c09f8a1a..df736010a76 100644 --- a/frameworks/Rust/xitca-web/.cargo/config.toml +++ b/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] diff --git a/frameworks/Rust/xitca-web/Cargo.lock b/frameworks/Rust/xitca-web/Cargo.lock index 71038073ca5..070a2e67550 100644 --- a/frameworks/Rust/xitca-web/Cargo.lock +++ b/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", diff --git a/frameworks/Rust/xitca-web/Cargo.toml b/frameworks/Rust/xitca-web/Cargo.toml index 03d7098e796..c547c16de48 100644 --- a/frameworks/Rust/xitca-web/Cargo.toml +++ b/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" } diff --git a/frameworks/Rust/xitca-web/benchmark_config.json b/frameworks/Rust/xitca-web/benchmark_config.json index 973d1961472..db81e362742 100755 --- a/frameworks/Rust/xitca-web/benchmark_config.json +++ b/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": "" }, diff --git a/frameworks/Rust/xitca-web/src/db.rs b/frameworks/Rust/xitca-web/src/db.rs index ad75d6f8bbe..abb9ef10870 100644 --- a/frameworks/Rust/xitca-web/src/db.rs +++ b/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 { 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 { impl Client { pub async fn get_world(&self) -> HandleResult { 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(¶ms)).query_mut(&mut pipe)?; + update_stmt.bind(sort_update_params(¶ms)).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? { diff --git a/frameworks/Rust/xitca-web/src/db_diesel.rs b/frameworks/Rust/xitca-web/src/db_diesel.rs index cf19768ac9f..1675a7c2cd1 100644 --- a/frameworks/Rust/xitca-web/src/db_diesel.rs +++ b/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> { }) } -#[cold] -#[inline(never)] -fn not_found() -> Error { - "world not found".into() -} - impl _Pool { pub fn get_world(&self) -> HandleResult { 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::(&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> { @@ -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::(&mut conn)? - .pop() - .map(|mut w| { - w.randomnumber = *num; - w - }) - .ok_or_else(not_found) - }) - .collect::>>()?; - - 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::>>()?; - 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::(&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(); - }); - }) -} diff --git a/frameworks/Rust/xitca-web/src/db_diesel_async.rs b/frameworks/Rust/xitca-web/src/db_diesel_async.rs index fdd93e43a74..e3ee7e895fd 100644 --- a/frameworks/Rust/xitca-web/src/db_diesel_async.rs +++ b/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, rng: Mutex, } -pub async fn create() -> io::Result> { +pub async fn create() -> io::Result { bb8::Pool::builder() .max_size(1) .min_idle(Some(1)) @@ -34,21 +34,13 @@ pub async fn create() -> io::Result> { .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 { 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::(&mut conn); - async { fut.await?.pop().ok_or_else(not_found) } - }) - .collect::>() + 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::>() } .try_collect() .await @@ -81,48 +73,36 @@ impl _Pool { pub async fn update(&self, num: u16) -> HandleResult> { 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::(&mut conn); + let fut = world.filter(id.eq(w_id)).load::(&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::>(); + (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::>(), 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 { @@ -130,7 +110,7 @@ impl _Pool { let mut items = { let mut conn = self.pool.get().await?; - fortune.load::(&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(); - }); - }) -} diff --git a/frameworks/Rust/xitca-web/src/db_unrealistic.rs b/frameworks/Rust/xitca-web/src/db_unrealistic.rs index 7b04449a716..99e4249e39b 100644 --- a/frameworks/Rust/xitca-web/src/db_unrealistic.rs +++ b/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 { 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 { 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> { 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(¶ms)) - .query_mut(&mut pipe)?; + self.updates[len].bind(sort_update_params(¶ms)).query(&mut pipe)?; pipe.query(&self.cli)? }; diff --git a/frameworks/Rust/xitca-web/src/db_util.rs b/frameworks/Rust/xitca-web/src/db_util.rs index 19879333427..42c2c455bbe 100644 --- a/frameworks/Rust/xitca-web/src/db_util.rs +++ b/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 { - 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 { - 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); +#[cfg(feature = "pg")] +pub use pg::*; - impl Iterator for ParamIter - 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.0.next() - } + use crate::util::Rand; - #[inline] - fn size_hint(&self) -> (usize, Option) { - 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 { + 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 when inferring - // it's size hint. possible to cause runtime panic. - impl ExactSizeIterator for ParamIter where I: Iterator {} + pub fn sort_update_params(params: &[[i32; 2]]) -> impl ExactSizeIterator { + let mut params = params.to_owned(); + params.sort_by(|a, b| a[0].cmp(&b[0])); + + struct ParamIter(I); + + impl Iterator for ParamIter + where + I: Iterator, + { + type Item = I::Item; - ParamIter(params.into_iter().flatten()) + #[inline] + fn next(&mut self) -> Option { + self.0.next() + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + self.0.size_hint() + } + } + + // impl depends on compiler optimization to flat Vec<[T]> to Vec when inferring + // it's size hint. possible to cause runtime panic. + impl ExactSizeIterator for ParamIter where I: Iterator {} + + ParamIter(params.into_iter().flatten()) + } } diff --git a/frameworks/Rust/xitca-web/src/main_iou.rs b/frameworks/Rust/xitca-web/src/main_unrealistic.rs similarity index 60% rename from frameworks/Rust/xitca-web/src/main_iou.rs rename to frameworks/Rust/xitca-web/src/main_unrealistic.rs index 7dcf6d1a719..e291799408c 100644 --- a/frameworks/Rust/xitca-web/src/main_iou.rs +++ b/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) -> Response<'h, 3> { +async fn handler<'h>(req: Request<'h>, res: Response<'h>, state: &State) -> 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 { - 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) -} diff --git a/frameworks/Rust/xitca-web/src/ser.rs b/frameworks/Rust/xitca-web/src/ser.rs index 3c630c54bc6..edf9183a8c0 100644 --- a/frameworks/Rust/xitca-web/src/ser.rs +++ b/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 IntoResponse for Request { fn text_response(self) -> Result { 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) } diff --git a/frameworks/Rust/xitca-web/src/util.rs b/frameworks/Rust/xitca-web/src/util.rs index 9183d067f5d..3998a2ef1ec 100755 --- a/frameworks/Rust/xitca-web/src/util.rs +++ b/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(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 State { } #[cfg(not(target_arch = "wasm32"))] -mod non_wasm { +pub mod non_wasm { use rand::{rngs::SmallRng, Rng, SeedableRng}; pub struct Rand(SmallRng); diff --git a/frameworks/Rust/xitca-web/xitca-web-iou.dockerfile b/frameworks/Rust/xitca-web/xitca-web-iou.dockerfile deleted file mode 100644 index 461d5740603..00000000000 --- a/frameworks/Rust/xitca-web/xitca-web-iou.dockerfile +++ /dev/null @@ -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 diff --git a/frameworks/Rust/xitca-web/xitca-web-unrealistic.dockerfile b/frameworks/Rust/xitca-web/xitca-web-unrealistic.dockerfile new file mode 100644 index 00000000000..f202947acc0 --- /dev/null +++ b/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