|
@@ -6,11 +6,11 @@ use std::io;
|
|
|
use std::sync::atomic::{AtomicUsize, Ordering};
|
|
|
use std::sync::Arc;
|
|
|
|
|
|
-use may_minihttp::{BodyWriter, HttpService, HttpServiceFactory, Request, Response};
|
|
|
-use may_postgres::{self, Client, RowStream, Statement};
|
|
|
+use may_minihttp::{HttpService, HttpServiceFactory, Request, Response};
|
|
|
+use may_postgres::{self, types::ToSql, Client, Statement};
|
|
|
use oorandom::Rand32;
|
|
|
-use serde::Serialize;
|
|
|
use smallvec::SmallVec;
|
|
|
+use yarte::{ywrite_html, Serialize};
|
|
|
|
|
|
mod utils {
|
|
|
use may_postgres::types::ToSql;
|
|
@@ -49,28 +49,6 @@ pub struct Fortune {
|
|
|
message: String,
|
|
|
}
|
|
|
|
|
|
-markup::define! {
|
|
|
- FortunesTemplate(fortunes: Vec<Fortune>) {
|
|
|
- {markup::doctype()}
|
|
|
- html {
|
|
|
- head {
|
|
|
- title { "Fortunes" }
|
|
|
- }
|
|
|
- body {
|
|
|
- table {
|
|
|
- tr { th { "id" } th { "message" } }
|
|
|
- @for item in {fortunes} {
|
|
|
- tr {
|
|
|
- td { {item.id} }
|
|
|
- td { {markup::raw(v_htmlescape::escape(&item.message))} }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
struct PgConnectionPool {
|
|
|
idx: AtomicUsize,
|
|
|
clients: Vec<Arc<PgConnection>>,
|
|
@@ -101,6 +79,7 @@ struct PgConnection {
|
|
|
client: Client,
|
|
|
world: Statement,
|
|
|
fortune: Statement,
|
|
|
+ updates: Vec<Statement>,
|
|
|
}
|
|
|
|
|
|
impl PgConnection {
|
|
@@ -112,10 +91,30 @@ impl PgConnection {
|
|
|
|
|
|
let fortune = client.prepare("SELECT id, message FROM fortune").unwrap();
|
|
|
|
|
|
+ let mut updates = Vec::new();
|
|
|
+ for num in 1..=500u16 {
|
|
|
+ let mut pl: u16 = 1;
|
|
|
+ let mut q = String::new();
|
|
|
+ q.push_str("UPDATE world SET randomnumber = CASE id ");
|
|
|
+ for _ in 1..=num {
|
|
|
+ let _ = write!(&mut q, "when ${} then ${} ", pl, pl + 1);
|
|
|
+ pl += 2;
|
|
|
+ }
|
|
|
+ q.push_str("ELSE randomnumber END WHERE id IN (");
|
|
|
+ for _ in 1..=num {
|
|
|
+ let _ = write!(&mut q, "${},", pl);
|
|
|
+ pl += 1;
|
|
|
+ }
|
|
|
+ q.pop();
|
|
|
+ q.push(')');
|
|
|
+ updates.push(client.prepare(&q).unwrap());
|
|
|
+ }
|
|
|
+
|
|
|
PgConnection {
|
|
|
client,
|
|
|
world,
|
|
|
fortune,
|
|
|
+ updates,
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -136,17 +135,17 @@ impl PgConnection {
|
|
|
&self,
|
|
|
num: usize,
|
|
|
rand: &mut Rand32,
|
|
|
- ) -> Result<Vec<WorldRow>, may_postgres::Error> {
|
|
|
- let mut queries = SmallVec::<[RowStream; 32]>::new();
|
|
|
+ ) -> Result<SmallVec<[WorldRow; 32]>, may_postgres::Error> {
|
|
|
+ let mut queries = SmallVec::<[_; 32]>::new();
|
|
|
for _ in 0..num {
|
|
|
- let random_id = rand.rand_range(1..10001) as i32;
|
|
|
+ let random_id = (rand.rand_u32() % 10_000 + 1) as i32;
|
|
|
queries.push(
|
|
|
self.client
|
|
|
.query_raw(&self.world, utils::slice_iter(&[&random_id]))?,
|
|
|
);
|
|
|
}
|
|
|
|
|
|
- let mut worlds = Vec::with_capacity(num);
|
|
|
+ let mut worlds = SmallVec::<[_; 32]>::new();
|
|
|
for mut q in queries {
|
|
|
match q.next().transpose()? {
|
|
|
Some(row) => worlds.push(WorldRow {
|
|
@@ -159,47 +158,50 @@ impl PgConnection {
|
|
|
Ok(worlds)
|
|
|
}
|
|
|
|
|
|
- fn updates(&self, num: usize, rand: &mut Rand32) -> Result<Vec<WorldRow>, may_postgres::Error> {
|
|
|
- let mut queries = SmallVec::<[RowStream; 32]>::new();
|
|
|
+ fn updates(
|
|
|
+ &self,
|
|
|
+ num: usize,
|
|
|
+ rand: &mut Rand32,
|
|
|
+ ) -> Result<SmallVec<[WorldRow; 32]>, may_postgres::Error> {
|
|
|
+ let mut queries = SmallVec::<[_; 32]>::new();
|
|
|
for _ in 0..num {
|
|
|
- let random_id = rand.rand_range(1..10001) as i32;
|
|
|
+ let random_id = (rand.rand_u32() % 10_000 + 1) as i32;
|
|
|
queries.push(
|
|
|
self.client
|
|
|
.query_raw(&self.world, utils::slice_iter(&[&random_id]))?,
|
|
|
);
|
|
|
}
|
|
|
|
|
|
- let mut worlds = Vec::with_capacity(num);
|
|
|
+ let mut worlds = SmallVec::<[_; 32]>::new();
|
|
|
for mut q in queries {
|
|
|
+ let new_random_num = (rand.rand_u32() % 10_000 + 1) as i32;
|
|
|
match q.next().transpose()? {
|
|
|
Some(row) => worlds.push(WorldRow {
|
|
|
id: row.get(0),
|
|
|
- randomnumber: row.get(1),
|
|
|
+ randomnumber: new_random_num,
|
|
|
}),
|
|
|
None => unreachable!(),
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- let mut update = String::with_capacity(120 + 12 * num);
|
|
|
- update.push_str("UPDATE world SET randomnumber = temp.randomnumber FROM (VALUES ");
|
|
|
-
|
|
|
- for w in &mut worlds {
|
|
|
- w.randomnumber = rand.rand_range(1..10001) as i32;
|
|
|
- let _ = write!(&mut update, "({}, {}),", w.id, w.randomnumber);
|
|
|
+ let mut params: Vec<&(dyn ToSql + Sync)> = Vec::with_capacity(num * 3);
|
|
|
+ for w in &worlds {
|
|
|
+ params.push(&w.id);
|
|
|
+ params.push(&w.randomnumber);
|
|
|
+ }
|
|
|
+ for w in &worlds {
|
|
|
+ params.push(&w.id);
|
|
|
}
|
|
|
- update.pop();
|
|
|
- update.push_str(" ORDER BY 1) AS temp(id, randomnumber) WHERE temp.id = world.id");
|
|
|
|
|
|
- self.client.simple_query(&update)?;
|
|
|
+ self.client.query(&self.updates[num - 1], ¶ms)?;
|
|
|
Ok(worlds)
|
|
|
}
|
|
|
|
|
|
- fn tell_fortune(&self) -> Result<Vec<Fortune>, may_postgres::Error> {
|
|
|
- let mut items = Vec::with_capacity(80);
|
|
|
- items.push(Fortune {
|
|
|
+ fn tell_fortune(&self) -> Result<SmallVec<[Fortune; 32]>, may_postgres::Error> {
|
|
|
+ let mut items: SmallVec<[_; 32]> = smallvec::smallvec![Fortune {
|
|
|
id: 0,
|
|
|
message: "Additional fortune added at request time.".to_string(),
|
|
|
- });
|
|
|
+ }];
|
|
|
|
|
|
let rows = self
|
|
|
.client
|
|
@@ -229,38 +231,38 @@ impl HttpService for Techempower {
|
|
|
match req.path() {
|
|
|
"/json" => {
|
|
|
rsp.header("Content-Type: application/json");
|
|
|
- serde_json::to_writer(
|
|
|
- BodyWriter(rsp.body_mut()),
|
|
|
- &HeloMessage {
|
|
|
- message: "Hello, World!",
|
|
|
- },
|
|
|
- )?;
|
|
|
+ let msg = HeloMessage {
|
|
|
+ message: "Hello, World!",
|
|
|
+ };
|
|
|
+ msg.to_bytes_mut(rsp.body_mut());
|
|
|
}
|
|
|
"/plaintext" => {
|
|
|
rsp.header("Content-Type: text/plain").body("Hello, World!");
|
|
|
}
|
|
|
"/db" => {
|
|
|
- let random_id = self.rng.rand_range(1..10001) as i32;
|
|
|
- let world = self.db.get_world(random_id).unwrap();
|
|
|
rsp.header("Content-Type: application/json");
|
|
|
- serde_json::to_writer(BodyWriter(rsp.body_mut()), &world)?;
|
|
|
+ let random_id = (self.rng.rand_u32() % 10_000 + 1) as i32;
|
|
|
+ let world = self.db.get_world(random_id).unwrap();
|
|
|
+ world.to_bytes_mut(rsp.body_mut())
|
|
|
}
|
|
|
"/fortunes" => {
|
|
|
- let fortunes = self.db.tell_fortune().unwrap();
|
|
|
rsp.header("Content-Type: text/html; charset=utf-8");
|
|
|
- write!(rsp.body_mut(), "{}", FortunesTemplate { fortunes }).unwrap();
|
|
|
+ let fortunes = self.db.tell_fortune().unwrap();
|
|
|
+ let mut body = Vec::with_capacity(2048);
|
|
|
+ ywrite_html!(body, "{{> fortune }}");
|
|
|
+ rsp.body_mut().extend_from_slice(&body);
|
|
|
}
|
|
|
p if p.starts_with("/queries") => {
|
|
|
+ rsp.header("Content-Type: application/json");
|
|
|
let q = utils::get_query_param(p) as usize;
|
|
|
let worlds = self.db.get_worlds(q, &mut self.rng).unwrap();
|
|
|
- rsp.header("Content-Type: application/json");
|
|
|
- serde_json::to_writer(BodyWriter(rsp.body_mut()), &worlds)?;
|
|
|
+ worlds.to_bytes_mut(rsp.body_mut());
|
|
|
}
|
|
|
p if p.starts_with("/updates") => {
|
|
|
+ rsp.header("Content-Type: application/json");
|
|
|
let q = utils::get_query_param(p) as usize;
|
|
|
let worlds = self.db.updates(q, &mut self.rng).unwrap();
|
|
|
- rsp.header("Content-Type: application/json");
|
|
|
- serde_json::to_writer(BodyWriter(rsp.body_mut()), &worlds)?;
|
|
|
+ worlds.to_bytes_mut(rsp.body_mut());
|
|
|
}
|
|
|
_ => {
|
|
|
rsp.status_code("404", "Not Found");
|
|
@@ -289,6 +291,7 @@ fn main() {
|
|
|
may::config()
|
|
|
.set_pool_capacity(10000)
|
|
|
.set_stack_size(0x1000);
|
|
|
+ println!("Starting http server: 127.0.0.1:8080");
|
|
|
let server = HttpServer {
|
|
|
db_pool: PgConnectionPool::new(
|
|
|
"postgres://benchmarkdbuser:benchmarkdbpass@tfb-database/hello_world",
|