Skip to content

Commit 8d4f5ed

Browse files
authored
[xitca-web] add bench for async orm (#9287)
* [xitca-web] add bench for async orm * dep fix * use release build of xitca-postgres * fix framework naming * dep update * fix build * add unrealistic db impl * dedup * improve compile time * update to pre-release version of xitca-web
1 parent 643bbbd commit 8d4f5ed

File tree

15 files changed

+945
-685
lines changed

15 files changed

+945
-685
lines changed

frameworks/Rust/xitca-web/Cargo.lock

Lines changed: 274 additions & 302 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

frameworks/Rust/xitca-web/Cargo.toml

Lines changed: 35 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -19,65 +19,66 @@ path = "./src/main_wasm.rs"
1919
required-features = ["web"]
2020

2121
[[bin]]
22-
name = "xitca-web-axum"
23-
path = "./src/main_axum.rs"
24-
required-features = ["axum", "io-uring", "perf", "pg-sync", "template"]
22+
name = "xitca-web-orm"
23+
path = "./src/main_orm.rs"
24+
required-features = ["pg-orm-async", "template", "web-codegen"]
2525

2626
[[bin]]
2727
name = "xitca-web-sync"
2828
path = "./src/main_sync.rs"
2929
required-features = ["pg-orm", "template", "web-codegen"]
3030

3131
[features]
32-
# pg optional
32+
# pg client optional
3333
pg = ["dep:xitca-postgres"]
34-
# pg send/sync optional
35-
pg-sync = ["dep:xitca-postgres"]
36-
# pg orm optional
37-
pg-orm = ["dep:diesel"]
34+
# diesel orm optional
35+
pg-orm = ["diesel/r2d2"]
36+
# diesel async orm optional
37+
pg-orm-async = ["dep:diesel", "dep:diesel-async", "dep:xitca-postgres-diesel", "futures-util"]
3838
# http router optional
3939
router = ["xitca-http/router"]
4040
# web optional
4141
web = ["dep:xitca-web"]
42-
# web codegen optional
42+
# web with macros optional
4343
web-codegen = ["xitca-web/codegen", "xitca-web/urlencoded"]
4444
# template optional
4545
template = ["dep:sailfish"]
4646
# io-uring optional
47-
io-uring = ["xitca-http/io-uring", "xitca-server/io-uring"]
48-
# axum optional
49-
axum = ["dep:axum", "dep:http-body", "dep:tower", "dep:tower-http", "xitca-web/tower-http-compat" ]
47+
io-uring = ["dep:tokio-uring", "xitca-http/io-uring", "xitca-server/io-uring"]
5048
# unrealistic performance optimization
5149
perf = ["dep:mimalloc", "tokio/parking_lot"]
5250

5351
[dependencies]
54-
xitca-http = "0.6"
55-
xitca-io = "0.4"
56-
xitca-server = "0.4"
57-
xitca-service = "0.2"
52+
xitca-http = "0.7"
53+
xitca-io = "0.4.1"
54+
xitca-server = "0.5"
55+
xitca-service = "0.3"
5856
xitca-unsafe-collection = "0.2"
5957

6058
atoi = "2"
59+
httparse = "1"
6160
serde = { version = "1" }
6261
serde_json = { version = "1" }
6362

6463
# web optional
65-
xitca-web = { version = "0.6", features = ["json"], optional = true }
64+
xitca-web = { version = "0.7", features = ["json"], optional = true }
6665

6766
# raw-pg optional
68-
xitca-postgres = { version = "0.1", optional = true }
67+
xitca-postgres = { version = "0.2", optional = true }
6968

7069
# orm optional
71-
diesel = { version = "2", features = ["postgres", "r2d2"], optional = true }
70+
diesel = { version = "2", features = ["postgres"], optional = true }
71+
72+
# orm async optional
73+
diesel-async = { version = "0.5", features = ["bb8", "postgres"], optional = true }
74+
xitca-postgres-diesel = { version = "0.1", optional = true }
75+
futures-util = { version = "0.3", default-features = false, optional = true }
7276

7377
# template optional
74-
sailfish = { version = "0.9", default-features = false, features = ["derive", "perf-inline"], optional = true }
78+
sailfish = { version = "0.9", default-features = false, features = ["perf-inline"], optional = true }
7579

76-
# axum optional
77-
axum = { version = "0.7", optional = true, default-features = false, features = ["json", "query"] }
78-
http-body = { version = "1", optional = true }
79-
tower = { version = "0.4", optional = true }
80-
tower-http = { version = "0.5", features = ["set-header"], optional = true }
80+
# io-uring optional
81+
tokio-uring = { version = "0.5", optional = true }
8182

8283
# perf optional
8384
mimalloc = { version = "0.1", default-features = false, optional = true }
@@ -95,5 +96,13 @@ codegen-units = 1
9596
panic = "abort"
9697

9798
[patch.crates-io]
98-
xitca-postgres = { git = "https://github.com/HFQR/xitca-web.git", rev = "0cda225" }
99+
xitca-postgres-diesel = { git = "https://github.com/fakeshadow/xitca-postgres-diesel", rev = "ae93ee9" }
100+
101+
diesel-async = { git = "https://github.com/weiznich/diesel_async", rev = "5b8262b" }
99102
mio = { git = "https://github.com/fakeshadow/mio", rev = "9bae6012b7ecfc6083350785f71a5e8265358178" }
103+
104+
xitca-codegen = { git = "http://github.com/HFQR/xitca-web", rev = "d3066ba" }
105+
xitca-http = { git = "http://github.com/HFQR/xitca-web", rev = "d3066ba" }
106+
xitca-server = { git = "http://github.com/HFQR/xitca-web", rev = "d3066ba" }
107+
xitca-service = { git = "http://github.com/HFQR/xitca-web", rev = "d3066ba" }
108+
xitca-web = { git = "http://github.com/HFQR/xitca-web", rev = "d3066ba" }

frameworks/Rust/xitca-web/benchmark_config.json

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -35,14 +35,14 @@
3535
"approach": "Stripped",
3636
"classification": "Platform",
3737
"database": "Postgres",
38-
"framework": "xitca-web [unrealistic]",
38+
"framework": "xitca-web",
3939
"language": "Rust",
4040
"orm": "Raw",
4141
"platform": "None",
4242
"webserver": "xitca-server",
4343
"os": "Linux",
4444
"database_os": "Linux",
45-
"display_name": "xitca-web [unrealistic]",
45+
"display_name": "xitca-web [iou]",
4646
"notes": "",
4747
"versus": ""
4848
},
@@ -53,7 +53,7 @@
5353
"approach": "Realistic",
5454
"classification": "Micro",
5555
"database": "none",
56-
"framework": "xitca-web [wasm]",
56+
"framework": "xitca-web",
5757
"language": "rust",
5858
"orm": "raw",
5959
"platform": "none",
@@ -64,7 +64,7 @@
6464
"notes": "",
6565
"versus": ""
6666
},
67-
"axum": {
67+
"orm": {
6868
"json_url": "/json",
6969
"plaintext_url": "/plaintext",
7070
"db_url": "/db",
@@ -73,16 +73,16 @@
7373
"update_url": "/updates?q=",
7474
"port": 8080,
7575
"approach": "realistic",
76-
"classification": "micro",
76+
"classification": "fullstack",
7777
"database": "postgres",
78-
"framework": "axum [xitca]",
78+
"framework": "xitca-web",
7979
"language": "rust",
80-
"orm": "raw",
80+
"orm": "full",
8181
"platform": "none",
8282
"webserver": "xitca-server",
8383
"os": "linux",
8484
"database_os": "linux",
85-
"display_name": "axum [xitca]",
85+
"display_name": "xitca-web [orm]",
8686
"notes": "",
8787
"versus": ""
8888
},
@@ -97,7 +97,7 @@
9797
"approach": "realistic",
9898
"classification": "micro",
9999
"database": "postgres",
100-
"framework": "xitca-web [sync]",
100+
"framework": "xitca-web",
101101
"language": "rust",
102102
"orm": "full",
103103
"platform": "none",
Lines changed: 37 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -1,101 +1,63 @@
1-
// clippy is dumb and have no idea what should be lazy or not
2-
#![allow(clippy::unnecessary_lazy_evaluations)]
1+
#[path = "./db_util.rs"]
2+
mod db_util;
33

4-
use xitca_io::bytes::BytesMut;
5-
use xitca_postgres::{pipeline::Pipeline, pool::Pool, AsyncLendingIterator, Type};
4+
use std::cell::RefCell;
5+
6+
use xitca_postgres::{
7+
iter::AsyncLendingIterator, pipeline::Pipeline, pool::Pool, statement::Statement, Execute, ExecuteMut,
8+
};
69

710
use super::{
811
ser::{Fortune, Fortunes, World},
9-
util::{bulk_update_gen, HandleResult, Rand, DB_URL},
12+
util::{HandleResult, DB_URL},
1013
};
1114

15+
use db_util::{sort_update_params, update_query, Shared, FORTUNE_STMT, WORLD_STMT};
16+
1217
pub struct Client {
1318
pool: Pool,
14-
#[cfg(not(feature = "pg-sync"))]
15-
shared: std::cell::RefCell<Shared>,
16-
#[cfg(feature = "pg-sync")]
17-
shared: std::sync::Mutex<Shared>,
19+
shared: RefCell<Shared>,
1820
updates: Box<[Box<str>]>,
1921
}
2022

21-
type Shared = (Rand, BytesMut);
22-
23-
const FORTUNE_SQL: &str = "SELECT * FROM fortune";
24-
25-
const FORTUNE_SQL_TYPES: &[Type] = &[];
26-
27-
const WORLD_SQL: &str = "SELECT * FROM world WHERE id=$1";
28-
29-
const WORLD_SQL_TYPES: &[Type] = &[Type::INT4];
30-
31-
fn update_query(num: usize) -> Box<str> {
32-
bulk_update_gen(|query| {
33-
use std::fmt::Write;
34-
(1..=num).fold((1, query), |(idx, query), _| {
35-
write!(query, "(${}::int,${}::int),", idx, idx + 1).unwrap();
36-
(idx + 2, query)
37-
});
38-
})
39-
.into_boxed_str()
40-
}
41-
4223
pub async fn create() -> HandleResult<Client> {
43-
let pool = Pool::builder(DB_URL).capacity(1).build()?;
44-
45-
let shared = (Rand::default(), BytesMut::new());
46-
47-
let updates = core::iter::once(Box::from(""))
48-
.chain((1..=500).map(update_query))
49-
.collect();
50-
5124
Ok(Client {
52-
pool,
53-
#[cfg(not(feature = "pg-sync"))]
54-
shared: std::cell::RefCell::new(shared),
55-
#[cfg(feature = "pg-sync")]
56-
shared: std::sync::Mutex::new(shared),
57-
updates,
25+
pool: Pool::builder(DB_URL).capacity(1).build()?,
26+
shared: Default::default(),
27+
updates: core::iter::once(Box::from(""))
28+
.chain((1..=500).map(update_query))
29+
.collect(),
5830
})
5931
}
6032

6133
impl Client {
62-
#[cfg(not(feature = "pg-sync"))]
63-
fn shared(&self) -> std::cell::RefMut<'_, Shared> {
64-
self.shared.borrow_mut()
65-
}
66-
67-
#[cfg(feature = "pg-sync")]
68-
fn shared(&self) -> std::sync::MutexGuard<'_, Shared> {
69-
self.shared.lock().unwrap()
70-
}
71-
7234
pub async fn get_world(&self) -> HandleResult<World> {
7335
let mut conn = self.pool.get().await?;
74-
let stmt = conn.prepare(WORLD_SQL, WORLD_SQL_TYPES).await?;
75-
let id = self.shared().0.gen_id();
76-
let mut res = conn.consume().query_raw(&stmt, [id])?;
77-
let row = res.try_next().await?.ok_or_else(|| "World does not exist")?;
78-
Ok(World::new(row.get_raw(0), row.get_raw(1)))
36+
let stmt = WORLD_STMT.execute_mut(&mut conn).await?;
37+
let id = self.shared.borrow_mut().0.gen_id();
38+
let mut res = stmt.bind([id]).query(&conn.consume()).await?;
39+
let row = res.try_next().await?.ok_or("request World does not exist")?;
40+
Ok(World::new(row.get(0), row.get(1)))
7941
}
8042

8143
pub async fn get_worlds(&self, num: u16) -> HandleResult<Vec<World>> {
8244
let len = num as usize;
8345

8446
let mut conn = self.pool.get().await?;
85-
let stmt = conn.prepare(WORLD_SQL, WORLD_SQL_TYPES).await?;
47+
let stmt = WORLD_STMT.execute_mut(&mut conn).await?;
8648

8749
let mut res = {
88-
let (ref mut rng, ref mut buf) = *self.shared();
50+
let (ref mut rng, ref mut buf) = *self.shared.borrow_mut();
8951
let mut pipe = Pipeline::with_capacity_from_buf(len, buf);
90-
(0..num).try_for_each(|_| pipe.query_raw(&stmt, [rng.gen_id()]))?;
91-
conn.consume().pipeline(pipe)?
52+
(0..num).try_for_each(|_| stmt.bind([rng.gen_id()]).query_mut(&mut pipe))?;
53+
pipe.query(&conn.consume())?
9254
};
9355

9456
let mut worlds = Vec::with_capacity(len);
9557

9658
while let Some(mut item) = res.try_next().await? {
9759
while let Some(row) = item.try_next().await? {
98-
worlds.push(World::new(row.get_raw(0), row.get_raw(1)))
60+
worlds.push(World::new(row.get(0), row.get(1)))
9961
}
10062
}
10163

@@ -105,25 +67,24 @@ impl Client {
10567
pub async fn update(&self, num: u16) -> HandleResult<Vec<World>> {
10668
let len = num as usize;
10769

108-
let update = self.updates.get(len).ok_or_else(|| "num out of bound")?;
109-
70+
let update = self.updates.get(len).ok_or("request num is out of range")?;
11071
let mut conn = self.pool.get().await?;
111-
let world_stmt = conn.prepare(WORLD_SQL, WORLD_SQL_TYPES).await?;
112-
let update_stmt = conn.prepare(update, &[]).await?;
72+
let world_stmt = WORLD_STMT.execute_mut(&mut conn).await?;
73+
let update_stmt = Statement::named(update, &[]).execute_mut(&mut conn).await?;
11374

11475
let mut params = Vec::with_capacity(len);
11576

11677
let mut res = {
117-
let (ref mut rng, ref mut buf) = *self.shared();
78+
let (ref mut rng, ref mut buf) = *self.shared.borrow_mut();
11879
let mut pipe = Pipeline::with_capacity_from_buf(len + 1, buf);
11980
(0..num).try_for_each(|_| {
12081
let w_id = rng.gen_id();
12182
let r_id = rng.gen_id();
12283
params.push([w_id, r_id]);
123-
pipe.query_raw(&world_stmt, [w_id])
84+
world_stmt.bind([w_id]).query_mut(&mut pipe)
12485
})?;
125-
pipe.query_raw(&update_stmt, sort_update_params(&params))?;
126-
conn.consume().pipeline(pipe)?
86+
update_stmt.bind(sort_update_params(&params)).query_mut(&mut pipe)?;
87+
pipe.query(&conn.consume())?
12788
};
12889

12990
let mut worlds = Vec::with_capacity(len);
@@ -133,7 +94,7 @@ impl Client {
13394
while let Some(mut item) = res.try_next().await? {
13495
while let Some(row) = item.try_next().await? {
13596
let r_id = r_ids.next().unwrap()[1];
136-
worlds.push(World::new(row.get_raw(0), r_id))
97+
worlds.push(World::new(row.get(0), r_id))
13798
}
13899
}
139100

@@ -145,45 +106,15 @@ impl Client {
145106
items.push(Fortune::new(0, "Additional fortune added at request time."));
146107

147108
let mut conn = self.pool.get().await?;
148-
let stmt = conn.prepare(FORTUNE_SQL, FORTUNE_SQL_TYPES).await?;
149-
let mut res = conn.consume().query_raw::<[i32; 0]>(&stmt, [])?;
109+
let stmt = FORTUNE_STMT.execute_mut(&mut conn).await?;
110+
let mut res = stmt.query(&conn.consume()).await?;
150111

151112
while let Some(row) = res.try_next().await? {
152-
items.push(Fortune::new(row.get_raw(0), row.get_raw::<String>(1)));
113+
items.push(Fortune::new(row.get(0), row.get::<String>(1)));
153114
}
154115

155116
items.sort_by(|it, next| it.message.cmp(&next.message));
156117

157118
Ok(Fortunes::new(items))
158119
}
159120
}
160-
161-
fn sort_update_params(params: &[[i32; 2]]) -> impl ExactSizeIterator<Item = i32> {
162-
let mut params = params.to_owned();
163-
params.sort_by(|a, b| a[0].cmp(&b[0]));
164-
165-
struct ParamIter<I>(I);
166-
167-
impl<I> Iterator for ParamIter<I>
168-
where
169-
I: Iterator,
170-
{
171-
type Item = I::Item;
172-
173-
#[inline]
174-
fn next(&mut self) -> Option<Self::Item> {
175-
self.0.next()
176-
}
177-
178-
#[inline]
179-
fn size_hint(&self) -> (usize, Option<usize>) {
180-
self.0.size_hint()
181-
}
182-
}
183-
184-
// impl depends on compiler optimization to flat Vec<[T]> to Vec<T> when inferring
185-
// it's size hint. possible to cause runtime panic.
186-
impl<I> ExactSizeIterator for ParamIter<I> where I: Iterator {}
187-
188-
ParamIter(params.into_iter().flatten())
189-
}

0 commit comments

Comments
 (0)