Browse Source

Updatet may-minihttp(rust) (#6503)

Xudong Huang 4 years ago
parent
commit
df965ffc45

+ 2 - 6
frameworks/Rust/may-minihttp/Cargo.toml

@@ -5,19 +5,15 @@ authors = ["Xudong Huang <[email protected]>"]
 edition = "2018"
 
 [dependencies]
-markup = "0.4"
+may = "0.3"
 mimalloc = "0.1"
 num_cpus = "1.0"
 oorandom = "11"
 smallvec = "1.1"
-v_htmlescape = "0.10"
-serde = { version = "1.0", features = ["derive"] }
-serde_json = "1.0"
-may = "0.3"
+yarte = { version = "0.15", features = ["bytes-buf", "json"] }
 may_postgres = { git = "https://github.com/Xudong-Huang/may_postgres.git" }
 may_minihttp = { git = "https://github.com/Xudong-Huang/may_minihttp.git" }
 
 [profile.release]
 lto = true
 codegen-units = 1
-

+ 2 - 2
frameworks/Rust/may-minihttp/may-minihttp.dockerfile

@@ -1,6 +1,6 @@
-FROM rust:1.44
+FROM rust:1.51
 
-RUN apt-get update -yqq && apt-get install -yqq cmake
+RUN apt-get update -yqq && apt-get install -yqq cmake g++
 
 ADD ./ /may
 WORKDIR /may

+ 65 - 62
frameworks/Rust/may-minihttp/src/main.rs

@@ -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], &params)?;
         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",

+ 5 - 0
frameworks/Rust/may-minihttp/templates/fortune.hbs

@@ -0,0 +1,5 @@
+<!DOCTYPE html><html><head><title>Fortunes</title></head><body><table><tr><th>id</th><th>message</th></tr>
+      {{~# each fortunes ~}}
+      <tr><td>{{id}}</td><td>{{message}}</td></tr>
+      {{~/each ~}}
+</table></body></html>