|
@@ -1,10 +1,10 @@
|
|
|
-use std::convert::identity;
|
|
|
+use std::{borrow::Cow, convert::identity};
|
|
|
|
|
|
use nanorand::{Rng, WyRand};
|
|
|
-use sqlx::Arguments;
|
|
|
-use stretto::AsyncCache;
|
|
|
+use once_cell::sync::OnceCell;
|
|
|
+use sqlx::{postgres::PgRow, Arguments, Pool, Row};
|
|
|
use viz::{
|
|
|
- get, header::SERVER, types::State, BytesMut, Error, IntoHandler, Response,
|
|
|
+ header::SERVER, types::State, BytesMut, Error, Request, RequestExt, Response,
|
|
|
ResponseExt, Result, Router, ServiceMaker,
|
|
|
};
|
|
|
|
|
@@ -13,37 +13,49 @@ mod models_sqlx;
|
|
|
mod server;
|
|
|
mod utils;
|
|
|
|
|
|
-use db_sqlx::{Counter, DatabaseConnection, PgArguments, PgError, PgPoolOptions};
|
|
|
+use db_sqlx::{
|
|
|
+ Counter, DatabaseConnection, PgArguments, PgError, PgPoolOptions, Postgres,
|
|
|
+};
|
|
|
use models_sqlx::{Fortune, World};
|
|
|
+use utils::{HDR_SERVER, RANGE};
|
|
|
+
|
|
|
+const DB_URL: &str =
|
|
|
+ "postgres://benchmarkdbuser:benchmarkdbpass@tfb-database/hello_world";
|
|
|
+static CACHED: OnceCell<Vec<World>> = OnceCell::new();
|
|
|
+
|
|
|
+async fn db(mut req: Request) -> Result<Response> {
|
|
|
+ let (State(mut rng), DatabaseConnection(mut conn)) =
|
|
|
+ req.extract::<(State<WyRand>, DatabaseConnection)>().await?;
|
|
|
|
|
|
-async fn db(
|
|
|
- State(mut rng): State<WyRand>,
|
|
|
- DatabaseConnection(mut conn): DatabaseConnection,
|
|
|
-) -> Result<Response> {
|
|
|
- let random_id = (rng.generate::<u32>() % 10_000 + 1) as i32;
|
|
|
- let mut args = PgArguments::default();
|
|
|
- args.add(random_id);
|
|
|
+ let random_id = rng.generate_range(RANGE);
|
|
|
|
|
|
let world: World =
|
|
|
- sqlx::query_as_with("SELECT id, randomnumber FROM World WHERE id = $1", args)
|
|
|
+ sqlx::query_as("SELECT id, randomnumber FROM World WHERE id = $1")
|
|
|
+ .bind(random_id)
|
|
|
.fetch_one(&mut conn)
|
|
|
.await
|
|
|
.map_err(PgError)?;
|
|
|
|
|
|
let mut res = Response::json(world)?;
|
|
|
- res.headers_mut().insert(SERVER, utils::HDR_SERVER);
|
|
|
+ res.headers_mut().insert(SERVER, HDR_SERVER);
|
|
|
Ok(res)
|
|
|
}
|
|
|
|
|
|
-async fn fortunes(DatabaseConnection(mut conn): DatabaseConnection) -> Result<Response> {
|
|
|
- let mut items: Vec<Fortune> = sqlx::query_as("SELECT * FROM Fortune")
|
|
|
+async fn fortunes(mut req: Request) -> Result<Response> {
|
|
|
+ let DatabaseConnection(mut conn) = req.extract::<DatabaseConnection>().await?;
|
|
|
+
|
|
|
+ let mut items: Vec<Fortune> = sqlx::query("SELECT * FROM Fortune")
|
|
|
+ .map(|row: PgRow| Fortune {
|
|
|
+ id: row.get(0),
|
|
|
+ message: Cow::Owned(row.get(1)),
|
|
|
+ })
|
|
|
.fetch_all(&mut conn)
|
|
|
.await
|
|
|
.map_err(PgError)?;
|
|
|
|
|
|
items.push(Fortune {
|
|
|
id: 0,
|
|
|
- message: "Additional fortune added at request time.".to_string(),
|
|
|
+ message: Cow::Borrowed("Additional fortune added at request time."),
|
|
|
});
|
|
|
|
|
|
items.sort_by(|it, next| it.message.cmp(&next.message));
|
|
@@ -52,83 +64,74 @@ async fn fortunes(DatabaseConnection(mut conn): DatabaseConnection) -> Result<Re
|
|
|
buf.extend(FortunesTemplate { items }.to_string().as_bytes());
|
|
|
|
|
|
let mut res = Response::html(buf.freeze());
|
|
|
- res.headers_mut().insert(SERVER, utils::HDR_SERVER);
|
|
|
+ res.headers_mut().insert(SERVER, HDR_SERVER);
|
|
|
Ok(res)
|
|
|
}
|
|
|
|
|
|
-async fn queries(
|
|
|
- Counter(count): Counter,
|
|
|
- State(mut rng): State<WyRand>,
|
|
|
- DatabaseConnection(mut conn): DatabaseConnection,
|
|
|
-) -> Result<Response> {
|
|
|
+async fn queries(mut req: Request) -> Result<Response> {
|
|
|
+ let (Counter(count), State(mut rng), DatabaseConnection(mut conn)) = req
|
|
|
+ .extract::<(Counter, State<WyRand>, DatabaseConnection)>()
|
|
|
+ .await?;
|
|
|
+
|
|
|
let mut worlds = Vec::with_capacity(count as usize);
|
|
|
|
|
|
for _ in 0..count {
|
|
|
- let id = (rng.generate::<u32>() % 10_000 + 1) as i32;
|
|
|
-
|
|
|
- let mut args = PgArguments::default();
|
|
|
- args.add(id);
|
|
|
+ let id = rng.generate_range(RANGE);
|
|
|
|
|
|
- let world = sqlx::query_as_with::<_, World, _>(
|
|
|
- "SELECT id, randomnumber FROM World WHERE id = $1",
|
|
|
- args,
|
|
|
- )
|
|
|
- .fetch_one(&mut conn)
|
|
|
- .await
|
|
|
- .map_err(PgError)?;
|
|
|
+ let world: World =
|
|
|
+ sqlx::query_as("SELECT id, randomnumber FROM World WHERE id = $1")
|
|
|
+ .bind(id)
|
|
|
+ .fetch_one(&mut conn)
|
|
|
+ .await
|
|
|
+ .map_err(PgError)?;
|
|
|
|
|
|
worlds.push(world);
|
|
|
}
|
|
|
|
|
|
let mut res = Response::json(worlds)?;
|
|
|
- res.headers_mut().insert(SERVER, utils::HDR_SERVER);
|
|
|
+ res.headers_mut().insert(SERVER, HDR_SERVER);
|
|
|
Ok(res)
|
|
|
}
|
|
|
|
|
|
-async fn cached_queries(
|
|
|
- Counter(count): Counter,
|
|
|
- State(mut rng): State<WyRand>,
|
|
|
- State(cached): State<AsyncCache<i32, World>>,
|
|
|
-) -> Result<Response> {
|
|
|
+async fn cached_queries(mut req: Request) -> Result<Response> {
|
|
|
+ let (Counter(count), State(mut rng)) =
|
|
|
+ req.extract::<(Counter, State<WyRand>)>().await?;
|
|
|
+
|
|
|
let worlds = (0..count)
|
|
|
.map(|_| {
|
|
|
- let id = (rng.generate::<u32>() % 10_000 + 1) as i32;
|
|
|
- cached.get(&id).map(|v| v.read())
|
|
|
+ let id = rng.generate_range(RANGE) as usize;
|
|
|
+ CACHED.get()?.get(id)
|
|
|
})
|
|
|
.filter_map(identity)
|
|
|
.collect::<Vec<_>>();
|
|
|
|
|
|
let mut res = Response::json(worlds)?;
|
|
|
- res.headers_mut().insert(SERVER, utils::HDR_SERVER);
|
|
|
+ res.headers_mut().insert(SERVER, HDR_SERVER);
|
|
|
Ok(res)
|
|
|
}
|
|
|
|
|
|
-async fn updates(
|
|
|
- Counter(count): Counter,
|
|
|
- State(mut rng): State<WyRand>,
|
|
|
- DatabaseConnection(mut conn): DatabaseConnection,
|
|
|
-) -> Result<Response> {
|
|
|
+async fn updates(mut req: Request) -> Result<Response> {
|
|
|
+ let (Counter(count), State(mut rng), DatabaseConnection(mut conn)) = req
|
|
|
+ .extract::<(Counter, State<WyRand>, DatabaseConnection)>()
|
|
|
+ .await?;
|
|
|
+
|
|
|
let mut worlds = Vec::with_capacity(count as usize);
|
|
|
|
|
|
for _ in 0..count {
|
|
|
- let id = (rng.generate::<u32>() % 10_000 + 1) as i32;
|
|
|
-
|
|
|
- let mut args = PgArguments::default();
|
|
|
- args.add(id);
|
|
|
+ let id = rng.generate_range(RANGE);
|
|
|
|
|
|
- let world = sqlx::query_as_with::<_, World, _>(
|
|
|
- "SELECT id, randomnumber FROM World WHERE id = $1",
|
|
|
- args,
|
|
|
- )
|
|
|
- .fetch_one(&mut conn)
|
|
|
- .await
|
|
|
- .map_err(PgError)?;
|
|
|
+ let world: World =
|
|
|
+ sqlx::query_as("SELECT id, randomnumber FROM World WHERE id = $1")
|
|
|
+ .bind(id)
|
|
|
+ .fetch_one(&mut conn)
|
|
|
+ .await
|
|
|
+ .map_err(PgError)?;
|
|
|
|
|
|
worlds.push(world);
|
|
|
}
|
|
|
|
|
|
for w in &mut worlds {
|
|
|
- let randomnumber = (rng.generate::<u32>() % 10_000 + 1) as i32;
|
|
|
+ let randomnumber = rng.generate_range(RANGE);
|
|
|
let mut args = PgArguments::default();
|
|
|
args.add(randomnumber);
|
|
|
args.add(w.id);
|
|
@@ -145,11 +148,20 @@ async fn updates(
|
|
|
Ok(res)
|
|
|
}
|
|
|
|
|
|
+async fn populate_cache(pool: Pool<Postgres>) -> Result<(), Error> {
|
|
|
+ let mut conn = pool.acquire().await.map_err(Error::normal)?;
|
|
|
+
|
|
|
+ let worlds: Vec<World> = sqlx::query_as("SELECT * FROM World LIMIT $1")
|
|
|
+ .bind(10_000)
|
|
|
+ .fetch_all(&mut conn)
|
|
|
+ .await
|
|
|
+ .map_err(PgError)?;
|
|
|
+ CACHED.set(worlds).unwrap();
|
|
|
+ Ok(())
|
|
|
+}
|
|
|
+
|
|
|
#[tokio::main]
|
|
|
async fn main() -> Result<()> {
|
|
|
- const DB_URL: &str =
|
|
|
- "postgres://benchmarkdbuser:benchmarkdbpass@tfb-database/hello_world";
|
|
|
-
|
|
|
let pool = PgPoolOptions::new()
|
|
|
.max_connections(56)
|
|
|
.min_connections(56)
|
|
@@ -157,36 +169,17 @@ async fn main() -> Result<()> {
|
|
|
.await
|
|
|
.map_err(PgError)?;
|
|
|
|
|
|
- let rng = WyRand::new();
|
|
|
-
|
|
|
- let cached = AsyncCache::new(10_000, 1e6 as i64, tokio::spawn).unwrap();
|
|
|
-
|
|
|
- {
|
|
|
- let mut conn = pool.acquire().await.map_err(PgError)?;
|
|
|
- let mut args = PgArguments::default();
|
|
|
- args.add(10_000);
|
|
|
- let worlds: Vec<World> =
|
|
|
- sqlx::query_as_with("SELECT id, randomnumber FROM World LIMIT $1", args)
|
|
|
- .fetch_all(&mut conn)
|
|
|
- .await
|
|
|
- .map_err(PgError)?;
|
|
|
+ populate_cache(pool.clone()).await?;
|
|
|
|
|
|
- for w in worlds {
|
|
|
- cached.insert(w.id, w, 1).await;
|
|
|
- }
|
|
|
- cached.wait().await.expect("cache insert failed");
|
|
|
- }
|
|
|
+ let rng = WyRand::new();
|
|
|
|
|
|
let app = Router::new()
|
|
|
- .route("/db", get(db.into_handler()))
|
|
|
- .route("/fortunes", get(fortunes.into_handler()))
|
|
|
- .route("/queries", get(queries.into_handler()))
|
|
|
- .route("/updates", get(updates.into_handler()))
|
|
|
+ .get("/db", db)
|
|
|
+ .get("/fortunes", fortunes)
|
|
|
+ .get("/queries", queries)
|
|
|
+ .get("/updates", updates)
|
|
|
.with(State::new(pool))
|
|
|
- .route(
|
|
|
- "/cached_queries",
|
|
|
- get(cached_queries.into_handler()).with(State::new(cached)),
|
|
|
- )
|
|
|
+ .get("/cached_queries", cached_queries)
|
|
|
.with(State::new(rng));
|
|
|
|
|
|
server::builder()
|