Browse Source

Rust Iron updates (#2405)

* add the rest of the benchmarks

* update configs in dev vm

* ensured that the endpoints were listed in the config

* DBHOST + Deadlock prevention + minor updates
Scott M. M. Jackson 8 years ago
parent
commit
3bd61a66f7

+ 17 - 5
frameworks/Rust/iron/Cargo.toml

@@ -1,9 +1,21 @@
 [package]
-
 name = "iron"
-version = "0.0.1"
+version = "0.0.2"
+build = "build.rs"
+
+[build-dependencies]
+serde_codegen = "0.8"
 
 [dependencies]
-rustc-serialize = "0.3.19"
-iron = "0.3.0"
-router = "0.1.1"
+serde = "0.8.19"
+serde_json = "0.8.3"
+iron = "0.4.0"
+router = "0.4.0"
+persistent = "0.2.1"
+hyper = "0.9.13"
+rand = "0.3"
+postgres = "0.13.3"
+r2d2 = "0.7.1"
+r2d2_postgres = "0.11.0"
+mustache = "0.8.0"
+rustc-serialize = "0.3"

+ 5 - 1
frameworks/Rust/iron/benchmark_config.json

@@ -4,11 +4,15 @@
     "default": {
       "setup_file": "setup",
       "json_url": "/json",
+      "db_url": "/db",
+      "fortune_url": "/fortune",
+      "query_url": "/queries?queries=",
+      "update_url": "/updates?queries=",
       "plaintext_url": "/plaintext",
       "port": 8080,
       "approach": "Realistic",
       "classification": "Micro",
-      "database": "None",
+      "database": "Postgres",
       "framework": "iron",
       "language": "rust",
       "orm": "raw",

+ 13 - 0
frameworks/Rust/iron/build.rs

@@ -0,0 +1,13 @@
+extern crate serde_codegen;
+
+use std::env;
+use std::path::Path;
+
+fn main() {
+    let out_dir = env::var_os("OUT_DIR").unwrap();
+
+    let src = Path::new("src/main_types.in.rs");
+    let dst = Path::new(&out_dir).join("main_types.rs");
+
+    serde_codegen::expand(&src, &dst).unwrap();
+}

+ 216 - 20
frameworks/Rust/iron/setup.sh

@@ -1,36 +1,232 @@
 extern crate iron;
-extern crate router;
+extern crate persistent;
+#[macro_use] extern crate router;
+extern crate serde;
+extern crate serde_json;
+extern crate hyper;
+extern crate rand;
+extern crate r2d2;
+extern crate postgres;
+extern crate r2d2_postgres;
+extern crate mustache;
 extern crate rustc_serialize;
 
-use iron::{Iron, Request, Response, IronResult};
+use iron::prelude::*;
 use iron::status;
-use router::Router;
-use rustc_serialize::json;
-use iron::mime::Mime;
-use iron::headers::Server;
 use iron::modifiers::Header;
+use iron::typemap::Key;
+use hyper::header::{Server, ContentType};
+use rand::distributions::{Range, IndependentSample};
+use r2d2_postgres::{PostgresConnectionManager, TlsMode};
+use persistent::{Read};
+use r2d2::Pool;
 
-#[derive(RustcDecodable, RustcEncodable)]
-struct Message {
-    message: String,
+include!(concat!(env!("OUT_DIR"),"/main_types.rs"));
+
+pub type PostgresPool = Pool<PostgresConnectionManager>;
+
+struct DbPool;
+impl Key for DbPool { type Value = PostgresPool; }
+
+struct FortuneTemplate;
+impl Key for FortuneTemplate { type Value = mustache::Template; }
+
+#[derive(RustcEncodable)]
+struct FortuneRow {
+    id: i32,
+    message: String
 }
 
 fn main() {
-    let mut router = Router::new();
-    router.get("/json", json_handler);
-    router.get("/plaintext", plaintext_handler);
-
-    Iron::new(router).http("0.0.0.0:8080").unwrap();
+    let dbhost = match option_env!("DBHOST") {
+        Some(it) => it,
+        _ => "localhost"
+    };
+    let r2d2_config = r2d2::Config::default();
+    let pg_conn_manager = PostgresConnectionManager::new(
+        format!("postgres://benchmarkdbuser:benchmarkdbpass@{dbhost}/hello_world", dbhost=dbhost),
+        TlsMode::None).unwrap();
+    let pool = r2d2::Pool::new(r2d2_config, pg_conn_manager).unwrap();
+    let template = mustache::compile_str("<!DOCTYPE html>
+    <html> <head><title>Fortunes</title></head>
+    <body> <table> 
+    <tr><th>id</th><th>message</th></tr> 
+    {{#.}} <tr><td>{{id}}</td><td>{{message}}</td></tr> 
+    {{/.}} 
+    </table> </body> </html>").unwrap();
+    let app = router!(
+            json: get "/json" => json_handler,
+            single_db_query: get "/db" => single_db_query_handler,
+            plaintext: get "/plaintext" => plaintext_handler,
+            queries: get "/queries" => queries_handler,
+            fortune: get "/fortune" => fortune_handler,
+            updates: get "/updates" => updates_handler
+        );
+    let mut middleware = Chain::new(app);
+    middleware.link(Read::<DbPool>::both(pool));
+    middleware.link(Read::<FortuneTemplate>::both(template));
+    println!("Starting server...");
+    Iron::new(middleware).http("0.0.0.0:8080").unwrap();
 }
 
 fn json_handler(_: &mut Request) -> IronResult<Response> {
-    let message: Message = Message { message: "Hello, World!".to_string() };
-    let mime: Mime = "application/json".parse().unwrap();
-    let server = Header(Server(String::from("Iron")));
-    Ok(Response::with((status::Ok, json::encode(&message).unwrap(), mime, server)))
+    let message: Message = Message { 
+        message: "Hello, World!".to_owned() 
+    };
+    let content_type = Header(ContentType::json());
+    let server = Header(Server("Iron".to_owned()));
+    Ok(Response::with(
+        (status::Ok,
+        serde_json::to_string(&message).unwrap(),
+        content_type,
+        server
+        )))
 }
 
 fn plaintext_handler(_: &mut Request) -> IronResult<Response> {
-    let server = Header(Server(String::from("Iron")));
-    Ok(Response::with((status::Ok, "Hello, World!", server)))
+    let server = Header(Server("Iron".to_owned()));
+    Ok(Response::with((
+        status::Ok, 
+        "Hello, World!", 
+        server)))
+}
+
+fn single_db_query_handler(req: &mut Request) -> IronResult<Response> {
+    let content_type = Header(ContentType::json());
+    let server = Header(Server("Iron".to_owned()));
+    let pool = req.get::<Read<DbPool>>().unwrap();
+    let conn = pool.get().unwrap();
+    let row = random_row(conn);
+    Ok(Response::with((
+        status::Ok,
+        serde_json::to_string(&row).unwrap(),
+        server,
+        content_type
+        )))
+}
+
+fn queries_handler(req: &mut Request) -> IronResult<Response> {
+    let content_type = Header(ContentType::json());
+    let server = Header(Server("Iron".to_owned()));
+    let pool = req.get::<Read<DbPool>>().unwrap();
+    let query = req.url.query().unwrap();
+    let param = match get_param(query, "queries") {
+        Some(n) => match n.parse::<usize>() {
+            Ok(m) => match m {
+                e @ 1...500 => e,
+                e if e > 500 => 500,
+                _ => 1
+            },
+            _ => 1
+        },
+        _ => 1
+    };
+    let mut res: Vec<DatabaseRow> = Vec::with_capacity(param);
+    for _ in 0..param {
+        let conn = pool.get().unwrap();
+        res.push(random_row(conn))
+    };
+    Ok(
+        Response::with((
+            status::Ok, 
+            serde_json::to_string(&res).unwrap(),
+            server,
+            content_type
+    )))
+}
+
+fn fortune_handler(req: &mut Request) -> IronResult<Response> {
+    let content_type = Header(ContentType::html());
+    let server = Header(Server("Iron".to_owned()));
+    let template = req.get::<Read<FortuneTemplate>>().unwrap();
+    let pool = req.get::<Read<DbPool>>().unwrap();
+    let conn = pool.get().unwrap();
+    let query_res = &conn.query("SELECT id, message FROM Fortune",&[]).unwrap();
+    let query_res_iter = query_res.iter();
+    let mut rows: Vec<FortuneRow> = query_res_iter.map(|row| FortuneRow {
+        id: row.get(0),
+        message: row.get(1)
+    }).collect();
+    rows.push(FortuneRow {
+        id: 0,
+        message: "Additional fortune added at request time.".to_string()
+    });
+    rows.sort_by(|it, next| it.message.cmp(&next.message));
+    let mut res = vec![];
+    template.render(&mut res, &rows).unwrap();
+    Ok(
+        Response::with((
+            status::Ok,
+            res,
+            server,
+            content_type
+    )))
+}
+
+fn updates_handler(req: &mut Request) -> IronResult<Response> {
+    let mut rng = rand::thread_rng();
+    let between = Range::new(1,10000);
+    let content_type = Header(ContentType::json());
+    let server = Header(Server("Iron".to_owned()));
+    let pool = req.get::<Read<DbPool>>().unwrap();
+    let query = req.url.query().unwrap();
+    let param = match get_param(query, "queries") {
+        Some(n) => match n.parse::<usize>() {
+            Ok(m) => match m {
+                e @ 1...500 => e,
+                e if e > 500 => 500,
+                _ => 1
+            },
+            _ => 1
+        },
+        _ => 1
+    };
+    let mut dbres: Vec<DatabaseRow> = Vec::with_capacity(param);
+    for _ in 0..param {
+        let conn = pool.get().unwrap();
+        dbres.push(random_row(conn))
+    };
+    let conn = pool.get().unwrap();
+    let trans = conn.transaction().unwrap();
+    // Sorting guarantees no deadlocks between multiple concurrent threads
+    dbres.sort_by_key(|it| it.id );
+    let mut res: Vec<DatabaseRow> = Vec::with_capacity(param);
+    for row in dbres {
+        let num = between.ind_sample(&mut rng);
+        trans.execute("UPDATE World SET randomnumber = $1 WHERE id = $2", &[&num, &row.id]).unwrap();
+        res.push(DatabaseRow {
+            id: row.id,
+            randomNumber: num 
+        })
+    }
+    trans.commit().unwrap();
+    Ok(
+        Response::with((
+            status::Ok,
+            serde_json::to_string(&res).unwrap(),
+            server,
+            content_type
+    )))
+}
+
+fn random_row(conn: r2d2::PooledConnection<PostgresConnectionManager>) -> DatabaseRow {
+    let mut rng = rand::thread_rng();
+    let between = Range::new(1,10000);
+    let num = between.ind_sample(&mut rng);
+    let rows = &conn.query("SELECT id, randomnumber FROM World WHERE id = $1",&[&num]).unwrap();
+    let row = rows.get(0);
+    DatabaseRow {
+        id: row.get(0),
+        randomNumber: row.get(1)
+    }
+}
+
+fn get_param<'a>(querystring: &'a str, param: &'a str) -> Option<&'a str> {
+    let n = querystring.split("&").find(
+        |&it| !(it.find(param).is_none())
+    ); 
+    match n {
+        Some(n) => n.split("=").nth(1),
+        _ => n
+    }
 }

+ 11 - 0
frameworks/Rust/iron/src/main_types.in.rs

@@ -0,0 +1,11 @@
+#[derive(Serialize, Deserialize)]
+struct Message {
+    message: String,
+}
+
+#[allow(non_snake_case)]
+#[derive(Serialize, Deserialize)]
+struct DatabaseRow {
+	id: i32,
+	randomNumber: i32
+}

+ 1 - 1
toolset/setup/linux/languages/rust.sh

@@ -1,6 +1,6 @@
 #!/bin/bash
 
-RUST_VERSION="1.9.0"
+RUST_VERSION="1.13.0"
 
 RETCODE=$(fw_exists $IROOT/rust.installed)
 [ ! "$RETCODE" == 0 ] || { \