Browse Source

[may_minihttp] refine tell fortunes code (#7778)

Xudong Huang 2 years ago
parent
commit
2ef0b6ce03
2 changed files with 50 additions and 41 deletions
  1. 10 4
      frameworks/Rust/may-minihttp/Cargo.toml
  2. 40 37
      frameworks/Rust/may-minihttp/src/main.rs

+ 10 - 4
frameworks/Rust/may-minihttp/Cargo.toml

@@ -2,16 +2,22 @@
 name = "may-minihttp"
 version = "0.1.0"
 authors = ["Xudong Huang <[email protected]>"]
-edition = "2018"
+edition = "2021"
 
 [dependencies]
-may = "0.3"
-mimalloc = "0.1"
+atoi = "2"
+bytes = "1"
 num_cpus = "1.0"
-oorandom = "11"
 smallvec = "1.1"
+
+log = { version = "0.4", features = ["release_max_level_off"] }
+mimalloc = { version = "0.1", default-features = false }
+nanorand = { version = "0.7", default-features = false, features = ["std", "wyrand"] }
+
 buf-min = { version = "0.7", features = ["bytes"] }
 yarte = { version = "0.15", features = ["bytes-buf", "json"] }
+
+may = { version = "0.3.25", default-features = false }
 may_postgres = { git = "https://github.com/Xudong-Huang/may_postgres.git" }
 may_minihttp = { git = "https://github.com/Xudong-Huang/may_minihttp.git" }
 

+ 40 - 37
frameworks/Rust/may-minihttp/src/main.rs

@@ -1,24 +1,25 @@
 #[global_allocator]
 static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc;
 
-use std::borrow::Cow;
 use std::fmt::Write;
 use std::io;
 use std::sync::atomic::{AtomicUsize, Ordering};
 use std::sync::Arc;
 
+use bytes::BytesMut;
 use may_minihttp::{HttpService, HttpServiceFactory, Request, Response};
 use may_postgres::{self, types::ToSql, Client, Statement};
-use oorandom::Rand32;
+use nanorand::{Rng, WyRand};
 use smallvec::SmallVec;
 use yarte::{ywrite_html, Serialize};
 
 mod utils {
+    use atoi::FromRadix10;
     use may_postgres::types::ToSql;
 
     pub fn get_query_param(query: &str) -> u16 {
         let q = if let Some(pos) = query.find("?q") {
-            query.split_at(pos + 3).1.parse::<u16>().ok().unwrap_or(1)
+            u16::from_radix_10(query.split_at(pos + 3).1.as_ref()).0
         } else {
             1
         };
@@ -33,7 +34,7 @@ mod utils {
 }
 
 #[derive(Serialize)]
-struct HeloMessage {
+struct HelloMessage {
     message: &'static str,
 }
 
@@ -44,9 +45,9 @@ struct WorldRow {
 }
 
 #[derive(Serialize)]
-pub struct Fortune {
+pub struct Fortune<'a> {
     id: i32,
-    message: Cow<'static, str>,
+    message: &'a str,
 }
 
 struct PgConnectionPool {
@@ -85,9 +86,11 @@ struct PgConnection {
 impl PgConnection {
     fn new(db_url: &str) -> Self {
         let client = may_postgres::connect(db_url).unwrap();
-        let world = client.prepare("SELECT * FROM world WHERE id=$1").unwrap();
+        let world = client
+            .prepare("SELECT id, randomnumber FROM world WHERE id=$1")
+            .unwrap();
 
-        let fortune = client.prepare("SELECT * FROM fortune").unwrap();
+        let fortune = client.prepare("SELECT id, message FROM fortune").unwrap();
 
         let mut updates = Vec::new();
         for num in 1..=500u16 {
@@ -125,18 +128,18 @@ impl PgConnection {
                 id: row.get(0),
                 randomnumber: row.get(1),
             }),
-            None => unreachable!(),
+            None => unreachable!("random_id={}", random_id),
         }
     }
 
     fn get_worlds(
         &self,
         num: usize,
-        rand: &mut Rand32,
+        rand: &mut WyRand,
     ) -> Result<SmallVec<[WorldRow; 32]>, may_postgres::Error> {
         let mut queries = SmallVec::<[_; 32]>::new();
         for _ in 0..num {
-            let random_id = (rand.rand_u32() % 10_000 + 1) as i32;
+            let random_id = (rand.generate::<u32>() % 10_000 + 1) as i32;
             queries.push(
                 self.client
                     .query_raw(&self.world, utils::slice_iter(&[&random_id]))?,
@@ -159,11 +162,11 @@ impl PgConnection {
     fn updates(
         &self,
         num: usize,
-        rand: &mut Rand32,
+        rand: &mut WyRand,
     ) -> Result<SmallVec<[WorldRow; 32]>, may_postgres::Error> {
         let mut queries = SmallVec::<[_; 32]>::new();
         for _ in 0..num {
-            let random_id = (rand.rand_u32() % 10_000 + 1) as i32;
+            let random_id = (rand.generate::<u32>() % 10_000 + 1) as i32;
             queries.push(
                 self.client
                     .query_raw(&self.world, utils::slice_iter(&[&random_id]))?,
@@ -172,7 +175,7 @@ impl PgConnection {
 
         let mut worlds = SmallVec::<[_; 32]>::new();
         for mut q in queries {
-            let new_random_num = (rand.rand_u32() % 10_000 + 1) as i32;
+            let new_random_num = (rand.generate::<u32>() % 10_000 + 1) as i32;
             match q.next().transpose()? {
                 Some(row) => worlds.push(WorldRow {
                     id: row.get(0),
@@ -195,32 +198,35 @@ impl PgConnection {
         Ok(worlds)
     }
 
-    fn tell_fortune(&self) -> Result<Vec<Fortune>, may_postgres::Error> {
-        let mut items = vec![Fortune {
-            id: 0,
-            message: Cow::Borrowed("Additional fortune added at request time."),
-        }];
-
+    fn tell_fortune(&self, buf: &mut BytesMut) -> Result<(), may_postgres::Error> {
         let rows = self
             .client
             .query_raw(&self.fortune, utils::slice_iter(&[]))?;
 
-        for row in rows {
-            let r = row?;
-            items.push(Fortune {
+        let all_rows = rows.map(|r| r.unwrap()).collect::<Vec<_>>();
+        let mut fortunes = all_rows
+            .iter()
+            .map(|r| Fortune {
                 id: r.get(0),
-                message: Cow::Owned(r.get(1)),
-            });
-        }
+                message: r.get(1),
+            })
+            .collect::<Vec<_>>();
+        fortunes.push(Fortune {
+            id: 0,
+            message: "Additional fortune added at request time.",
+        });
+        fortunes.sort_by(|it, next| it.message.cmp(next.message));
 
-        items.sort_by(|it, next| it.message.cmp(&next.message));
-        Ok(items)
+        let mut body = std::mem::replace(buf, BytesMut::new());
+        ywrite_html!(body, "{{> fortune }}");
+        let _ = std::mem::replace(buf, body);
+        Ok(())
     }
 }
 
 struct Techempower {
     db: Arc<PgConnection>,
-    rng: Rand32,
+    rng: WyRand,
 }
 
 impl HttpService for Techempower {
@@ -229,7 +235,7 @@ impl HttpService for Techempower {
         match req.path() {
             "/json" => {
                 rsp.header("Content-Type: application/json");
-                let msg = HeloMessage {
+                let msg = HelloMessage {
                     message: "Hello, World!",
                 };
                 msg.to_bytes_mut(rsp.body_mut());
@@ -239,16 +245,13 @@ impl HttpService for Techempower {
             }
             "/db" => {
                 rsp.header("Content-Type: application/json");
-                let random_id = (self.rng.rand_u32() % 10_000 + 1) as i32;
+                let random_id = (self.rng.generate::<u32>() % 10_000 + 1) as i32;
                 let world = self.db.get_world(random_id).unwrap();
                 world.to_bytes_mut(rsp.body_mut())
             }
             "/fortunes" => {
                 rsp.header("Content-Type: text/html; charset=utf-8");
-                let fortunes = self.db.tell_fortune().unwrap();
-                let mut body = Vec::with_capacity(2048);
-                ywrite_html!(body, "{{> fortune }}");
-                rsp.body_vec(body);
+                self.db.tell_fortune(rsp.body_mut()).unwrap();
             }
             p if p.starts_with("/queries") => {
                 rsp.header("Content-Type: application/json");
@@ -279,8 +282,8 @@ impl HttpServiceFactory for HttpServer {
     type Service = Techempower;
 
     fn new_service(&self) -> Self::Service {
-        let (db, idx) = self.db_pool.get_connection();
-        let rng = Rand32::new(idx as u64);
+        let (db, _idx) = self.db_pool.get_connection();
+        let rng = WyRand::new();
         Techempower { db, rng }
     }
 }