diff --git a/micro-changelog/Cargo.lock b/micro-changelog/Cargo.lock index acafd1edf..f994120b0 100644 --- a/micro-changelog/Cargo.lock +++ b/micro-changelog/Cargo.lock @@ -1,11 +1,31 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +[[package]] +name = "aho-corasick" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8716408b8bc624ed7f65d223ddb9ac2d044c0547b6fa4b0d554f3a9540496ada" +dependencies = [ + "memchr", +] + [[package]] name = "ascii" version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eab1c04a571841102f5345a8fc0f6bb3d31c315dec879b5c6e42e40ce7ffa34e" +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi 0.3.8", +] + [[package]] name = "bincode" version = "1.2.1" @@ -83,6 +103,19 @@ version = "1.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3" +[[package]] +name = "env_logger" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36" +dependencies = [ + "atty", + "humantime", + "log", + "regex", + "termcolor", +] + [[package]] name = "fnv" version = "1.0.6" @@ -158,6 +191,24 @@ dependencies = [ "wasi", ] +[[package]] +name = "hermit-abi" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "725cf19794cf90aa94e65050cb4191ff5d8fa87a498383774c47b332e3af952e" +dependencies = [ + "libc", +] + +[[package]] +name = "humantime" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f" +dependencies = [ + "quick-error", +] + [[package]] name = "idna" version = "0.2.0" @@ -238,9 +289,11 @@ checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400" [[package]] name = "micro_changelog" -version = "0.1.1" +version = "0.1.2" dependencies = [ "bincode", + "env_logger", + "log", "micro_model_moves", "redis_conn_pool", "redis_streams", @@ -368,6 +421,12 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "quick-error" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" + [[package]] name = "quote" version = "1.0.2" @@ -482,6 +541,24 @@ version = "0.1.56" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" +[[package]] +name = "regex" +version = "1.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f6946991529684867e47d86474e3a6d0c0ab9b82d5821e314b1ede31fa3a4b3" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", + "thread_local", +] + +[[package]] +name = "regex-syntax" +version = "0.6.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fe5bd57d1d7414c6b5ed48563a2c855d995ff777729dcd91c369ec7fea395ae" + [[package]] name = "scheduled-thread-pool" version = "0.2.3" @@ -546,6 +623,24 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "termcolor" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb6bfa289a4d7c5766392812c0a1f4c1ba45afa1ad47803c11e1f407d846d75f" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "thread_local" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14" +dependencies = [ + "lazy_static", +] + [[package]] name = "tokio" version = "0.2.12" @@ -672,6 +767,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +[[package]] +name = "winapi-util" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa515c5163a99cc82bab70fd3bfdd36d827be85de63737b40fcef2ce084a436e" +dependencies = [ + "winapi 0.3.8", +] + [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" diff --git a/micro-changelog/Cargo.toml b/micro-changelog/Cargo.toml index 926fffa75..7f3e38aed 100644 --- a/micro-changelog/Cargo.toml +++ b/micro-changelog/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "micro_changelog" -version = "0.1.1" +version = "0.1.2" authors = ["terkwood <38859656+Terkwood@users.noreply.github.com>"] edition = "2018" @@ -11,3 +11,5 @@ redis_conn_pool = { git = "https://github.com/Terkwood/BUGOUT", branch = "unstab redis_streams = { git = "https://github.com/Terkwood/BUGOUT", branch = "unstable" } bincode = "1.2.1" uuid = { version = "0.8.1", features = ["serde"] } +log = "0.4.8" +env_logger = "0.7.1" diff --git a/micro-changelog/Dockerfile b/micro-changelog/Dockerfile index 4d6644b19..1bec32b7a 100644 --- a/micro-changelog/Dockerfile +++ b/micro-changelog/Dockerfile @@ -8,4 +8,6 @@ COPY . /var/BUGOUT/micro-changelog/. RUN cargo install --path . +ENV RUST_LOG info + CMD ["micro_changelog"] diff --git a/micro-changelog/dev-cp.sh b/micro-changelog/dev-cp.sh new file mode 100755 index 000000000..3d006d219 --- /dev/null +++ b/micro-changelog/dev-cp.sh @@ -0,0 +1,4 @@ +#!/bin/bash + +docker cp Cargo.toml bugout_micro-changelog_1:/var/BUGOUT/micro-changelog/. +docker cp src bugout_micro-changelog_1:/var/BUGOUT/micro-changelog/. diff --git a/micro-changelog/src/lib.rs b/micro-changelog/src/lib.rs index 6e6c50fd3..7c148f7c9 100644 --- a/micro-changelog/src/lib.rs +++ b/micro-changelog/src/lib.rs @@ -1,6 +1,9 @@ extern crate bincode; +extern crate env_logger; +extern crate log; pub extern crate micro_model_moves; extern crate redis_streams; + mod model; pub mod repo; pub mod stream; @@ -9,6 +12,8 @@ pub use redis_conn_pool; pub use redis_conn_pool::{r2d2, r2d2_redis, redis, RedisHostUrl}; use repo::redis_key::KeyProvider; +use log::info; + pub struct Components { pub pool: redis_conn_pool::Pool, pub redis_key_provider: KeyProvider, @@ -17,7 +22,7 @@ pub struct Components { impl Default for Components { fn default() -> Self { let pool = redis_conn_pool::create(RedisHostUrl::default()); - println!("Connected to redis"); + info!("Connected to redis"); Components { pool, redis_key_provider: KeyProvider::default(), diff --git a/micro-changelog/src/main.rs b/micro-changelog/src/main.rs index f2189bee3..98d955198 100644 --- a/micro-changelog/src/main.rs +++ b/micro-changelog/src/main.rs @@ -1,11 +1,13 @@ -const NAME: &'static str = env!("CARGO_PKG_NAME"); -const VERSION: &'static str = env!("CARGO_PKG_VERSION"); - use micro_changelog::stream; use micro_changelog::Components; use stream::StreamTopics; +use log::info; + +const VERSION: &'static str = env!("CARGO_PKG_VERSION"); + fn main() { - println!("🔢 {:<8} {}", NAME, VERSION); + env_logger::init(); + info!("🔢 {}", VERSION); stream::process(StreamTopics::default(), &Components::default()) } diff --git a/micro-changelog/src/repo/game_states_repo.rs b/micro-changelog/src/repo/game_states_repo.rs index 14a55c34f..0c353d737 100644 --- a/micro-changelog/src/repo/game_states_repo.rs +++ b/micro-changelog/src/repo/game_states_repo.rs @@ -21,16 +21,16 @@ pub fn fetch(game_id: &GameId, components: &Components) -> Result Result { +) -> Result { let mut conn = components.pool.get().unwrap(); - let key = components.redis_key_provider.game_states(&game_id); + let key = components.redis_key_provider.game_states(game_id); let done = conn.set(&key, game_state.serialize()?)?; // Touch TTL whenever you set the record conn.expire(key, EXPIRY_SECS)?; - + Ok(done) } diff --git a/micro-changelog/src/stream/mod.rs b/micro-changelog/src/stream/mod.rs index 12228c867..ff1a2e09c 100644 --- a/micro-changelog/src/stream/mod.rs +++ b/micro-changelog/src/stream/mod.rs @@ -8,8 +8,11 @@ use micro_model_moves::*; use redis_conn_pool::redis; pub use topics::StreamTopics; use xread::*; + +use log::{error, info}; + pub fn process(topics: StreamTopics, components: &crate::Components) { - println!("Processing {:#?}", topics); + info!("Processing {:#?}", topics); loop { match entry_id_repo::fetch_all(components) { Ok(entry_ids) => match xread_sorted(entry_ids, &topics, &components.pool) { @@ -18,58 +21,59 @@ pub fn process(topics: StreamTopics, components: &crate::Components) { match time_ordered_event { (entry_id, StreamData::MA(move_acc)) => { match update_game_state(&move_acc, &components) { - Err(e) => println!("err updating game state {:?}", e), + Err(e) => error!("err updating game state {:?}", e), Ok(gs) => { + // These next two ops are concurrent in the kafka impl + if let Err(e) = xadd_game_states_changelog( + &move_acc.game_id, + gs, + &topics.game_states_changelog, + components, + ) { + error!("could not XADD to game state changelog {:?}", e) + } + if let Err(e) = xadd_move_made( &move_acc, &topics.move_made_ev, &components, - ) - .and_then(|_| { - xadd_game_states_changelog( - &move_acc.game_id, - gs, - &topics.game_states_changelog, - components, - ) - }) { - println!("err in XADDs {:?}", e) - } else { - if let Err(e) = entry_id_repo::update( - EntryIdType::MoveAcceptedEvent, - entry_id, - &components, - ) { - println!( - "err saving entry id for move accepted {:?}", - e - ) - } + ) { + error!("err in XADD move made {:?}", e) + } + + if let Err(e) = entry_id_repo::update( + EntryIdType::MoveAcceptedEvent, + entry_id, + &components, + ) { + error!("err saving entry id for move accepted {:?}", e) } } } } (entry_id, StreamData::GS(game_id, gs)) => { - if let Err(e) = game_states_repo::write(game_id, &gs, &components) { - println!("Error saving game state {:#?}", e) + if let Err(e) = game_states_repo::write(&game_id, &gs, &components) + { + error!("Error saving game state {:#?}", e) } else { - println!("loop game STATE"); - if let Err(e) = entry_id_repo::update( - EntryIdType::GameStateChangelog, - entry_id, - &components, - ) { - println!("Error saving entry ID for game state {:#?}", e) - } + info!("wrote game state: {:?} {:?}", game_id, gs); + } + + if let Err(e) = entry_id_repo::update( + EntryIdType::GameStateChangelog, + entry_id, + &components, + ) { + error!("Error saving entry ID for game state {:#?}", e) } } } } } - Err(e) => println!("Redis err in xread: {:#?}", e), + Err(e) => error!("Redis err in xread: {:#?}", e), }, - Err(FetchErr::Deser) => println!("Unable to deserialize entry IDs"), - Err(FetchErr::Redis(r)) => println!("Redis err {:#?}", r), + Err(FetchErr::Deser) => error!("Unable to deserialize entry IDs"), + Err(FetchErr::Redis(r)) => error!("Redis err {:#?}", r), } } } @@ -102,7 +106,7 @@ fn update_game_state( og.moves.push(move_acc.clone()); og })?; - game_states_repo::write(game_id, &new_game_state, &components)?; + game_states_repo::write(&game_id, &new_game_state, &components)?; Ok(new_game_state) } diff --git a/micro-changelog/src/stream/xread.rs b/micro-changelog/src/stream/xread.rs index c17656ad5..a09201e37 100644 --- a/micro-changelog/src/stream/xread.rs +++ b/micro-changelog/src/stream/xread.rs @@ -1,6 +1,7 @@ use super::StreamTopics; use crate::redis; use crate::repo::entry_id_repo::AllEntryIds; +use log::{error, warn}; use micro_model_moves::{GameId, GameState, MoveMade}; use redis_conn_pool::Pool; use redis_streams::XReadEntryId; @@ -67,7 +68,7 @@ fn deser(xread_result: XReadResult, topics: &StreamTopics) -> HashMap HashMap"] edition = "2018" @@ -10,3 +10,5 @@ bincode = "1.2.1" micro_model_moves = { git = "https://github.com/Terkwood/BUGOUT", branch = "unstable" } redis_conn_pool = { git = "https://github.com/Terkwood/BUGOUT", branch = "unstable" } redis_streams = {git = "https://github.com/Terkwood/BUGOUT", branch = "unstable"} +env_logger = "0.7.1" +log = "0.4.8" diff --git a/micro-judge/Dockerfile b/micro-judge/Dockerfile index 1fb97d662..16f1f4d30 100644 --- a/micro-judge/Dockerfile +++ b/micro-judge/Dockerfile @@ -8,4 +8,6 @@ COPY . /var/BUGOUT/micro-judge/. RUN cargo install --path . +ENV RUST_LOG info + CMD ["micro_judge"] diff --git a/micro-judge/src/game/mod.rs b/micro-judge/src/game/mod.rs index d8fe33ef6..8077bf617 100644 --- a/micro-judge/src/game/mod.rs +++ b/micro-judge/src/game/mod.rs @@ -1,6 +1,8 @@ use crate::model::*; pub use capturing::captures_for; +use log::info; + mod capturing; pub enum Judgement { @@ -8,6 +10,7 @@ pub enum Judgement { Rejected, } pub fn judge(mm: &MakeMoveCommand, game_state: &GameState) -> Judgement { + info!("Judge {:?}", mm); if validate_move(mm, game_state) { let captured: Vec = mm .coord diff --git a/micro-judge/src/io/stream.rs b/micro-judge/src/io/stream.rs index 993031017..4c385b594 100644 --- a/micro-judge/src/io/stream.rs +++ b/micro-judge/src/io/stream.rs @@ -8,58 +8,71 @@ use crate::model::*; use crate::repo::entry_id::{EntryIdRepo, EntryIdType}; use crate::repo::game_states::GameStatesRepo; +use log::{error, info, warn}; + +/// Spins too much. See https://github.com/Terkwood/BUGOUT/issues/217 pub fn process(opts: ProcessOpts, pool: &Pool) { let eid_repo = opts.entry_id_repo; + loop { match eid_repo.fetch_all() { Ok(entry_ids) => { - if let Ok(xread_result) = xread_sort(entry_ids, &opts.topics, &pool) { + if let Ok(xread_result) = xread_sort(&entry_ids, &opts.topics, &pool) { for time_ordered_event in xread_result { + info!("EIDS {:?}", entry_ids); match time_ordered_event { (entry_id, StreamData::MM(mm)) => { - match make_move(&mm, &opts.game_states_repo) { - Err(e) => println!("Fetch err on game state {:#?}", e), - Ok(Judgement::Accepted(move_made)) => { - if let Err(e) = xadd_move_accepted( - &move_made, - &pool, - &opts.topics.move_accepted_ev, - ) { - println!("Error XADD to move_accepted {:?}", e) - } else { - if let Err(e) = eid_repo - .update(EntryIdType::MakeMoveCommand, entry_id) - { - println!( - "error updating make_move_cmd eid: {:?}", - e - ) + let fetched_gs = opts.game_states_repo.fetch(&mm.game_id); + match fetched_gs { + Ok(Some(game_state)) => match judge(&mm, &game_state) { + Judgement::Accepted(move_made) => { + if let Err(e) = xadd_move_accepted( + &move_made, + &pool, + &opts.topics.move_accepted_ev, + ) { + error!("Error XADD to move_accepted {:?}", e) } } - } - Ok(Judgement::Rejected) => println!("MOVE REJECTED: {:#?}", mm), + Judgement::Rejected => error!("MOVE REJECTED: {:#?}", mm), + }, + Ok(None) => warn!("No game state for game {}", mm.game_id.0), + Err(e) => error!("Deser error ({:?})!", e), + } + + if let Err(e) = + eid_repo.update(EntryIdType::MakeMoveCommand, entry_id) + { + error!("error updating make_move_cmd eid: {:?}", e) } } (entry_id, StreamData::GS(game_id, game_state)) => { - if let Err(e) = &opts.game_states_repo.write(game_id, game_state) { - println!("error writing game state {:?}", e) - } else { - if let Err(e) = - eid_repo.update(EntryIdType::GameStateChangelog, entry_id) - { - println!("error updating changelog eid: {:?}", e); - } + if let Err(e) = &opts.game_states_repo.write(&game_id, &game_state) + { + error!( + "error writing game state {:?} -- advancing eid pointer", + e + ) + } + + if let Err(e) = + eid_repo.update(EntryIdType::GameStateChangelog, entry_id) + { + error!("error updating changelog eid: {:?}", e); } + + info!("Tracking {:?} {:?}", game_id, game_state); } } } } } - Err(FetchErr::Deser) => println!("Deserialization err in stream processing"), - Err(FetchErr::Redis(r)) => println!("Redis error in stream processing {:?}", r), + Err(FetchErr::Deser) => error!("Deserialization err in stream processing"), + Err(FetchErr::Redis(r)) => error!("Redis error in stream processing {:?}", r), } } } + #[derive(Clone)] pub struct ProcessOpts { pub topics: StreamTopics, @@ -81,11 +94,6 @@ impl Default for ProcessOpts { } } -fn make_move(mm: &MakeMoveCommand, gs_repo: &GameStatesRepo) -> Result { - let game_state = gs_repo.fetch(&mm.game_id)?; - Ok(judge(mm, &game_state)) -} - fn xadd_move_accepted( move_made: &MoveMade, pool: &Pool, diff --git a/micro-judge/src/io/xread.rs b/micro-judge/src/io/xread.rs index ff80caddc..becd9fd82 100644 --- a/micro-judge/src/io/xread.rs +++ b/micro-judge/src/io/xread.rs @@ -3,16 +3,17 @@ use super::redis; use super::topics::*; use crate::model::*; use crate::repo::entry_id::AllEntryIds; +use log::error; use redis_streams::*; use std::collections::HashMap; use std::str::FromStr; use uuid::Uuid; const BLOCK_MSEC: u32 = 5000; -pub type XReadResult = Vec>>>>; +pub type XReadResult = Vec>>>>>; pub fn xread_sort( - entry_ids: AllEntryIds, + entry_ids: &AllEntryIds, topics: &StreamTopics, pool: &Pool, ) -> Result, redis::RedisError> { @@ -37,6 +38,7 @@ pub fn xread_sort( answer.push((sk, data.clone())) } } + Ok(answer) } @@ -60,7 +62,7 @@ fn deser(xread_result: XReadResult, topics: &StreamTopics) -> HashMap HashMap HashMap, + xread_result: HashMap>, ) -> Result { - let mx: Option = xread_result + let values_as_strings: HashMap = xread_result + .iter() + .map(|(k, v)| (k.clone(), String::from_utf8(v.clone()).expect("bytes"))) + .collect(); + let mx: Option = values_as_strings .get("coord_x") .and_then(|s| s.parse::().ok()); - let my: Option = xread_result + let my: Option = values_as_strings .get("coord_y") .and_then(|s| s.parse::().ok()); let coord = match (mx, my) { @@ -102,9 +109,9 @@ fn deser_make_move_command( _ => None, }; Ok(MakeMoveCommand { - game_id: GameId(Uuid::from_str(&xread_result["game_id"])?), - req_id: ReqId(Uuid::from_str(&xread_result["req_id"])?), - player: Player::from_str(&xread_result["player"]), + game_id: GameId(Uuid::from_str(&values_as_strings["game_id"])?), + req_id: ReqId(Uuid::from_str(&values_as_strings["req_id"])?), + player: Player::from_str(&values_as_strings["player"]), coord, }) } diff --git a/micro-judge/src/lib.rs b/micro-judge/src/lib.rs index d3e9cbca8..97c8c0d5e 100644 --- a/micro-judge/src/lib.rs +++ b/micro-judge/src/lib.rs @@ -1,3 +1,5 @@ +extern crate env_logger; +extern crate log; extern crate micro_model_moves; pub extern crate redis_conn_pool; extern crate redis_streams; diff --git a/micro-judge/src/main.rs b/micro-judge/src/main.rs index 5447a37bd..75b1fc656 100644 --- a/micro-judge/src/main.rs +++ b/micro-judge/src/main.rs @@ -1,12 +1,16 @@ +extern crate log; extern crate micro_judge; +use log::info; + use micro_judge::io::{conn_pool, stream}; const NAME: &'static str = env!("CARGO_PKG_NAME"); const VERSION: &'static str = env!("CARGO_PKG_VERSION"); fn main() { - println!("🔢 {:<8} {}", NAME, VERSION); + env_logger::init(); + info!("🔢 {:<8} {}", NAME, VERSION); stream::process( stream::ProcessOpts::default(), &conn_pool::create(conn_pool::RedisHostUrl::default()), diff --git a/micro-judge/src/repo/entry_id.rs b/micro-judge/src/repo/entry_id.rs index 5365c3e9a..f28dc23e1 100644 --- a/micro-judge/src/repo/entry_id.rs +++ b/micro-judge/src/repo/entry_id.rs @@ -16,6 +16,7 @@ pub struct EntryIdRepo { pub namespace: RedisKeyNamespace, pub pool: Pool, } + impl EntryIdRepo { pub fn fetch_all(&self) -> Result { let mut conn = self.pool.get().unwrap(); @@ -46,6 +47,7 @@ impl EntryIdRepo { entry_id: XReadEntryId, ) -> Result<(), redis::RedisError> { let mut conn = self.pool.get().unwrap(); + conn.hset( entry_ids_hash_key(&self.namespace), entry_id_type.hash_field(), diff --git a/micro-judge/src/repo/game_states.rs b/micro-judge/src/repo/game_states.rs index 5ffce5e87..de454ffba 100644 --- a/micro-judge/src/repo/game_states.rs +++ b/micro-judge/src/repo/game_states.rs @@ -14,17 +14,22 @@ pub struct GameStatesRepo { } impl GameStatesRepo { - pub fn fetch(&self, game_id: &GameId) -> Result { + pub fn fetch(&self, game_id: &GameId) -> Result, FetchErr> { let mut conn = self.pool.get().unwrap(); let key = game_states_key(&self.namespace, &game_id); - let bin_data: Vec = conn.get(&key)?; - let r = GameState::from(&bin_data)?; - // Touch TTL whenever you get the record - conn.expire(key, EXPIRY_SECS)?; + let bin_data: Option> = conn.get(&key)?; + let r = if let Some(b) = bin_data { + // Touch TTL whenever you get the record + conn.expire(key, EXPIRY_SECS)?; + Some(GameState::from(&b)?) + } else { + None + }; + Ok(r) } - pub fn write(&self, game_id: GameId, game_state: GameState) -> Result { + pub fn write(&self, game_id: &GameId, game_state: &GameState) -> Result { let mut conn = self.pool.get().unwrap(); let key = game_states_key(&self.namespace, &game_id);