|
@@ -1,33 +1,81 @@
|
|
|
+use std::io;
|
|
|
+
|
|
|
+use actix::fut;
|
|
|
use actix::prelude::*;
|
|
|
-use postgres::{Connection, TlsMode};
|
|
|
+use futures::{stream, Future, Stream};
|
|
|
use rand::{thread_rng, Rng, ThreadRng};
|
|
|
-use std::io;
|
|
|
+use tokio_postgres::{connect, Client, Statement, TlsMode};
|
|
|
|
|
|
use models::{Fortune, World};
|
|
|
|
|
|
/// Postgres interface
|
|
|
pub struct PgConnection {
|
|
|
- conn: Connection,
|
|
|
+ cl: Option<Client>,
|
|
|
+ fortune: Option<Statement>,
|
|
|
+ world: Option<Statement>,
|
|
|
+ update: Option<Statement>,
|
|
|
rng: ThreadRng,
|
|
|
}
|
|
|
|
|
|
impl Actor for PgConnection {
|
|
|
- type Context = SyncContext<Self>;
|
|
|
+ type Context = Context<Self>;
|
|
|
}
|
|
|
|
|
|
impl PgConnection {
|
|
|
- pub fn new(db_url: &str) -> PgConnection {
|
|
|
- let conn = Connection::connect(db_url, TlsMode::None)
|
|
|
- .expect(&format!("Error connecting to {}", db_url));
|
|
|
- PgConnection {
|
|
|
- conn,
|
|
|
- rng: thread_rng(),
|
|
|
- }
|
|
|
+ pub fn connect(db_url: &str) -> Addr<PgConnection> {
|
|
|
+ let hs = connect(db_url.parse().unwrap(), TlsMode::None);
|
|
|
+
|
|
|
+ PgConnection::create(move |ctx| {
|
|
|
+ let act = PgConnection {
|
|
|
+ cl: None,
|
|
|
+ fortune: None,
|
|
|
+ world: None,
|
|
|
+ update: None,
|
|
|
+ rng: thread_rng(),
|
|
|
+ };
|
|
|
+
|
|
|
+ hs.map_err(|_| panic!("can not connect to postgresql"))
|
|
|
+ .into_actor(&act)
|
|
|
+ .and_then(|(mut cl, conn), act, ctx| {
|
|
|
+ ctx.wait(
|
|
|
+ cl.prepare("SELECT id, message FROM fortune")
|
|
|
+ .map_err(|_| ())
|
|
|
+ .into_actor(act)
|
|
|
+ .and_then(|st, act, _| {
|
|
|
+ act.fortune = Some(st);
|
|
|
+ fut::ok(())
|
|
|
+ }),
|
|
|
+ );
|
|
|
+ ctx.wait(
|
|
|
+ cl.prepare("SELECT id, randomnumber FROM world WHERE id=$1")
|
|
|
+ .map_err(|_| ())
|
|
|
+ .into_actor(act)
|
|
|
+ .and_then(|st, act, _| {
|
|
|
+ act.world = Some(st);
|
|
|
+ fut::ok(())
|
|
|
+ }),
|
|
|
+ );
|
|
|
+ ctx.wait(
|
|
|
+ cl.prepare("SELECT id FROM world WHERE id=$1")
|
|
|
+ .map_err(|_| ())
|
|
|
+ .into_actor(act)
|
|
|
+ .and_then(|st, act, _| {
|
|
|
+ act.update = Some(st);
|
|
|
+ fut::ok(())
|
|
|
+ }),
|
|
|
+ );
|
|
|
+
|
|
|
+ act.cl = Some(cl);
|
|
|
+ Arbiter::spawn(conn.map_err(|e| panic!("{}", e)));
|
|
|
+ fut::ok(())
|
|
|
+ })
|
|
|
+ .wait(ctx);
|
|
|
+
|
|
|
+ act
|
|
|
+ })
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-unsafe impl Send for PgConnection {}
|
|
|
-
|
|
|
pub struct RandomWorld;
|
|
|
|
|
|
impl Message for RandomWorld {
|
|
@@ -35,21 +83,26 @@ impl Message for RandomWorld {
|
|
|
}
|
|
|
|
|
|
impl Handler<RandomWorld> for PgConnection {
|
|
|
- type Result = io::Result<World>;
|
|
|
+ type Result = ResponseFuture<World, io::Error>;
|
|
|
|
|
|
fn handle(&mut self, _: RandomWorld, _: &mut Self::Context) -> Self::Result {
|
|
|
- let random_world = self
|
|
|
- .conn
|
|
|
- .prepare_cached("SELECT id, randomnumber FROM world WHERE id=$1")
|
|
|
- .unwrap();
|
|
|
-
|
|
|
let random_id = self.rng.gen_range::<i32>(1, 10_000);
|
|
|
- let rows = &random_world.query(&[&random_id]).unwrap();
|
|
|
- let row = rows.get(0);
|
|
|
- Ok(World {
|
|
|
- id: row.get(0),
|
|
|
- randomnumber: row.get(1),
|
|
|
- })
|
|
|
+
|
|
|
+ Box::new(
|
|
|
+ self.cl
|
|
|
+ .as_mut()
|
|
|
+ .unwrap()
|
|
|
+ .query(self.world.as_ref().unwrap(), &[&random_id])
|
|
|
+ .into_future()
|
|
|
+ .map_err(|e| io::Error::new(io::ErrorKind::Other, e.0))
|
|
|
+ .and_then(|(row, _)| {
|
|
|
+ let row = row.unwrap();
|
|
|
+ Ok(World {
|
|
|
+ id: row.get(0),
|
|
|
+ randomnumber: row.get(1),
|
|
|
+ })
|
|
|
+ }),
|
|
|
+ )
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -60,25 +113,30 @@ impl Message for RandomWorlds {
|
|
|
}
|
|
|
|
|
|
impl Handler<RandomWorlds> for PgConnection {
|
|
|
- type Result = io::Result<Vec<World>>;
|
|
|
+ type Result = ResponseFuture<Vec<World>, io::Error>;
|
|
|
|
|
|
fn handle(&mut self, msg: RandomWorlds, _: &mut Self::Context) -> Self::Result {
|
|
|
- let random_world = self
|
|
|
- .conn
|
|
|
- .prepare_cached("SELECT id, randomnumber FROM world WHERE id=$1")
|
|
|
- .unwrap();
|
|
|
-
|
|
|
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_000);
|
|
|
- let rows = &random_world.query(&[&w_id]).unwrap();
|
|
|
- let row = rows.get(0);
|
|
|
- worlds.push(World {
|
|
|
- id: row.get(0),
|
|
|
- randomnumber: row.get(1),
|
|
|
- });
|
|
|
+ worlds.push(
|
|
|
+ self.cl
|
|
|
+ .as_mut()
|
|
|
+ .unwrap()
|
|
|
+ .query(self.world.as_ref().unwrap(), &[&w_id])
|
|
|
+ .into_future()
|
|
|
+ .map_err(|e| io::Error::new(io::ErrorKind::Other, e.0))
|
|
|
+ .and_then(|(row, _)| {
|
|
|
+ let row = row.unwrap();
|
|
|
+ Ok(World {
|
|
|
+ id: row.get(0),
|
|
|
+ randomnumber: row.get(1),
|
|
|
+ })
|
|
|
+ }),
|
|
|
+ );
|
|
|
}
|
|
|
- Ok(worlds)
|
|
|
+
|
|
|
+ Box::new(stream::futures_unordered(worlds).collect())
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -89,36 +147,57 @@ impl Message for UpdateWorld {
|
|
|
}
|
|
|
|
|
|
impl Handler<UpdateWorld> for PgConnection {
|
|
|
- type Result = io::Result<Vec<World>>;
|
|
|
+ type Result = ResponseActFuture<Self, Vec<World>, io::Error>;
|
|
|
|
|
|
fn handle(&mut self, msg: UpdateWorld, _: &mut Self::Context) -> Self::Result {
|
|
|
- let get_world = self
|
|
|
- .conn
|
|
|
- .prepare_cached("SELECT id FROM world WHERE id=$1")
|
|
|
- .unwrap();
|
|
|
- let mut update = String::with_capacity(120 + 6 * msg.0 as usize);
|
|
|
- update
|
|
|
- .push_str("UPDATE world SET randomnumber = temp.randomnumber FROM (VALUES ");
|
|
|
-
|
|
|
let mut worlds = Vec::with_capacity(msg.0 as usize);
|
|
|
for _ in 0..msg.0 {
|
|
|
- let random_id = self.rng.gen_range::<i32>(1, 10_000);
|
|
|
- let rows = &get_world.query(&[&random_id]).unwrap();
|
|
|
- let w = World {
|
|
|
- id: rows.get(0).get(0),
|
|
|
- randomnumber: self.rng.gen_range(1, 10_000),
|
|
|
- };
|
|
|
- update.push_str(&format!("({}, {}),", w.id, w.randomnumber));
|
|
|
- worlds.push(w);
|
|
|
+ let id: i32 = self.rng.gen_range(1, 10_000);
|
|
|
+ let w_id: i32 = self.rng.gen_range(1, 10_000);
|
|
|
+ worlds.push(
|
|
|
+ self.cl
|
|
|
+ .as_mut()
|
|
|
+ .unwrap()
|
|
|
+ .query(self.update.as_ref().unwrap(), &[&w_id])
|
|
|
+ .into_future()
|
|
|
+ .map_err(|e| io::Error::new(io::ErrorKind::Other, e.0))
|
|
|
+ .and_then(move |(row, _)| {
|
|
|
+ let row = row.unwrap();
|
|
|
+ Ok(World {
|
|
|
+ id: row.get(0),
|
|
|
+ randomnumber: id,
|
|
|
+ })
|
|
|
+ }),
|
|
|
+ );
|
|
|
}
|
|
|
- worlds.sort_by_key(|w| w.id);
|
|
|
-
|
|
|
- update.pop();
|
|
|
- update
|
|
|
- .push_str(" ORDER BY 1) AS temp(id, randomnumber) WHERE temp.id = world.id");
|
|
|
- self.conn.execute(&update, &[]).unwrap();
|
|
|
|
|
|
- Ok(worlds)
|
|
|
+ Box::new(
|
|
|
+ stream::futures_unordered(worlds)
|
|
|
+ .collect()
|
|
|
+ .into_actor(self)
|
|
|
+ .and_then(move |mut worlds, act, _| {
|
|
|
+ let mut update = String::with_capacity(120 + 6 * msg.0 as usize);
|
|
|
+ update
|
|
|
+ .push_str("UPDATE world SET randomnumber = temp.randomnumber FROM (VALUES ");
|
|
|
+
|
|
|
+ for w in &worlds {
|
|
|
+ update.push_str(&format!("({}, {}),", w.id, w.randomnumber));
|
|
|
+ }
|
|
|
+ worlds.sort_by_key(|w| w.id);
|
|
|
+
|
|
|
+ update.pop();
|
|
|
+ update
|
|
|
+ .push_str(" ORDER BY 1) AS temp(id, randomnumber) WHERE temp.id = world.id");
|
|
|
+
|
|
|
+ act.cl
|
|
|
+ .as_mut()
|
|
|
+ .unwrap()
|
|
|
+ .batch_execute(&update)
|
|
|
+ .map_err(|e| io::Error::new(io::ErrorKind::Other, e))
|
|
|
+ .into_actor(act)
|
|
|
+ .and_then(|_, _, _| fut::ok(worlds))
|
|
|
+ }),
|
|
|
+ )
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -129,27 +208,32 @@ impl Message for TellFortune {
|
|
|
}
|
|
|
|
|
|
impl Handler<TellFortune> for PgConnection {
|
|
|
- type Result = io::Result<Vec<Fortune>>;
|
|
|
+ type Result = ResponseFuture<Vec<Fortune>, io::Error>;
|
|
|
|
|
|
fn handle(&mut self, _: TellFortune, _: &mut Self::Context) -> Self::Result {
|
|
|
- let fortune = self
|
|
|
- .conn
|
|
|
- .prepare_cached("SELECT id, message FROM fortune")
|
|
|
- .unwrap();
|
|
|
-
|
|
|
let mut items = Vec::with_capacity(16);
|
|
|
items.push(Fortune {
|
|
|
id: 0,
|
|
|
message: "Additional fortune added at request time.".to_string(),
|
|
|
});
|
|
|
|
|
|
- for row in &fortune.query(&[])? {
|
|
|
- items.push(Fortune {
|
|
|
- id: row.get(0),
|
|
|
- message: row.get(1),
|
|
|
- });
|
|
|
- }
|
|
|
- items.sort_by(|it, next| it.message.cmp(&next.message));
|
|
|
- Ok(items)
|
|
|
+ Box::new(
|
|
|
+ self.cl
|
|
|
+ .as_mut()
|
|
|
+ .unwrap()
|
|
|
+ .query(self.fortune.as_ref().unwrap(), &[])
|
|
|
+ .fold(items, move |mut items, row| {
|
|
|
+ items.push(Fortune {
|
|
|
+ id: row.get(0),
|
|
|
+ message: row.get(1),
|
|
|
+ });
|
|
|
+ Ok::<_, io::Error>(items)
|
|
|
+ })
|
|
|
+ .map_err(|e| io::Error::new(io::ErrorKind::Other, e))
|
|
|
+ .and_then(|mut items| {
|
|
|
+ items.sort_by(|it, next| it.message.cmp(&next.message));
|
|
|
+ Ok(items)
|
|
|
+ }),
|
|
|
+ )
|
|
|
}
|
|
|
}
|