Browse Source

prepare Actix Web v4 update (#7120)

* Bump non-actix deps in actix

* update main_platform to v4

* actix web v4 update checkpoint 1

* fix raw server

* fix diesel bench

* fmt

* fix config

* fix server impl

* update bench config
Rob Ede 3 years ago
parent
commit
fab368b3b6

File diff suppressed because it is too large
+ 188 - 715
frameworks/Rust/actix/Cargo.lock


+ 36 - 39
frameworks/Rust/actix/Cargo.toml

@@ -1,59 +1,56 @@
 [package]
-name = "actix"
+name = "tfb-actix"
 version = "3.0.0"
 edition = "2018"
 
 [[bin]]
-name = "actix"
-path = "src/main.rs"
+name = "tfb-web"
+path = "src/main_web.rs"
 
 [[bin]]
-name = "actix-platform"
-path = "src/main_platform.rs"
+name = "tfb-web-diesel"
+path = "src/main_web_diesel.rs"
 
 [[bin]]
-name = "actix-diesel"
-path = "src/main_diesel.rs"
+name = "tfb-http"
+path = "src/main_http.rs"
 
 [[bin]]
-name = "actix-pg"
-path = "src/main_pg.rs"
-
-[[bin]]
-name = "actix-raw"
-path = "src/main_raw.rs"
+name = "tfb-server"
+path = "src/main_server.rs"
 
 [dependencies]
-actix = "0.10.0"
-actix-web = { version = "3.1.0", default-features = false }
-actix-http = { version = "2.0.0", default-features = false }
-actix-rt = "1.1.1"
-actix-codec = "0.3.0"
-actix-server = "1.0.4"
-actix-service = "1.0.6"
-snmalloc-rs = "0.2.6"
-askama = "0.8"
-yarte = { version = "0.14", features = ["bytes-buf"] }
-serde = { version = "1.0", features = ["derive"] }
-serde_json = "1.0"
-env_logger = "0.7"
-rand = { version = "0.7", features = ["small_rng"] }
-bytes = "0.5.3"
-num_cpus = "1.13.0"
-futures = "0.3.1"
+actix = "0.12"
+actix-web = { version = "4.0.0-rc.3", default-features = false, features = ["macros"] }
+actix-http = { version = "3.0.0-rc.2", default-features = false }
+actix-rt = "2"
+actix-codec = "0.4"
+actix-server = "2"
+actix-service = "2"
+
+askama = "0.11"
+bytes = "1"
+diesel = { version = "1.4", features = ["postgres"] }
+env_logger = "0.9"
+futures = "0.3.7"
 http = "0.2"
-simd-json = "0.3"
-simd-json-derive = "0.1.9"
-diesel = { version = "1.4.3", features = ["postgres"] }
-url = "2.1.1"
 log = { version = "0.4", features = ["release_max_level_debug"] }
-v_htmlescape = "0.10"
-tokio = "0.2"
-tokio-postgres = { git = "https://github.com/fafhrd91/rust-postgres.git" }
+num_cpus = "1.13"
+rand = { version = "0.8", features = ["small_rng"] }
+serde = { version = "1", features = ["derive"] }
+serde_json = "1"
+simd-json = "0.4"
+simd-json-derive = "0.2"
+snmalloc-rs = "0.2.6"
+tokio = { version = "1", features = ["full"] }
+tokio-postgres = "0.7.5"
+url = "2.1"
+v_htmlescape = "0.14"
+yarte = { version = "0.15", features = ["bytes-buf"] }
 
 [build-dependencies]
-askama = "0.8"
-bindgen = "0.42"
+askama = "0.11"
+bindgen = "0.59"
 
 [profile.release]
 lto = true

+ 0 - 13
frameworks/Rust/actix/actix-diesel.dockerfile

@@ -1,13 +0,0 @@
-FROM rust:1.46
-
-RUN apt-get update -yqq && apt-get install -yqq cmake g++
-
-ADD ./ /actix
-WORKDIR /actix
-
-RUN cargo clean
-RUN RUSTFLAGS="-C target-cpu=native" cargo build --release
-
-EXPOSE 8080
-
-CMD ./target/release/actix-diesel

+ 2 - 2
frameworks/Rust/actix/actix-pg.dockerfile → frameworks/Rust/actix/actix-http.dockerfile

@@ -1,4 +1,4 @@
-FROM rust:1.46
+FROM rust:1.58
 
 RUN apt-get update -yqq && apt-get install -yqq cmake g++
 
@@ -10,4 +10,4 @@ RUN RUSTFLAGS="-C target-cpu=native" cargo build --release
 
 EXPOSE 8080
 
-CMD ./target/release/actix-pg
+CMD ./target/release/tfb-http

+ 2 - 2
frameworks/Rust/actix/actix-raw.dockerfile → frameworks/Rust/actix/actix-server.dockerfile

@@ -1,4 +1,4 @@
-FROM rust:1.46
+FROM rust:1.58
 
 RUN apt-get update -yqq && apt-get install -yqq cmake g++
 
@@ -10,4 +10,4 @@ RUN RUSTFLAGS="-C target-cpu=native" cargo build --release
 
 EXPOSE 8080
 
-CMD ./target/release/actix-raw
+CMD ./target/release/tfb-server

+ 2 - 2
frameworks/Rust/actix/actix-core.dockerfile → frameworks/Rust/actix/actix-web-diesel.dockerfile

@@ -1,4 +1,4 @@
-FROM rust:1.46
+FROM rust:1.58
 
 RUN apt-get update -yqq && apt-get install -yqq cmake g++
 
@@ -10,4 +10,4 @@ RUN RUSTFLAGS="-C target-cpu=native" cargo build --release
 
 EXPOSE 8080
 
-CMD ./target/release/actix-platform
+CMD ./target/release/tfb-web-diesel

+ 2 - 2
frameworks/Rust/actix/actix.dockerfile

@@ -1,4 +1,4 @@
-FROM rust:1.46
+FROM rust:1.58
 
 RUN apt-get update -yqq && apt-get install -yqq cmake g++
 
@@ -10,4 +10,4 @@ RUN RUSTFLAGS="-C target-cpu=native" cargo build --release
 
 EXPOSE 8080
 
-CMD ./target/release/actix
+CMD ./target/release/tfb-web

+ 23 - 43
frameworks/Rust/actix/benchmark_config.json

@@ -11,89 +11,69 @@
       "framework": "actix",
       "language": "Rust",
       "orm": "Raw",
-      "platform": "None",
+      "platform": "actix-http",
       "webserver": "actix-web",
       "os": "Linux",
       "database_os": "Linux",
-      "display_name": "Actix",
+      "display_name": "Actix Web [Postgres]",
       "notes": "",
-      "versus": ""
+      "versus": "actix-http"
     },
-    "core": {
-      "fortune_url": "/fortunes",
+    "web-diesel": {
       "db_url": "/db",
+      "fortune_url": "/fortunes",
       "query_url": "/queries?q=",
       "update_url": "/updates?q=",
       "port": 8080,
       "approach": "Realistic",
-      "classification": "Platform",
-      "database": "Postgres",
-      "framework": "actix",
-      "language": "Rust",
-      "orm": "Raw",
-      "platform": "None",
-      "webserver": "actix-web",
-      "os": "Linux",
-      "database_os": "Linux",
-      "display_name": "Actix [Platform]",
-      "notes": "",
-      "versus": ""
-    },
-    "raw": {
-      "json_url": "/json",
-      "plaintext_url": "/plaintext",
-      "port": 8080,
-      "approach": "Realistic",
-      "classification": "Platform",
+      "classification": "Micro",
       "database": "Postgres",
       "framework": "actix",
       "language": "Rust",
-      "orm": "Raw",
-      "platform": "None",
+      "orm": "Full",
+      "platform": "actix-http",
       "webserver": "actix-web",
       "os": "Linux",
       "database_os": "Linux",
-      "display_name": "Actix [Raw]",
+      "display_name": "Actix Web [Diesel]",
       "notes": "",
-      "versus": ""
+      "versus": "actix-http"
     },
-    "diesel": {
+    "http": {
       "db_url": "/db",
       "fortune_url": "/fortunes",
       "query_url": "/queries?q=",
       "update_url": "/updates?q=",
       "port": 8080,
       "approach": "Realistic",
-      "classification": "Micro",
+      "classification": "Platform",
       "database": "Postgres",
       "framework": "actix",
       "language": "Rust",
-      "orm": "Full",
+      "orm": "Raw",
       "platform": "None",
-      "webserver": "actix-web",
+      "webserver": "actix-http",
       "os": "Linux",
       "database_os": "Linux",
-      "display_name": "Actix [Diesel]",
+      "display_name": "actix-http",
       "notes": "",
-      "versus": ""
+      "versus": "actix-server"
     },
-    "pg": {
-      "db_url": "/db",
-      "fortune_url": "/fortunes",
-      "query_url": "/queries?q=",
-      "update_url": "/updates?q=",
+    "server": {
+      "json_url": "/json",
+      "plaintext_url": "/plaintext",
       "port": 8080,
-      "approach": "Realistic",
-      "classification": "Micro",
+      "approach": "Stripped",
+      "classification": "Platform",
       "database": "Postgres",
       "framework": "actix",
       "language": "Rust",
       "orm": "Raw",
       "platform": "None",
-      "webserver": "actix-web",
+      "webserver": "actix-server",
       "os": "Linux",
       "database_os": "Linux",
-      "display_name": "Actix [Postgres]",
+      "display_name": "actix-server",
       "notes": "",
       "versus": ""
     }

+ 0 - 73
frameworks/Rust/actix/config.toml

@@ -1,73 +0,0 @@
-[framework]
-name = "actix"
-
-[main]
-urls.plaintext = "/plaintext"
-urls.json = "/json"
-approach = "Realistic"
-classification = "Micro"
-database = "Postgres"
-database_os = "Linux"
-os = "Linux"
-orm = "Raw"
-platform = "None"
-webserver = "actix-web"
-versus = ""
-
-[core]
-urls.db = "/db"
-urls.query = "/queries?q="
-urls.update = "/updates?q="
-urls.fortune = "/fortunes"
-approach = "Realistic"
-classification = "Platform"
-database = "Postgres"
-database_os = "Linux"
-os = "Linux"
-orm = "Raw"
-platform = "None"
-webserver = "actix-web"
-versus = ""
-
-[diesel]
-urls.db = "/db"
-urls.query = "/queries?q="
-urls.update = "/updates?q="
-urls.fortune = "/fortunes"
-approach = "Realistic"
-classification = "Micro"
-database = "Postgres"
-database_os = "Linux"
-os = "Linux"
-orm = "Full"
-platform = "None"
-webserver = "actix-web"
-versus = ""
-
-[pg]
-urls.db = "/db"
-urls.query = "/queries?q="
-urls.update = "/updates?q="
-urls.fortune = "/fortunes"
-approach = "Realistic"
-classification = "Micro"
-database = "Postgres"
-database_os = "Linux"
-os = "Linux"
-orm = "Raw"
-platform = "None"
-webserver = "actix-web"
-versus = ""
-
-[raw]
-urls.plaintext = "/plaintext"
-urls.json = "/json"
-approach = "Realistic"
-classification = "Platform"
-database = "Postgres"
-database_os = "Linux"
-os = "Linux"
-orm = "Raw"
-platform = "None"
-webserver = "actix-web"
-versus = ""

+ 2 - 0
frameworks/Rust/actix/rust-toolchain.toml

@@ -0,0 +1,2 @@
+[toolchain]
+channel = "nightly"

+ 1 - 1
frameworks/Rust/actix/rustfmt.toml

@@ -1,2 +1,2 @@
-max_width = 89
+max_width = 96
 reorder_imports = true

+ 150 - 107
frameworks/Rust/actix/src/db.rs

@@ -1,147 +1,190 @@
-//! Db executor actor
-
-use std::io;
+use std::{borrow::Cow, collections::HashMap, fmt::Write, io, rc::Rc};
+
+use actix_http::{body::BoxBody, Response};
+use actix_rt::pin;
+use bytes::{Bytes, BytesMut};
+use futures::{
+    stream::futures_unordered::FuturesUnordered, FutureExt, StreamExt, TryStreamExt,
+};
+use rand::{rngs::SmallRng, thread_rng, Rng, SeedableRng};
+use tokio_postgres::{connect, types::ToSql, Client, NoTls, Statement};
+
+use crate::{
+    models::World,
+    utils::{Fortune, Writer},
+};
+
+#[derive(Debug)]
+pub enum PgError {
+    Io(io::Error),
+    Pg(tokio_postgres::Error),
+}
 
-use actix::prelude::*;
-use diesel::prelude::*;
-use diesel::result::Error;
-use rand::rngs::SmallRng;
-use rand::{Rng, SeedableRng};
+impl From<io::Error> for PgError {
+    fn from(err: io::Error) -> Self {
+        PgError::Io(err)
+    }
+}
 
-use crate::models;
+impl From<tokio_postgres::Error> for PgError {
+    fn from(err: tokio_postgres::Error) -> Self {
+        PgError::Pg(err)
+    }
+}
 
-pub struct DbExecutor {
-    conn: PgConnection,
-    rng: SmallRng,
+impl From<PgError> for Response<BoxBody> {
+    fn from(_err: PgError) -> Self {
+        Response::internal_server_error()
+    }
 }
 
-impl Actor for DbExecutor {
-    type Context = SyncContext<Self>;
+/// Postgres interface
+pub struct PgConnection {
+    client: Client,
+    fortune: Statement,
+    world: Statement,
+    updates: HashMap<u16, Statement>,
 }
 
-impl DbExecutor {
-    pub fn new(db_url: &str) -> DbExecutor {
-        DbExecutor {
-            conn: PgConnection::establish(db_url)
-                .expect(&format!("Error connecting to {}", db_url)),
-            rng: SmallRng::from_entropy(),
+impl PgConnection {
+    pub async fn connect(db_url: &str) -> Rc<PgConnection> {
+        let (cl, conn) = connect(db_url, NoTls)
+            .await
+            .expect("can not connect to postgresql");
+
+        actix_rt::spawn(conn.map(|_| ()));
+
+        let fortune = cl.prepare("SELECT * FROM fortune").await.unwrap();
+        let mut updates = HashMap::new();
+
+        for num in 1..=500u16 {
+            let mut pl = 1;
+            let mut q = String::new();
+
+            q.push_str("UPDATE world SET randomnumber = CASE id ");
+
+            for _ in 1..=num {
+                let _ = write!(q, "when ${} then ${} ", pl, pl + 1);
+                pl += 2;
+            }
+
+            q.push_str("ELSE randomnumber END WHERE id IN (");
+
+            for _ in 1..=num {
+                let _ = write!(q, "${},", pl);
+                pl += 1;
+            }
+
+            q.pop();
+            q.push(')');
+
+            updates.insert(num, cl.prepare(&q).await.unwrap());
         }
+
+        let world = cl.prepare("SELECT * FROM world WHERE id=$1").await.unwrap();
+
+        Rc::new(PgConnection {
+            client: cl,
+            fortune,
+            world,
+            updates,
+        })
     }
 }
 
-pub struct RandomWorld;
+impl PgConnection {
+    async fn query_one_world(&self, id: i32) -> Result<World, PgError> {
+        let stream = self.client.query_raw(&self.world, &[&id]).await?;
+        pin!(stream);
+        let row = stream.next().await.unwrap()?;
+        Ok(World::new(row.get(0), row.get(1)))
+    }
 
-impl Message for RandomWorld {
-    type Result = io::Result<models::World>;
-}
+    pub async fn get_world(&self) -> Result<Bytes, PgError> {
+        let mut rng = SmallRng::from_rng(&mut thread_rng()).unwrap();
 
-impl Handler<RandomWorld> for DbExecutor {
-    type Result = io::Result<models::World>;
+        let random_id = (rng.gen::<u32>() % 10_000 + 1) as i32;
 
-    fn handle(&mut self, _: RandomWorld, _: &mut Self::Context) -> Self::Result {
-        use crate::schema::world::dsl::*;
+        let world = self.query_one_world(random_id).await?;
+        let mut body = BytesMut::with_capacity(40);
+        serde_json::to_writer(Writer(&mut body), &world).unwrap();
 
-        let random_id = self.rng.gen_range(1, 10_001);
-        match world
-            .filter(id.eq(random_id))
-            .load::<models::World>(&self.conn)
-        {
-            Ok(mut items) => Ok(items.pop().unwrap()),
-            Err(_) => Err(io::Error::new(io::ErrorKind::Other, "Database error")),
-        }
+        Ok(body.freeze())
     }
-}
 
-pub struct RandomWorlds(pub u16);
+    pub async fn get_worlds(&self, num: usize) -> Result<Vec<World>, PgError> {
+        let mut rng = SmallRng::from_rng(&mut thread_rng()).unwrap();
 
-impl Message for RandomWorlds {
-    type Result = io::Result<Vec<models::World>>;
-}
+        let worlds = FuturesUnordered::new();
 
-impl Handler<RandomWorlds> for DbExecutor {
-    type Result = io::Result<Vec<models::World>>;
+        for _ in 0..num {
+            let w_id = (rng.gen::<u32>() % 10_000 + 1) as i32;
+            worlds.push(self.query_one_world(w_id));
+        }
+
+        worlds.try_collect().await
+    }
 
-    fn handle(&mut self, msg: RandomWorlds, _: &mut Self::Context) -> Self::Result {
-        use crate::schema::world::dsl::*;
+    pub async fn update(&self, num: u16) -> Result<Vec<World>, PgError> {
+        let mut rng = SmallRng::from_rng(&mut thread_rng()).unwrap();
 
-        let mut worlds = Vec::with_capacity(msg.0 as usize);
-        for _ in 0..msg.0 {
-            let w_id = self.rng.gen_range(1, 10_001);
-            let w = match world.filter(id.eq(w_id)).load::<models::World>(&self.conn) {
-                Ok(mut items) => items.pop().unwrap(),
-                Err(_) => {
-                    return Err(io::Error::new(io::ErrorKind::Other, "Database error"));
+        let worlds = FuturesUnordered::new();
+
+        for _ in 0..num {
+            let id = (rng.gen::<u32>() % 10_000 + 1) as i32;
+            let w_id = (rng.gen::<u32>() % 10_000 + 1) as i32;
+
+            worlds.push(self.query_one_world(w_id).map(move |res| match res {
+                Ok(mut world) => {
+                    world.randomnumber = id;
+                    Ok(world)
                 }
-            };
-            worlds.push(w)
+
+                Err(err) => Err(err),
+            }));
         }
-        Ok(worlds)
-    }
-}
 
-pub struct UpdateWorld(pub u16);
+        let st = self.updates.get(&num).unwrap().clone();
 
-impl Message for UpdateWorld {
-    type Result = io::Result<Vec<models::World>>;
-}
+        let worlds: Vec<World> = worlds.try_collect().await?;
 
-impl Handler<UpdateWorld> for DbExecutor {
-    type Result = io::Result<Vec<models::World>>;
+        let mut params: Vec<&(dyn ToSql + Sync)> = Vec::with_capacity(num as usize * 3);
 
-    fn handle(&mut self, msg: UpdateWorld, _: &mut Self::Context) -> Self::Result {
-        use crate::schema::world::dsl::*;
+        for w in &worlds {
+            params.push(&w.id);
+            params.push(&w.randomnumber);
+        }
 
-        let mut worlds = Vec::with_capacity(msg.0 as usize);
-        for _ in 0..msg.0 {
-            let w_id: i32 = self.rng.gen_range(1, 10_001);
-            let mut w = match world.filter(id.eq(w_id)).load::<models::World>(&self.conn)
-            {
-                Ok(mut items) => items.pop().unwrap(),
-                Err(_) => {
-                    return Err(io::Error::new(io::ErrorKind::Other, "Database error"));
-                }
-            };
-            w.randomnumber = self.rng.gen_range(1, 10_001);
-            worlds.push(w);
+        for w in &worlds {
+            params.push(&w.id);
         }
-        worlds.sort_by_key(|w| w.id);
-
-        let _ = self.conn.transaction::<(), Error, _>(|| {
-            for w in &worlds {
-                let _ = diesel::update(world)
-                    .filter(id.eq(w.id))
-                    .set(randomnumber.eq(w.randomnumber))
-                    .execute(&self.conn);
-            }
-            Ok(())
-        });
+
+        self.client.query(&st, &params[..]).await?;
 
         Ok(worlds)
     }
-}
 
-pub struct TellFortune;
+    pub async fn tell_fortune(&self) -> Result<Vec<Fortune>, PgError> {
+        let mut items = vec![Fortune {
+            id: 0,
+            message: Cow::Borrowed("Additional fortune added at request time."),
+        }];
 
-impl Message for TellFortune {
-    type Result = io::Result<Vec<models::Fortune>>;
-}
+        let fut = self.client.query_raw::<_, _, &[i32; 0]>(&self.fortune, &[]);
 
-impl Handler<TellFortune> for DbExecutor {
-    type Result = io::Result<Vec<models::Fortune>>;
+        let stream = fut.await?;
+        pin!(stream);
 
-    fn handle(&mut self, _: TellFortune, _: &mut Self::Context) -> Self::Result {
-        use crate::schema::fortune::dsl::*;
+        while let Some(row) = stream.next().await {
+            let row = row?;
 
-        match fortune.load::<models::Fortune>(&self.conn) {
-            Ok(mut items) => {
-                items.push(models::Fortune {
-                    id: 0,
-                    message: "Additional fortune added at request time.".to_string(),
-                });
-                items.sort_by(|it, next| it.message.cmp(&next.message));
-                Ok(items)
-            }
-            Err(e) => Err(io::Error::new(io::ErrorKind::Other, e)),
+            items.push(Fortune {
+                id: row.get(0),
+                message: Cow::Owned(row.get(1)),
+            });
         }
+
+        items.sort_by(|it, next| it.message.cmp(&next.message));
+        Ok(items)
     }
 }

+ 144 - 0
frameworks/Rust/actix/src/db_diesel.rs

@@ -0,0 +1,144 @@
+//! Diesel DB methods.
+
+use std::io;
+
+use actix::prelude::*;
+use diesel::{prelude::*, result::Error};
+use rand::{rngs::SmallRng, Rng, SeedableRng};
+
+use crate::models;
+
+pub struct DbExecutor {
+    conn: PgConnection,
+    rng: SmallRng,
+}
+
+impl DbExecutor {
+    pub fn new(db_url: &str) -> DbExecutor {
+        DbExecutor {
+            conn: PgConnection::establish(db_url)
+                .unwrap_or_else(|_| panic!("Error connecting to {}", db_url)),
+            rng: SmallRng::from_entropy(),
+        }
+    }
+}
+
+impl Actor for DbExecutor {
+    type Context = SyncContext<Self>;
+}
+
+pub struct RandomWorld;
+
+impl Message for RandomWorld {
+    type Result = io::Result<models::World>;
+}
+
+impl Handler<RandomWorld> for DbExecutor {
+    type Result = io::Result<models::World>;
+
+    fn handle(&mut self, _: RandomWorld, _: &mut Self::Context) -> Self::Result {
+        use crate::schema::world::dsl::*;
+
+        let random_id = self.rng.gen_range(1..10_001);
+        match world
+            .filter(id.eq(random_id))
+            .load::<models::World>(&self.conn)
+        {
+            Ok(mut items) => Ok(items.pop().unwrap()),
+            Err(_) => Err(io::Error::new(io::ErrorKind::Other, "Database error")),
+        }
+    }
+}
+
+pub struct RandomWorlds(pub u16);
+
+impl Message for RandomWorlds {
+    type Result = io::Result<Vec<models::World>>;
+}
+
+impl Handler<RandomWorlds> for DbExecutor {
+    type Result = io::Result<Vec<models::World>>;
+
+    fn handle(&mut self, msg: RandomWorlds, _: &mut Self::Context) -> Self::Result {
+        use crate::schema::world::dsl::*;
+
+        let mut worlds = Vec::with_capacity(msg.0 as usize);
+        for _ in 0..msg.0 {
+            let w_id = self.rng.gen_range(1..10_001);
+            let w = match world.filter(id.eq(w_id)).load::<models::World>(&self.conn) {
+                Ok(mut items) => items.pop().unwrap(),
+                Err(_) => {
+                    return Err(io::Error::new(io::ErrorKind::Other, "Database error"));
+                }
+            };
+            worlds.push(w)
+        }
+        Ok(worlds)
+    }
+}
+
+pub struct UpdateWorld(pub u16);
+
+impl Message for UpdateWorld {
+    type Result = io::Result<Vec<models::World>>;
+}
+
+impl Handler<UpdateWorld> for DbExecutor {
+    type Result = io::Result<Vec<models::World>>;
+
+    fn handle(&mut self, msg: UpdateWorld, _: &mut Self::Context) -> Self::Result {
+        use crate::schema::world::dsl::*;
+
+        let mut worlds = Vec::with_capacity(msg.0 as usize);
+        for _ in 0..msg.0 {
+            let w_id: i32 = self.rng.gen_range(1..10_001);
+            let mut w = match world.filter(id.eq(w_id)).load::<models::World>(&self.conn) {
+                Ok(mut items) => items.pop().unwrap(),
+                Err(_) => {
+                    return Err(io::Error::new(io::ErrorKind::Other, "Database error"));
+                }
+            };
+            w.randomnumber = self.rng.gen_range(1..10_001);
+            worlds.push(w);
+        }
+        worlds.sort_by_key(|w| w.id);
+
+        let _ = self.conn.transaction::<(), Error, _>(|| {
+            for w in &worlds {
+                let _ = diesel::update(world)
+                    .filter(id.eq(w.id))
+                    .set(randomnumber.eq(w.randomnumber))
+                    .execute(&self.conn);
+            }
+            Ok(())
+        });
+
+        Ok(worlds)
+    }
+}
+
+pub struct TellFortune;
+
+impl Message for TellFortune {
+    type Result = io::Result<Vec<models::Fortune>>;
+}
+
+impl Handler<TellFortune> for DbExecutor {
+    type Result = io::Result<Vec<models::Fortune>>;
+
+    fn handle(&mut self, _: TellFortune, _: &mut Self::Context) -> Self::Result {
+        use crate::schema::fortune::dsl::*;
+
+        match fortune.load::<models::Fortune>(&self.conn) {
+            Ok(mut items) => {
+                items.push(models::Fortune {
+                    id: 0,
+                    message: "Additional fortune added at request time.".to_string(),
+                });
+                items.sort_by(|it, next| it.message.cmp(&next.message));
+                Ok(items)
+            }
+            Err(e) => Err(io::Error::new(io::ErrorKind::Other, e)),
+        }
+    }
+}

+ 0 - 219
frameworks/Rust/actix/src/db_pg.rs

@@ -1,219 +0,0 @@
-use std::borrow::Cow;
-use std::collections::HashMap;
-use std::fmt::Write;
-use std::io;
-
-use actix::prelude::*;
-use bytes::{Bytes, BytesMut};
-use futures::stream::futures_unordered::FuturesUnordered;
-use futures::{FutureExt, StreamExt, TryStreamExt};
-use rand::rngs::SmallRng;
-use rand::{Rng, SeedableRng};
-use tokio_postgres::types::ToSql;
-use tokio_postgres::{connect, Client, NoTls, Statement};
-
-use crate::models::World;
-use crate::utils::{Fortune, Writer};
-
-/// Postgres interface
-pub struct PgConnection {
-    cl: Client,
-    fortune: Statement,
-    world: Statement,
-    rng: SmallRng,
-    updates: HashMap<u16, Statement>,
-}
-
-impl Actor for PgConnection {
-    type Context = Context<Self>;
-}
-
-impl PgConnection {
-    pub async fn connect(db_url: &str) -> Result<Addr<PgConnection>, io::Error> {
-        let (cl, conn) = connect(db_url, NoTls)
-            .await
-            .expect("can not connect to postgresql");
-        actix_rt::spawn(conn.map(|_| ()));
-
-        let fortune = cl.prepare("SELECT * FROM fortune").await.unwrap();
-        let world = cl.prepare("SELECT * FROM world WHERE id=$1").await.unwrap();
-        let mut updates = HashMap::new();
-        for num in 1..=500u16 {
-            let mut pl = 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.insert(num, cl.prepare(&q).await.unwrap());
-        }
-
-        Ok(PgConnection::create(move |_| PgConnection {
-            cl,
-            fortune,
-            world,
-            updates,
-            rng: SmallRng::from_entropy(),
-        }))
-    }
-}
-
-pub struct RandomWorld;
-
-impl Message for RandomWorld {
-    type Result = io::Result<Bytes>;
-}
-
-impl Handler<RandomWorld> for PgConnection {
-    type Result = ResponseFuture<Result<Bytes, io::Error>>;
-
-    fn handle(&mut self, _: RandomWorld, _: &mut Self::Context) -> Self::Result {
-        let random_id = (self.rng.gen::<u32>() % 10_000 + 1) as i32;
-        let fut = self.cl.query_one(&self.world, &[&random_id]);
-
-        Box::pin(async move {
-            let row = fut
-                .await
-                .map_err(|e| io::Error::new(io::ErrorKind::Other, format!("{:?}", e)))?;
-
-            let mut body = BytesMut::with_capacity(40);
-            serde_json::to_writer(
-                Writer(&mut body),
-                &World {
-                    id: row.get(0),
-                    randomnumber: row.get(1),
-                },
-            )
-            .unwrap();
-
-            Ok(body.freeze())
-        })
-    }
-}
-
-pub struct RandomWorlds(pub u16);
-
-impl Message for RandomWorlds {
-    type Result = io::Result<Vec<World>>;
-}
-
-impl Handler<RandomWorlds> for PgConnection {
-    type Result = ResponseFuture<Result<Vec<World>, io::Error>>;
-
-    fn handle(&mut self, msg: RandomWorlds, _: &mut Self::Context) -> Self::Result {
-        let worlds = FuturesUnordered::new();
-        for _ in 0..msg.0 {
-            let w_id = (self.rng.gen::<u32>() % 10_000 + 1) as i32;
-            worlds.push(
-                self.cl
-                    .query_one(&self.world, &[&w_id])
-                    .map(|res| match res {
-                        Err(e) => {
-                            Err(io::Error::new(io::ErrorKind::Other, format!("{:?}", e)))
-                        }
-                        Ok(row) => Ok(World {
-                            id: row.get(0),
-                            randomnumber: row.get(1),
-                        }),
-                    }),
-            );
-        }
-
-        Box::pin(worlds.try_collect())
-    }
-}
-
-pub struct UpdateWorld(pub u16);
-
-impl Message for UpdateWorld {
-    type Result = io::Result<Vec<World>>;
-}
-
-impl Handler<UpdateWorld> for PgConnection {
-    type Result = ResponseFuture<Result<Vec<World>, io::Error>>;
-
-    fn handle(&mut self, msg: UpdateWorld, _: &mut Self::Context) -> Self::Result {
-        let worlds = FuturesUnordered::new();
-        for _ in 0..msg.0 {
-            let id = (self.rng.gen::<u32>() % 10_000 + 1) as i32;
-            let w_id = (self.rng.gen::<u32>() % 10_000 + 1) as i32;
-            worlds.push(self.cl.query_one(&self.world, &[&w_id]).map(
-                move |res| match res {
-                    Err(e) => {
-                        Err(io::Error::new(io::ErrorKind::Other, format!("{:?}", e)))
-                    }
-                    Ok(row) => Ok(World {
-                        id: row.get(0),
-                        randomnumber: id,
-                    }),
-                },
-            ));
-        }
-
-        let cl = self.cl.clone();
-        let st = self.updates.get(&msg.0).unwrap().clone();
-        Box::pin(async move {
-            let worlds: Vec<World> = worlds.try_collect().await?;
-
-            let mut params: Vec<&dyn ToSql> = Vec::with_capacity(msg.0 as usize * 3);
-            for w in &worlds {
-                params.push(&w.id);
-                params.push(&w.randomnumber);
-            }
-            for w in &worlds {
-                params.push(&w.id);
-            }
-
-            cl.query(&st, &params)
-                .await
-                .map_err(|e| io::Error::new(io::ErrorKind::Other, format!("{:?}", e)))?;
-
-            Ok(worlds)
-        })
-    }
-}
-
-pub struct TellFortune;
-
-impl Message for TellFortune {
-    type Result = io::Result<Vec<Fortune>>;
-}
-
-impl Handler<TellFortune> for PgConnection {
-    type Result = ResponseFuture<Result<Vec<Fortune>, io::Error>>;
-
-    fn handle(&mut self, _: TellFortune, _: &mut Self::Context) -> Self::Result {
-        let mut items = vec![Fortune {
-            id: 0,
-            message: Cow::Borrowed("Additional fortune added at request time."),
-        }];
-        let fut = self.cl.query_raw(&self.fortune, &[]);
-
-        Box::pin(async move {
-            let mut stream = fut
-                .await
-                .map_err(|e| io::Error::new(io::ErrorKind::Other, format!("{:?}", e)))?;
-
-            while let Some(row) = stream.next().await {
-                let row = row.map_err(|e| {
-                    io::Error::new(io::ErrorKind::Other, format!("{:?}", e))
-                })?;
-                items.push(Fortune {
-                    id: row.get(0),
-                    message: Cow::Owned(row.get(1)),
-                });
-            }
-
-            items.sort_by(|it, next| it.message.cmp(&next.message));
-            Ok(items)
-        })
-    }
-}

+ 0 - 190
frameworks/Rust/actix/src/db_pg_direct.rs

@@ -1,190 +0,0 @@
-use std::borrow::Cow;
-use std::collections::HashMap;
-use std::fmt::Write;
-use std::io;
-
-use actix_http::Error;
-use bytes::{Bytes, BytesMut};
-use futures::stream::futures_unordered::FuturesUnordered;
-use futures::{Future, FutureExt, StreamExt, TryStreamExt};
-use rand::rngs::SmallRng;
-use rand::{Rng, SeedableRng};
-use tokio_postgres::types::ToSql;
-use tokio_postgres::{connect, Client, NoTls, Statement};
-
-use crate::models::World;
-use crate::utils::{Fortune, Writer};
-
-/// Postgres interface
-pub struct PgConnection {
-    cl: Client,
-    fortune: Statement,
-    world: Statement,
-    rng: SmallRng,
-    updates: HashMap<u16, Statement>,
-}
-
-impl PgConnection {
-    pub async fn connect(db_url: &str) -> PgConnection {
-        let (cl, conn) = connect(db_url, NoTls)
-            .await
-            .expect("can not connect to postgresql");
-        actix_rt::spawn(conn.map(|_| ()));
-
-        let fortune = cl.prepare("SELECT * FROM fortune").await.unwrap();
-        let mut updates = HashMap::new();
-        for num in 1..=500u16 {
-            let mut pl = 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.insert(num, cl.prepare(&q).await.unwrap());
-        }
-        let world = cl.prepare("SELECT * FROM world WHERE id=$1").await.unwrap();
-
-        PgConnection {
-            cl,
-            fortune,
-            world,
-            updates,
-            rng: SmallRng::from_entropy(),
-        }
-    }
-}
-
-impl PgConnection {
-    pub fn get_world(&mut self) -> impl Future<Output = Result<Bytes, Error>> {
-        let random_id = (self.rng.gen::<u32>() % 10_000 + 1) as i32;
-        let fut = self.cl.query_one(&self.world, &[&random_id]);
-
-        async move {
-            let row = fut.await.map_err(|e| {
-                Error::from(io::Error::new(io::ErrorKind::Other, format!("{:?}", e)))
-            })?;
-
-            let mut body = BytesMut::with_capacity(40);
-            serde_json::to_writer(
-                Writer(&mut body),
-                &World {
-                    id: row.get(0),
-                    randomnumber: row.get(1),
-                },
-            )
-            .unwrap();
-
-            Ok(body.freeze())
-        }
-    }
-
-    pub fn get_worlds(
-        &mut self,
-        num: usize,
-    ) -> impl Future<Output = Result<Vec<World>, io::Error>> {
-        let worlds = FuturesUnordered::new();
-        for _ in 0..num {
-            let w_id = (self.rng.gen::<u32>() % 10_000 + 1) as i32;
-            worlds.push(
-                self.cl
-                    .query_one(&self.world, &[&w_id])
-                    .map(|res| match res {
-                        Err(e) => {
-                            Err(io::Error::new(io::ErrorKind::Other, format!("{:?}", e)))
-                        }
-                        Ok(row) => Ok(World {
-                            id: row.get(0),
-                            randomnumber: row.get(1),
-                        }),
-                    }),
-            );
-        }
-
-        worlds.try_collect()
-    }
-
-    pub fn update(
-        &mut self,
-        num: u16,
-    ) -> impl Future<Output = Result<Vec<World>, io::Error>> {
-        let worlds = FuturesUnordered::new();
-        for _ in 0..num {
-            let id = (self.rng.gen::<u32>() % 10_000 + 1) as i32;
-            let w_id = (self.rng.gen::<u32>() % 10_000 + 1) as i32;
-            worlds.push(self.cl.query_one(&self.world, &[&w_id]).map(
-                move |res| match res {
-                    Err(e) => {
-                        Err(io::Error::new(io::ErrorKind::Other, format!("{:?}", e)))
-                    }
-                    Ok(row) => {
-                        let mut world = World {
-                            id: row.get(0),
-                            randomnumber: row.get(1),
-                        };
-                        world.randomnumber = id;
-                        Ok(world)
-                    }
-                },
-            ));
-        }
-
-        let cl = self.cl.clone();
-        let st = self.updates.get(&num).unwrap().clone();
-        async move {
-            let worlds: Vec<World> = worlds.try_collect().await?;
-
-            let mut params: Vec<&dyn ToSql> = Vec::with_capacity(num as usize * 3);
-            for w in &worlds {
-                params.push(&w.id);
-                params.push(&w.randomnumber);
-            }
-            for w in &worlds {
-                params.push(&w.id);
-            }
-
-            cl.query(&st, &params)
-                .await
-                .map_err(|e| io::Error::new(io::ErrorKind::Other, format!("{:?}", e)))?;
-
-            Ok(worlds)
-        }
-    }
-
-    pub fn tell_fortune(
-        &mut self,
-    ) -> impl Future<Output = Result<Vec<Fortune>, io::Error>> {
-        let mut items = vec![Fortune {
-            id: 0,
-            message: Cow::Borrowed("Additional fortune added at request time."),
-        }];
-
-        let fut = self.cl.query_raw(&self.fortune, &[]);
-
-        async move {
-            let mut stream = fut
-                .await
-                .map_err(|e| io::Error::new(io::ErrorKind::Other, format!("{:?}", e)))?;
-
-            while let Some(row) = stream.next().await {
-                let row = row.map_err(|e| {
-                    io::Error::new(io::ErrorKind::Other, format!("{:?}", e))
-                })?;
-                items.push(Fortune {
-                    id: row.get(0),
-                    message: Cow::Owned(row.get(1)),
-                });
-            }
-
-            items.sort_by(|it, next| it.message.cmp(&next.message));
-            Ok(items)
-        }
-    }
-}

+ 83 - 64
frameworks/Rust/actix/src/main_platform.rs → frameworks/Rust/actix/src/main_http.rs

@@ -1,121 +1,142 @@
 #[global_allocator]
 static ALLOC: snmalloc_rs::SnMalloc = snmalloc_rs::SnMalloc;
 
-use std::future::Future;
-use std::pin::Pin;
-use std::task::{Context, Poll};
-
-use actix_http::body::Body;
-use actix_http::http::header::{CONTENT_TYPE, SERVER};
-use actix_http::http::{HeaderValue, StatusCode};
-use actix_http::{Error, HttpService, KeepAlive, Request, Response};
+use std::{io, rc::Rc, time::Duration};
+
+use actix_http::{
+    body::BoxBody,
+    header::{HeaderValue, CONTENT_TYPE, SERVER},
+    HttpService, KeepAlive, Request, Response, StatusCode,
+};
 use actix_server::Server;
 use actix_service::{Service, ServiceFactory};
 use bytes::{Bytes, BytesMut};
-use futures::future::ok;
-use serde_json::to_writer;
+use futures::future::{ok, LocalBoxFuture};
 use yarte::ywrite_html;
 
-mod db_pg_direct;
+mod db;
 mod models;
 mod utils;
 
-use crate::db_pg_direct::PgConnection;
-use crate::utils::Writer;
+use crate::{
+    db::{PgConnection, PgError},
+    utils::Writer,
+};
+
+#[derive(Debug)]
+enum Error {
+    Pg(PgError),
+    Io(io::Error),
+}
+
+impl From<io::Error> for Error {
+    fn from(err: io::Error) -> Self {
+        Error::Io(err)
+    }
+}
+
+impl From<PgError> for Error {
+    fn from(err: PgError) -> Self {
+        Error::Pg(err)
+    }
+}
+
+impl From<Error> for Response<BoxBody> {
+    fn from(_err: Error) -> Self {
+        Response::internal_server_error()
+    }
+}
 
 struct App {
-    db: PgConnection,
+    db: Rc<PgConnection>,
     hdr_srv: HeaderValue,
     hdr_ctjson: HeaderValue,
     hdr_cthtml: HeaderValue,
 }
 
-impl Service for App {
-    type Request = Request;
-    type Response = Response;
+impl Service<Request> for App {
+    type Response = Response<Bytes>;
     type Error = Error;
-    type Future = Pin<Box<dyn Future<Output = Result<Response, Error>>>>;
+    type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;
 
-    #[inline]
-    fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
-        Poll::Ready(Ok(()))
-    }
+    actix_service::always_ready!();
 
-    fn call(&mut self, req: Request) -> Self::Future {
-        let path = req.path();
-        match path {
+    fn call(&self, req: Request) -> Self::Future {
+        match req.path() {
             "/db" => {
                 let h_srv = self.hdr_srv.clone();
                 let h_ct = self.hdr_ctjson.clone();
-                let fut = self.db.get_world();
+                let db = Rc::clone(&self.db);
 
                 Box::pin(async move {
-                    let body = fut.await?;
-                    let mut res = Response::with_body(StatusCode::OK, Body::Bytes(body));
+                    let body = db.get_world().await?;
+                    let mut res = Response::with_body(StatusCode::OK, body);
                     let hdrs = res.headers_mut();
                     hdrs.insert(SERVER, h_srv);
                     hdrs.insert(CONTENT_TYPE, h_ct);
                     Ok(res)
                 })
             }
+
             "/fortunes" => {
                 let h_srv = self.hdr_srv.clone();
                 let h_ct = self.hdr_cthtml.clone();
-                let fut = self.db.tell_fortune();
+                let db = Rc::clone(&self.db);
 
                 Box::pin(async move {
-                    let fortunes = fut.await?;
+                    let fortunes = db.tell_fortune().await?;
 
                     let mut body = Vec::with_capacity(2048);
                     ywrite_html!(body, "{{> fortune }}");
 
-                    let mut res = Response::with_body(
-                        StatusCode::OK,
-                        Body::Bytes(Bytes::from(body)),
-                    );
+                    let mut res = Response::with_body(StatusCode::OK, Bytes::from(body));
                     let hdrs = res.headers_mut();
                     hdrs.insert(SERVER, h_srv);
                     hdrs.insert(CONTENT_TYPE, h_ct);
                     Ok(res)
                 })
             }
+
             "/queries" => {
                 let q = utils::get_query_param(req.uri().query().unwrap_or("")) as usize;
                 let h_srv = self.hdr_srv.clone();
                 let h_ct = self.hdr_ctjson.clone();
-                let fut = self.db.get_worlds(q);
+                let db = Rc::clone(&self.db);
 
                 Box::pin(async move {
-                    let worlds = fut.await?;
+                    let worlds = db.get_worlds(q).await?;
                     let mut body = BytesMut::with_capacity(35 * worlds.len());
-                    to_writer(Writer(&mut body), &worlds).unwrap();
-                    let mut res =
-                        Response::with_body(StatusCode::OK, Body::Bytes(body.freeze()));
+                    serde_json::to_writer(Writer(&mut body), &worlds).unwrap();
+                    let mut res = Response::with_body(StatusCode::OK, body.freeze());
                     let hdrs = res.headers_mut();
                     hdrs.insert(SERVER, h_srv);
                     hdrs.insert(CONTENT_TYPE, h_ct);
                     Ok(res)
                 })
             }
+
             "/updates" => {
                 let q = utils::get_query_param(req.uri().query().unwrap_or(""));
                 let h_srv = self.hdr_srv.clone();
                 let h_ct = self.hdr_ctjson.clone();
-                let fut = self.db.update(q);
+                let db = Rc::clone(&self.db);
 
                 Box::pin(async move {
-                    let worlds = fut.await?;
+                    let worlds = db.update(q).await?;
                     let mut body = BytesMut::with_capacity(35 * worlds.len());
-                    to_writer(Writer(&mut body), &worlds).unwrap();
-                    let mut res =
-                        Response::with_body(StatusCode::OK, Body::Bytes(body.freeze()));
+                    serde_json::to_writer(Writer(&mut body), &worlds).unwrap();
+                    let mut res = Response::with_body(StatusCode::OK, body.freeze());
                     let hdrs = res.headers_mut();
                     hdrs.insert(SERVER, h_srv);
                     hdrs.insert(CONTENT_TYPE, h_ct);
                     Ok(res)
                 })
             }
-            _ => Box::pin(ok(Response::new(http::StatusCode::NOT_FOUND))),
+
+            _ => Box::pin(ok(Response::with_body(
+                http::StatusCode::NOT_FOUND,
+                Bytes::new(),
+            ))),
         }
     }
 }
@@ -123,14 +144,13 @@ impl Service for App {
 #[derive(Clone)]
 struct AppFactory;
 
-impl ServiceFactory for AppFactory {
+impl ServiceFactory<Request> for AppFactory {
     type Config = ();
-    type Request = Request;
-    type Response = Response;
+    type Response = Response<Bytes>;
     type Error = Error;
     type Service = App;
     type InitError = ();
-    type Future = Pin<Box<dyn Future<Output = Result<Self::Service, Self::InitError>>>>;
+    type Future = LocalBoxFuture<'static, Result<Self::Service, Self::InitError>>;
 
     fn new_service(&self, _: ()) -> Self::Future {
         const DB_URL: &str =
@@ -149,19 +169,18 @@ impl ServiceFactory for AppFactory {
 }
 
 fn main() -> std::io::Result<()> {
-    let sys = actix_rt::System::builder().stop_on_panic(false).build();
-
-    Server::build()
-        .backlog(1024)
-        .bind("techempower", "0.0.0.0:8080", || {
-            HttpService::build()
-                .keep_alive(KeepAlive::Os)
-                .client_timeout(0)
-                .h1(AppFactory)
-                .tcp()
-        })?
-        .start();
-
-    println!("Started http server: 127.0.0.1:8080");
-    sys.run()
+    println!("Starting HTTP server on http://127.0.0.1:8080");
+
+    actix_rt::System::new().block_on(
+        Server::build()
+            .backlog(1024)
+            .bind("tfb-actix-http", "0.0.0.0:8080", || {
+                HttpService::build()
+                    .keep_alive(KeepAlive::Os)
+                    .client_request_timeout(Duration::ZERO)
+                    .h1(AppFactory)
+                    .tcp()
+            })?
+            .run(),
+    )
 }

+ 0 - 144
frameworks/Rust/actix/src/main_pg.rs

@@ -1,144 +0,0 @@
-#[global_allocator]
-static ALLOC: snmalloc_rs::SnMalloc = snmalloc_rs::SnMalloc;
-
-use actix::prelude::*;
-use actix_http::error::ErrorInternalServerError;
-use actix_http::{HttpService, KeepAlive};
-use actix_service::map_config;
-use actix_web::dev::{AppConfig, Body, Server};
-use actix_web::http::{header::CONTENT_TYPE, header::SERVER, HeaderValue, StatusCode};
-use actix_web::{web, App, Error, HttpRequest, HttpResponse};
-use bytes::{Bytes, BytesMut};
-use yarte::ywrite_html;
-
-mod db_pg;
-mod models;
-mod utils;
-use crate::db_pg::{PgConnection, RandomWorld, RandomWorlds, TellFortune, UpdateWorld};
-use crate::utils::Writer;
-
-async fn world_row(db: web::Data<Addr<PgConnection>>) -> Result<HttpResponse, Error> {
-    let res = db
-        .send(RandomWorld)
-        .await
-        .map_err(|e| ErrorInternalServerError(e))?;
-    match res {
-        Ok(body) => {
-            let mut res = HttpResponse::with_body(StatusCode::OK, Body::Bytes(body));
-            res.headers_mut()
-                .insert(SERVER, HeaderValue::from_static("Actix"));
-            res.headers_mut()
-                .insert(CONTENT_TYPE, HeaderValue::from_static("application/json"));
-            Ok(res)
-        }
-        Err(_) => Ok(HttpResponse::InternalServerError().into()),
-    }
-}
-
-async fn queries(
-    req: HttpRequest,
-    db: web::Data<Addr<PgConnection>>,
-) -> Result<HttpResponse, Error> {
-    // get queries parameter
-    let q = utils::get_query_param(req.query_string());
-
-    // run sql queries
-    let res = db
-        .send(RandomWorlds(q))
-        .await
-        .map_err(|e| ErrorInternalServerError(e))?;
-    if let Ok(worlds) = res {
-        let mut body = BytesMut::with_capacity(35 * worlds.len());
-        serde_json::to_writer(Writer(&mut body), &worlds).unwrap();
-        let mut res =
-            HttpResponse::with_body(StatusCode::OK, Body::Bytes(body.freeze()));
-        res.headers_mut()
-            .insert(SERVER, HeaderValue::from_static("Actix"));
-        res.headers_mut()
-            .insert(CONTENT_TYPE, HeaderValue::from_static("application/json"));
-        Ok(res)
-    } else {
-        Ok(HttpResponse::InternalServerError().into())
-    }
-}
-
-async fn updates(
-    req: HttpRequest,
-    db: web::Data<Addr<PgConnection>>,
-) -> Result<HttpResponse, Error> {
-    // get queries parameter
-    let q = utils::get_query_param(req.query_string());
-
-    // update db
-    let res = db
-        .send(UpdateWorld(q))
-        .await
-        .map_err(|e| ErrorInternalServerError(e))?;
-    if let Ok(worlds) = res {
-        let mut body = BytesMut::with_capacity(35 * worlds.len());
-        serde_json::to_writer(Writer(&mut body), &worlds).unwrap();
-        let mut res =
-            HttpResponse::with_body(StatusCode::OK, Body::Bytes(body.freeze()));
-        res.headers_mut()
-            .insert(SERVER, HeaderValue::from_static("Actix"));
-        res.headers_mut()
-            .insert(CONTENT_TYPE, HeaderValue::from_static("application/json"));
-        Ok(res)
-    } else {
-        Ok(HttpResponse::InternalServerError().into())
-    }
-}
-
-async fn fortune(db: web::Data<Addr<PgConnection>>) -> Result<HttpResponse, Error> {
-    let res = db
-        .send(TellFortune)
-        .await
-        .map_err(|e| ErrorInternalServerError(e))?;
-
-    match res {
-        Ok(fortunes) => {
-            let mut body = Vec::with_capacity(2048);
-            ywrite_html!(body, "{{> fortune }}");
-
-            let mut res =
-                HttpResponse::with_body(StatusCode::OK, Body::Bytes(Bytes::from(body)));
-            res.headers_mut()
-                .insert(SERVER, HeaderValue::from_static("Actix"));
-            res.headers_mut().insert(
-                CONTENT_TYPE,
-                HeaderValue::from_static("text/html; charset=utf-8"),
-            );
-            Ok(res)
-        }
-        Err(_) => Ok(HttpResponse::InternalServerError().into()),
-    }
-}
-
-#[actix_web::main]
-async fn main() -> std::io::Result<()> {
-    println!("Started http server: 127.0.0.1:8080");
-
-    const DB_URL: &str =
-        "postgres://benchmarkdbuser:benchmarkdbpass@tfb-database/hello_world";
-
-    // start http server
-    Server::build()
-        .backlog(1024)
-        .bind("techempower", "0.0.0.0:8080", move || {
-            HttpService::build()
-                .keep_alive(KeepAlive::Os)
-                .client_timeout(0)
-                .h1(map_config(
-                    App::new()
-                        .data_factory(|| PgConnection::connect(DB_URL))
-                        .service(web::resource("/db").to(world_row))
-                        .service(web::resource("/queries").to(queries))
-                        .service(web::resource("/fortunes").to(fortune))
-                        .service(web::resource("/updates").to(updates)),
-                    |_| AppConfig::default(),
-                ))
-                .tcp()
-        })?
-        .start()
-        .await
-}

+ 41 - 24
frameworks/Rust/actix/src/main_raw.rs → frameworks/Rust/actix/src/main_server.rs

@@ -6,24 +6,27 @@ use std::io;
 use std::pin::Pin;
 use std::task::{Context, Poll};
 
-use actix_codec::{AsyncRead, AsyncWrite, Decoder};
+use actix_codec::{AsyncWrite, Decoder};
 use actix_http::{h1, Request};
 use actix_rt::net::TcpStream;
 use actix_server::Server;
 use actix_service::fn_service;
 use bytes::{Buf, BufMut, BytesMut};
 use simd_json_derive::Serialize;
+use tokio::io::{AsyncBufRead, BufReader};
 
 mod models;
 mod utils;
 
 use crate::utils::Writer;
 
-const JSON: &[u8] = b"HTTP/1.1 200 OK\r\nServer: A\r\nContent-Type: application/json\r\nContent-Length: 27\r\n";
-const PLAIN: &[u8] = b"HTTP/1.1 200 OK\r\nServer: A\r\nContent-Type: text/plain\r\nContent-Length: 13\r\n";
-const HTTPNFOUND: &[u8] = b"HTTP/1.1 400 OK\r\n";
-const HDR_SERVER: &[u8] = b"Server: A\r\n";
-const BODY: &[u8] = b"Hello, World!";
+const HEAD_JSON: &[u8] =
+    b"HTTP/1.1 200 OK\r\nServer: A\r\nContent-Type: application/json\r\nContent-Length: 27\r\n";
+const HEAD_PLAIN: &[u8] =
+    b"HTTP/1.1 200 OK\r\nServer: A\r\nContent-Type: text/plain\r\nContent-Length: 13\r\n";
+const HEAD_NOT_FOUND: &[u8] = b"HTTP/1.1 204 OK\r\nServer: A\r\n";
+const BODY_PLAIN: &[u8] = b"Hello, World!";
+const HDR_END: &[u8] = b"\r\n";
 
 #[derive(Serialize)]
 pub struct Message {
@@ -31,7 +34,7 @@ pub struct Message {
 }
 
 struct App {
-    io: TcpStream,
+    io: BufReader<TcpStream>,
     read_buf: BytesMut,
     write_buf: BytesMut,
     codec: h1::Codec,
@@ -44,20 +47,28 @@ impl App {
                 let message = Message {
                     message: "Hello, World!",
                 };
-                self.write_buf.put_slice(JSON);
-                self.codec.config().set_date(&mut self.write_buf);
+                self.write_buf.put_slice(HEAD_JSON);
+                self.codec
+                    .config()
+                    .write_date_header(&mut self.write_buf, false);
+                self.write_buf.put_slice(HDR_END);
                 message
                     .json_write(&mut Writer(&mut self.write_buf))
                     .unwrap();
             }
+
             "/plaintext" => {
-                self.write_buf.put_slice(PLAIN);
-                self.codec.config().set_date(&mut self.write_buf);
-                self.write_buf.put_slice(BODY);
+                self.write_buf.put_slice(HEAD_PLAIN);
+                self.codec
+                    .config()
+                    .write_date_header(&mut self.write_buf, false);
+                self.write_buf.put_slice(HDR_END);
+                self.write_buf.put_slice(BODY_PLAIN);
             }
+
             _ => {
-                self.write_buf.put_slice(HTTPNFOUND);
-                self.write_buf.put_slice(HDR_SERVER);
+                self.write_buf.put_slice(HEAD_NOT_FOUND);
+                self.write_buf.put_slice(HDR_END);
             }
         }
     }
@@ -73,16 +84,21 @@ impl Future for App {
             if this.read_buf.capacity() - this.read_buf.len() < 512 {
                 this.read_buf.reserve(32_768);
             }
-            let read = Pin::new(&mut this.io).poll_read_buf(cx, &mut this.read_buf);
-            match read {
+
+            let n = match Pin::new(&mut this.io).poll_fill_buf(cx) {
                 Poll::Pending => break,
-                Poll::Ready(Ok(n)) => {
-                    if n == 0 {
+                Poll::Ready(Ok(filled)) => {
+                    if filled.is_empty() {
                         return Poll::Ready(Ok(()));
                     }
+
+                    this.read_buf.extend_from_slice(filled);
+                    filled.len()
                 }
                 Poll::Ready(Err(_)) => return Poll::Ready(Err(())),
-            }
+            };
+
+            Pin::new(&mut this.io).consume(n);
         }
 
         if this.write_buf.capacity() - this.write_buf.len() <= 512 {
@@ -90,7 +106,7 @@ impl Future for App {
         }
 
         loop {
-            match this.codec.decode(&mut Pin::new(&mut this.read_buf)) {
+            match this.codec.decode(&mut this.read_buf) {
                 Ok(Some(h1::Message::Item(req))) => this.handle_request(req),
                 Ok(None) => break,
                 _ => return Poll::Ready(Err(())),
@@ -115,6 +131,7 @@ impl Future for App {
                     Poll::Ready(Err(_)) => return Poll::Ready(Err(())),
                 }
             }
+
             if written == len {
                 unsafe { this.write_buf.set_len(0) }
             } else if written > 0 {
@@ -127,19 +144,19 @@ impl Future for App {
 
 #[actix_web::main]
 async fn main() -> io::Result<()> {
-    println!("Started http server: 127.0.0.1:8080");
+    println!("Started HTTP server: 127.0.0.1:8080");
 
     // start http server
     Server::build()
         .backlog(1024)
-        .bind("techempower", "0.0.0.0:8080", || {
+        .bind("tfb-actix-server", "0.0.0.0:8080", || {
             fn_service(|io: TcpStream| App {
-                io,
+                io: BufReader::new(io),
                 read_buf: BytesMut::with_capacity(32_768),
                 write_buf: BytesMut::with_capacity(32_768),
                 codec: h1::Codec::default(),
             })
         })?
-        .start()
+        .run()
         .await
 }

+ 22 - 18
frameworks/Rust/actix/src/main.rs → frameworks/Rust/actix/src/main_web.rs

@@ -1,31 +1,38 @@
 #[global_allocator]
 static ALLOC: snmalloc_rs::SnMalloc = snmalloc_rs::SnMalloc;
 
+use std::time::Duration;
+
 use actix_http::{HttpService, KeepAlive};
 use actix_service::map_config;
-use actix_web::dev::{AppConfig, Body, Server};
-use actix_web::http::header::{CONTENT_TYPE, SERVER};
-use actix_web::http::{HeaderValue, StatusCode};
-use actix_web::{web, App, HttpResponse};
-use bytes::{Bytes, BytesMut};
+use actix_web::{
+    dev::{AppConfig, Server},
+    http::{
+        header::{HeaderValue, CONTENT_TYPE, SERVER},
+        StatusCode,
+    },
+    web::{self, Bytes, BytesMut},
+    App, HttpResponse,
+};
 use simd_json_derive::Serialize;
 
 mod utils;
-use utils::{Writer, SIZE};
+use utils::{Writer, JSON_MSG_SIZE};
 
 #[derive(Serialize)]
 pub struct Message {
     pub message: &'static str,
 }
 
-async fn json() -> HttpResponse {
+async fn json() -> HttpResponse<Bytes> {
     let message = Message {
         message: "Hello, World!",
     };
-    let mut body = BytesMut::with_capacity(SIZE);
+
+    let mut body = BytesMut::with_capacity(JSON_MSG_SIZE);
     message.json_write(&mut Writer(&mut body)).unwrap();
 
-    let mut res = HttpResponse::with_body(StatusCode::OK, Body::Bytes(body.freeze()));
+    let mut res = HttpResponse::with_body(StatusCode::OK, body.freeze());
     res.headers_mut()
         .insert(SERVER, HeaderValue::from_static("A"));
     res.headers_mut()
@@ -33,11 +40,8 @@ async fn json() -> HttpResponse {
     res
 }
 
-async fn plaintext() -> HttpResponse {
-    let mut res = HttpResponse::with_body(
-        StatusCode::OK,
-        Body::Bytes(Bytes::from_static(b"Hello, World!")),
-    );
+async fn plaintext() -> HttpResponse<Bytes> {
+    let mut res = HttpResponse::with_body(StatusCode::OK, Bytes::from_static(b"Hello, World!"));
     res.headers_mut()
         .insert(SERVER, HeaderValue::from_static("A"));
     res.headers_mut()
@@ -47,15 +51,15 @@ async fn plaintext() -> HttpResponse {
 
 #[actix_web::main]
 async fn main() -> std::io::Result<()> {
-    println!("Started http server: 127.0.0.1:8080");
+    println!("Started HTTP server: 127.0.0.1:8080");
 
     // start http server
     Server::build()
         .backlog(1024)
-        .bind("techempower", "0.0.0.0:8080", || {
+        .bind("tfb-actix-web", "0.0.0.0:8080", || {
             HttpService::build()
                 .keep_alive(KeepAlive::Os)
-                .client_timeout(0)
+                .client_request_timeout(Duration::ZERO)
                 .h1(map_config(
                     App::new()
                         .service(web::resource("/json").to(json))
@@ -64,6 +68,6 @@ async fn main() -> std::io::Result<()> {
                 ))
                 .tcp()
         })?
-        .start()
+        .run()
         .await
 }

+ 35 - 31
frameworks/Rust/actix/src/main_diesel.rs → frameworks/Rust/actix/src/main_web_diesel.rs

@@ -5,31 +5,34 @@ static ALLOC: snmalloc_rs::SnMalloc = snmalloc_rs::SnMalloc;
 extern crate diesel;
 
 use actix::prelude::*;
-use actix_http::error::ErrorInternalServerError;
-use actix_web::{http, web, App, Error, HttpRequest, HttpResponse, HttpServer};
+use actix_web::{
+    error,
+    http::{self, header::ContentType},
+    web, App, Error, HttpRequest, HttpResponse, HttpServer,
+};
 use askama::Template;
 use bytes::BytesMut;
 
-mod db;
+mod db_diesel;
 mod models;
 mod schema;
 mod utils;
 
 use utils::Writer;
 
-async fn world_row(db: web::Data<Addr<db::DbExecutor>>) -> Result<HttpResponse, Error> {
+async fn world_row(db: web::Data<Addr<db_diesel::DbExecutor>>) -> Result<HttpResponse, Error> {
     let res = db
-        .send(db::RandomWorld)
+        .send(db_diesel::RandomWorld)
         .await
-        .map_err(|e| ErrorInternalServerError(e))?;
+        .map_err(error::ErrorInternalServerError)?;
 
     match res {
         Ok(row) => {
             let mut body = BytesMut::with_capacity(33);
             serde_json::to_writer(Writer(&mut body), &row).unwrap();
             Ok(HttpResponse::Ok()
-                .header(http::header::SERVER, "Actix")
-                .header(http::header::CONTENT_TYPE, "application/json")
+                .insert_header((http::header::SERVER, "Actix"))
+                .insert_header((http::header::CONTENT_TYPE, ContentType::json()))
                 .body(body))
         }
         Err(_) => Ok(HttpResponse::InternalServerError().into()),
@@ -38,22 +41,22 @@ async fn world_row(db: web::Data<Addr<db::DbExecutor>>) -> Result<HttpResponse,
 
 async fn queries(
     req: HttpRequest,
-    db: web::Data<Addr<db::DbExecutor>>,
+    db: web::Data<Addr<db_diesel::DbExecutor>>,
 ) -> Result<HttpResponse, Error> {
     // get queries parameter
     let q = utils::get_query_param(req.query_string());
 
-    // run sql queries
+    // run SQL queries
     let res = db
-        .send(db::RandomWorlds(q))
+        .send(db_diesel::RandomWorlds(q))
         .await
-        .map_err(|e| ErrorInternalServerError(e))?;
+        .map_err(error::ErrorInternalServerError)?;
     if let Ok(worlds) = res {
         let mut body = BytesMut::with_capacity(35 * worlds.len());
         serde_json::to_writer(Writer(&mut body), &worlds).unwrap();
         Ok(HttpResponse::Ok()
-            .header(http::header::SERVER, "Actix")
-            .header(http::header::CONTENT_TYPE, "application/json")
+            .insert_header((http::header::SERVER, "Actix"))
+            .insert_header((http::header::CONTENT_TYPE, ContentType::json()))
             .body(body))
     } else {
         Ok(HttpResponse::InternalServerError().into())
@@ -62,23 +65,23 @@ async fn queries(
 
 async fn updates(
     req: HttpRequest,
-    db: web::Data<Addr<db::DbExecutor>>,
+    db: web::Data<Addr<db_diesel::DbExecutor>>,
 ) -> Result<HttpResponse, Error> {
     // get queries parameter
     let q = utils::get_query_param(req.query_string());
 
     // update worlds
     let res = db
-        .send(db::UpdateWorld(q))
+        .send(db_diesel::UpdateWorld(q))
         .await
-        .map_err(|e| ErrorInternalServerError(e))?;
+        .map_err(error::ErrorInternalServerError)?;
 
     if let Ok(worlds) = res {
         let mut body = BytesMut::with_capacity(35 * worlds.len());
         serde_json::to_writer(Writer(&mut body), &worlds).unwrap();
         Ok(HttpResponse::Ok()
-            .header(http::header::SERVER, "Actix")
-            .header(http::header::CONTENT_TYPE, "application/json")
+            .insert_header((http::header::SERVER, "Actix"))
+            .insert_header((http::header::CONTENT_TYPE, ContentType::json()))
             .body(body))
     } else {
         Ok(HttpResponse::InternalServerError().into())
@@ -91,19 +94,19 @@ struct FortuneTemplate<'a> {
     items: &'a Vec<models::Fortune>,
 }
 
-async fn fortune(db: web::Data<Addr<db::DbExecutor>>) -> Result<HttpResponse, Error> {
+async fn fortune(db: web::Data<Addr<db_diesel::DbExecutor>>) -> Result<HttpResponse, Error> {
     let res = db
-        .send(db::TellFortune)
+        .send(db_diesel::TellFortune)
         .await
-        .map_err(|e| ErrorInternalServerError(e))?;
+        .map_err(error::ErrorInternalServerError)?;
     match res {
         Ok(rows) => {
             let tmpl = FortuneTemplate { items: &rows };
             let res = tmpl.render().unwrap();
 
             Ok(HttpResponse::Ok()
-                .header(http::header::SERVER, "Actix")
-                .header(http::header::CONTENT_TYPE, "text/html; charset=utf-8")
+                .insert_header((http::header::SERVER, "Actix"))
+                .insert_header((http::header::CONTENT_TYPE, ContentType::html()))
                 .body(res))
         }
         Err(_) => Ok(HttpResponse::InternalServerError().into()),
@@ -112,18 +115,19 @@ async fn fortune(db: web::Data<Addr<db::DbExecutor>>) -> Result<HttpResponse, Er
 
 #[actix_web::main]
 async fn main() -> std::io::Result<()> {
-    println!("Starting http server: 127.0.0.1:8080");
-
     let db_url = "postgres://benchmarkdbuser:benchmarkdbpass@tfb-database/hello_world";
 
-    // Start db executor actors
-    let addr =
-        SyncArbiter::start(num_cpus::get() * 3, move || db::DbExecutor::new(db_url));
+    // start DB executor actors
+    let addr = SyncArbiter::start(num_cpus::get() * 3, move || {
+        db_diesel::DbExecutor::new(db_url)
+    });
+
+    println!("Starting HTTP server: 127.0.0.1:8080");
 
-    // start http server
+    // start HTTP server
     HttpServer::new(move || {
         App::new()
-            .data(addr.clone())
+            .app_data(web::Data::new(addr.clone()))
             .service(web::resource("/db").to(world_row))
             .service(web::resource("/fortunes").to(fortune))
             .service(web::resource("/queries").to(queries))

+ 7 - 0
frameworks/Rust/actix/src/models.rs

@@ -13,6 +13,13 @@ pub struct World {
     pub randomnumber: i32,
 }
 
+impl World {
+    #[allow(dead_code)]
+    pub fn new(id: i32, randomnumber: i32) -> Self {
+        Self { id, randomnumber }
+    }
+}
+
 #[allow(non_snake_case)]
 #[derive(Serialize, Queryable, Debug)]
 pub struct Fortune {

+ 11 - 6
frameworks/Rust/actix/src/utils.rs

@@ -1,9 +1,8 @@
 #![allow(dead_code, unused_braces)]
 
-use std::borrow::Cow;
-use std::{cmp, io};
+use std::{borrow::Cow, cmp, io};
 
-use bytes::{BufMut, BytesMut};
+use bytes::BufMut;
 use serde::{Deserialize, Serialize};
 
 #[derive(Serialize, Debug)]
@@ -12,16 +11,22 @@ pub struct Fortune {
     pub message: Cow<'static, str>,
 }
 
-pub const SIZE: usize = 27;
+pub const JSON_MSG_SIZE: usize = 27;
 
 #[derive(Serialize, Deserialize)]
 pub struct Message {
     pub message: &'static str,
 }
 
-pub struct Writer<'a>(pub &'a mut BytesMut);
+/// An `io::Write`r that only requires mutable reference and assumes that there is space available
+/// in the buffer for every write operation or that it can be extended implicitly (like
+/// `bytes::BytesMut`, for example).
+///
+/// This is slightly faster (~10%) than `bytes::buf::Writer` in such cases because it does not
+/// perform a remaining length check before writing.
+pub struct Writer<'a, B>(pub &'a mut B);
 
-impl<'a> io::Write for Writer<'a> {
+impl<'a, B: BufMut> io::Write for Writer<'a, B> {
     fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
         self.0.put_slice(buf);
         Ok(buf.len())

+ 71 - 57
frameworks/Rust/xitca-web/Cargo.lock

@@ -146,22 +146,33 @@ dependencies = [
 
 [[package]]
 name = "diesel"
-version = "1.4.8"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b28135ecf6b7d446b43e27e225622a038cc4e2930a1022f51cdb97ada19b8e4d"
+version = "2.0.0"
+source = "git+https://github.com/diesel-rs/diesel.git?rev=37ec18f46ced2d6e9197414156fdb705d7a61426#37ec18f46ced2d6e9197414156fdb705d7a61426"
 dependencies = [
  "bitflags",
  "byteorder",
  "diesel_derives",
- "pq-sys",
+ "itoa",
+]
+
+[[package]]
+name = "diesel-async"
+version = "0.1.0"
+source = "git+https://github.com/weiznich/diesel_async.git?rev=06b3416826dbc8ce404f6d613daea989b23549ca#06b3416826dbc8ce404f6d613daea989b23549ca"
+dependencies = [
+ "async-trait",
+ "diesel",
+ "futures",
+ "tokio",
+ "tokio-postgres",
 ]
 
 [[package]]
 name = "diesel_derives"
-version = "1.4.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "45f5098f628d02a7a0f68ddba586fb61e80edec3bdc1be3b921f4ceec60858d3"
+version = "2.0.0"
+source = "git+https://github.com/diesel-rs/diesel.git?rev=37ec18f46ced2d6e9197414156fdb705d7a61426#37ec18f46ced2d6e9197414156fdb705d7a61426"
 dependencies = [
+ "proc-macro-error",
  "proc-macro2",
  "quote",
  "syn",
@@ -226,9 +237,9 @@ dependencies = [
 
 [[package]]
 name = "futures-channel"
-version = "0.3.17"
+version = "0.3.19"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5da6ba8c3bb3c165d3c7319fc1cc8304facf1fb8db99c5de877183c08a273888"
+checksum = "ba3dda0b6588335f360afc675d0564c17a77a2bda81ca178a4b6081bd86c7f0b"
 dependencies = [
  "futures-core",
  "futures-sink",
@@ -236,9 +247,9 @@ dependencies = [
 
 [[package]]
 name = "futures-core"
-version = "0.3.17"
+version = "0.3.19"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "88d1c26957f23603395cd326b0ffe64124b818f4449552f960d815cfba83a53d"
+checksum = "d0c8ff0461b82559810cdccfde3215c3f373807f5e5232b71479bff7bb2583d7"
 
 [[package]]
 name = "futures-executor"
@@ -253,18 +264,16 @@ dependencies = [
 
 [[package]]
 name = "futures-io"
-version = "0.3.17"
+version = "0.3.19"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "522de2a0fe3e380f1bc577ba0474108faf3f6b18321dbf60b3b9c39a75073377"
+checksum = "b1f9d34af5a1aac6fb380f735fe510746c38067c5bf16c7fd250280503c971b2"
 
 [[package]]
 name = "futures-macro"
-version = "0.3.17"
+version = "0.3.19"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "18e4a4b95cea4b4ccbcf1c5675ca7c4ee4e9e75eb79944d07defde18068f79bb"
+checksum = "6dbd947adfffb0efc70599b3ddcf7b5597bb5fa9e245eb99f62b3a5f7bb8bd3c"
 dependencies = [
- "autocfg",
- "proc-macro-hack",
  "proc-macro2",
  "quote",
  "syn",
@@ -272,23 +281,22 @@ dependencies = [
 
 [[package]]
 name = "futures-sink"
-version = "0.3.17"
+version = "0.3.19"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "36ea153c13024fe480590b3e3d4cad89a0cfacecc24577b68f86c6ced9c2bc11"
+checksum = "e3055baccb68d74ff6480350f8d6eb8fcfa3aa11bdc1a1ae3afdd0514617d508"
 
 [[package]]
 name = "futures-task"
-version = "0.3.17"
+version = "0.3.19"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1d3d00f4eddb73e498a54394f228cd55853bdf059259e8e7bc6e69d408892e99"
+checksum = "6ee7c6485c30167ce4dfb83ac568a849fe53274c831081476ee13e0dce1aad72"
 
 [[package]]
 name = "futures-util"
-version = "0.3.17"
+version = "0.3.19"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "36568465210a3a6ee45e1f165136d68671471a501e632e9a98d96872222b5481"
+checksum = "d9b5cf40b47a271f77a8b1bec03ca09044d99d2372c0de244e66430761127164"
 dependencies = [
- "autocfg",
  "futures-channel",
  "futures-core",
  "futures-io",
@@ -298,8 +306,6 @@ dependencies = [
  "memchr",
  "pin-project-lite",
  "pin-utils",
- "proc-macro-hack",
- "proc-macro-nested",
  "slab",
 ]
 
@@ -470,6 +476,12 @@ dependencies = [
  "cfg-if",
 ]
 
+[[package]]
+name = "matchit"
+version = "0.4.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9376a4f0340565ad675d11fc1419227faf5f60cd7ac9cb2e7185a471f30af833"
+
 [[package]]
 name = "md-5"
 version = "0.9.1"
@@ -655,31 +667,40 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ed0cfbc8191465bed66e1718596ee0b0b35d5ee1f41c5df2189d0fe8bde535ba"
 
 [[package]]
-name = "pq-sys"
-version = "0.4.6"
+name = "proc-macro-error"
+version = "1.0.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6ac25eee5a0582f45a67e837e350d784e7003bd29a5f460796772061ca49ffda"
+checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
 dependencies = [
- "vcpkg",
+ "proc-macro-error-attr",
+ "proc-macro2",
+ "quote",
+ "syn",
+ "version_check",
 ]
 
 [[package]]
-name = "proc-macro-hack"
-version = "0.5.19"
+name = "proc-macro-error-attr"
+version = "1.0.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5"
+checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "version_check",
+]
 
 [[package]]
-name = "proc-macro-nested"
-version = "0.1.7"
+name = "proc-macro-hack"
+version = "0.5.19"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bc881b2c22681370c6a780e47af9840ef841837bc98118431d4e1868bd0c1086"
+checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5"
 
 [[package]]
 name = "proc-macro2"
-version = "1.0.26"
+version = "1.0.36"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a152013215dca273577e18d2bf00fa862b89b24169fb78c4c95aeb07992c9cec"
+checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029"
 dependencies = [
  "unicode-xid",
 ]
@@ -751,8 +772,7 @@ checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e"
 [[package]]
 name = "sailfish"
 version = "0.3.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "816920a08514d9741242b3efe70c16c350ed548bc4a5ba03426e56faf9d45f77"
+source = "git+https://github.com/jdrouet/sailfish?rev=7d2b59247eaab10b67311d6c1c7d50a7d751d791#7d2b59247eaab10b67311d6c1c7d50a7d751d791"
 dependencies = [
  "itoap",
  "ryu",
@@ -763,8 +783,7 @@ dependencies = [
 [[package]]
 name = "sailfish-compiler"
 version = "0.3.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4276e7b848bde8e7813d534f014bc35ce5acd2b9e2b6b075727113fcf478ba63"
+source = "git+https://github.com/jdrouet/sailfish?rev=7d2b59247eaab10b67311d6c1c7d50a7d751d791#7d2b59247eaab10b67311d6c1c7d50a7d751d791"
 dependencies = [
  "filetime",
  "home",
@@ -778,8 +797,7 @@ dependencies = [
 [[package]]
 name = "sailfish-macros"
 version = "0.3.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6bba2458ef07ae12c9aed2edb866c3db2f9c21cf19a2c3f2613b2982bc1a4a46"
+source = "git+https://github.com/jdrouet/sailfish?rev=7d2b59247eaab10b67311d6c1c7d50a7d751d791#7d2b59247eaab10b67311d6c1c7d50a7d751d791"
 dependencies = [
  "proc-macro2",
  "sailfish-compiler",
@@ -1072,12 +1090,6 @@ dependencies = [
  "ryu",
 ]
 
-[[package]]
-name = "vcpkg"
-version = "0.2.15"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
-
 [[package]]
 name = "version_check"
 version = "0.9.3"
@@ -1127,13 +1139,14 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
 [[package]]
 name = "xitca-http"
 version = "0.1.0"
-source = "git+https://github.com/fakeshadow/xitca-web.git?rev=cce3eee576a9b303a71d4654a6c922402b5fd4f3#cce3eee576a9b303a71d4654a6c922402b5fd4f3"
+source = "git+https://github.com/fakeshadow/xitca-web.git?rev=59827177f6c319c6fa9940fe5f146754fff90aad#59827177f6c319c6fa9940fe5f146754fff90aad"
 dependencies = [
  "futures-core",
  "http",
  "httparse",
  "httpdate",
  "itoa",
+ "matchit",
  "pin-project-lite",
  "socket2",
  "tokio",
@@ -1145,7 +1158,7 @@ dependencies = [
 [[package]]
 name = "xitca-http-codegen"
 version = "0.1.0"
-source = "git+https://github.com/fakeshadow/xitca-web.git?rev=cce3eee576a9b303a71d4654a6c922402b5fd4f3#cce3eee576a9b303a71d4654a6c922402b5fd4f3"
+source = "git+https://github.com/fakeshadow/xitca-web.git?rev=59827177f6c319c6fa9940fe5f146754fff90aad#59827177f6c319c6fa9940fe5f146754fff90aad"
 dependencies = [
  "quote",
  "syn",
@@ -1154,7 +1167,7 @@ dependencies = [
 [[package]]
 name = "xitca-io"
 version = "0.1.0"
-source = "git+https://github.com/fakeshadow/xitca-web.git?rev=cce3eee576a9b303a71d4654a6c922402b5fd4f3#cce3eee576a9b303a71d4654a6c922402b5fd4f3"
+source = "git+https://github.com/fakeshadow/xitca-web.git?rev=59827177f6c319c6fa9940fe5f146754fff90aad#59827177f6c319c6fa9940fe5f146754fff90aad"
 dependencies = [
  "bytes",
  "tokio",
@@ -1163,7 +1176,7 @@ dependencies = [
 [[package]]
 name = "xitca-server"
 version = "0.1.0"
-source = "git+https://github.com/fakeshadow/xitca-web.git?rev=cce3eee576a9b303a71d4654a6c922402b5fd4f3#cce3eee576a9b303a71d4654a6c922402b5fd4f3"
+source = "git+https://github.com/fakeshadow/xitca-web.git?rev=59827177f6c319c6fa9940fe5f146754fff90aad#59827177f6c319c6fa9940fe5f146754fff90aad"
 dependencies = [
  "futures-core",
  "num_cpus",
@@ -1176,7 +1189,7 @@ dependencies = [
 [[package]]
 name = "xitca-service"
 version = "0.1.0"
-source = "git+https://github.com/fakeshadow/xitca-web.git?rev=cce3eee576a9b303a71d4654a6c922402b5fd4f3#cce3eee576a9b303a71d4654a6c922402b5fd4f3"
+source = "git+https://github.com/fakeshadow/xitca-web.git?rev=59827177f6c319c6fa9940fe5f146754fff90aad#59827177f6c319c6fa9940fe5f146754fff90aad"
 
 [[package]]
 name = "xitca-web"
@@ -1186,6 +1199,7 @@ dependencies = [
  "atoi",
  "core_affinity",
  "diesel",
+ "diesel-async",
  "futures-util",
  "mimalloc",
  "rand",
@@ -1199,13 +1213,13 @@ dependencies = [
  "xitca-http-codegen",
  "xitca-server",
  "xitca-service",
- "xitca-web 0.1.0 (git+https://github.com/fakeshadow/xitca-web.git?rev=cce3eee576a9b303a71d4654a6c922402b5fd4f3)",
+ "xitca-web 0.1.0 (git+https://github.com/fakeshadow/xitca-web.git?rev=59827177f6c319c6fa9940fe5f146754fff90aad)",
 ]
 
 [[package]]
 name = "xitca-web"
 version = "0.1.0"
-source = "git+https://github.com/fakeshadow/xitca-web.git?rev=cce3eee576a9b303a71d4654a6c922402b5fd4f3#cce3eee576a9b303a71d4654a6c922402b5fd4f3"
+source = "git+https://github.com/fakeshadow/xitca-web.git?rev=59827177f6c319c6fa9940fe5f146754fff90aad#59827177f6c319c6fa9940fe5f146754fff90aad"
 dependencies = [
  "futures-core",
  "xitca-http",

Some files were not shown because too many files changed in this diff