diff --git a/Cargo.toml b/Cargo.toml index 50d56c2..69d8413 100755 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,6 +22,7 @@ clap = "2.34.0" reqwest = { version="*" , features = ["blocking"]} signal-hook = "0.3.9" atomic_float = "0.1.0" +lazy_static = "1.4.0" [dev-dependencies] tokio-test = "*" diff --git a/README.md b/README.md index c943154..9cff8fe 100644 --- a/README.md +++ b/README.md @@ -76,7 +76,8 @@ Done you now have nun-db running in your docker and exposing all the ports to be * [How to create your simple version of google analytics real-time using Nun-db](https://mateusfreira.github.io/@mateusfreira-create-a-simple-verison-of-google-analytics-realtime-using-nun-db/) * [NunDb How to backup one or all databases](https://mateusfreira.github.io/@mateusfreira-nundb-how-to-backups-all-databases-with-one-command/) - +* [To snapshot the database from memory to disk] +nun-db --user $NUN_USER -p $NUN_PWD --host "https://http.nundb.org" exec "use-db $DB_NAME $DB_TOKEN; snapshot" ## Technical documentations diff --git a/src/lib/commad_line/commands.rs b/src/lib/commad_line/commands.rs index 605994a..1e6960c 100644 --- a/src/lib/commad_line/commands.rs +++ b/src/lib/commad_line/commands.rs @@ -10,14 +10,12 @@ pub fn prepare_args<'a>() -> ArgMatches<'static> { Arg::with_name("user") .short("u") .long("user") - .required(true) .takes_value(true) .help("Admin username"), ) .arg( Arg::with_name("pwd") .short("p") - .required(true) .takes_value(true) .help("Admin password"), ) diff --git a/src/lib/configuration.rs b/src/lib/configuration.rs new file mode 100644 index 0000000..3a2c383 --- /dev/null +++ b/src/lib/configuration.rs @@ -0,0 +1,49 @@ +use std::env; +use lazy_static::lazy_static; + +lazy_static! { + pub static ref NUN_USER: String = expect_env_var("NUN_USER", "mateus", false); + pub static ref NUN_PWD: String = expect_env_var("NUN_PWD", "mateus", false); + pub static ref NUN_DBS_DIR: String = expect_env_var("NUN_DBS_DIR", "dbs", false); + pub static ref NUN_WS_ADDR: String = expect_env_var("NUN_WS_ADDR", "0.0.0.0:3012", false); + pub static ref NUN_HTTP_ADDR: String = expect_env_var("NUN_HTTP_ADDR", "0.0.0.0:3013", false); + pub static ref NUN_TCP_ADDR: String = expect_env_var("NUN_TCP_ADDR", "0.0.0.0:3014", false); + pub static ref NUN_REPLICATE_ADDR: String = expect_env_var("NUN_REPLICATE_ADDR", "", false); + pub static ref NUN_LOG_LEVEL: String = expect_env_var("NUN_LOG_LEVEL", "Info", false); //(Off, Error, Warn, Info, Debug, Trace) + pub static ref NUN_LOG_DIR: String = expect_env_var("NUN_LOG_DIR", "/tmp/data/nun_db/log", false); + pub static ref NUN_LOG_TO_FILE: String = expect_env_var("NUN_LOG_TO_FILE", "true", false); //true or false +} + +#[cfg(debug_assertions)] +fn expect_env_var(name: &str, default: &str, _required: bool) -> String { + return env::var(name).unwrap_or(String::from(default)); +} + +#[cfg(not(debug_assertions))] +fn expect_env_var(name: &str, _default: &str, required: bool) -> String { + if required { + return env::var(name).expect(&format!( + "Environment variable {name} is not defined", + name = name + )); + } else { + return env::var(name).unwrap_or("".to_string()); + } +} + + +#[cfg(test)] +mod tests { + use crate::lib::configuration::expect_env_var; + + #[test] + fn run_mode_should_get_empty_but_debug_mode_got_value() { + + #[cfg(debug_assertions)] + assert_eq!(expect_env_var("NUN_USER", "mateus", false), "mateus".to_string()); + + #[cfg(not(debug_assertions))] + assert_eq!(expect_env_var("NUN_USER", "", false), "".to_string()) + } + +} diff --git a/src/lib/db_ops.rs b/src/lib/db_ops.rs index da5f2f2..738cc78 100644 --- a/src/lib/db_ops.rs +++ b/src/lib/db_ops.rs @@ -94,6 +94,7 @@ pub fn create_db(name: &String, token: &String, dbs: &Arc, client: &C } } } + pub fn snapshot_db(db: &Database, dbs: &Databases) -> Response { let name = db.name.clone(); { diff --git a/src/lib/disk_ops.rs b/src/lib/disk_ops.rs index 8c8b884..97b4e3b 100644 --- a/src/lib/disk_ops.rs +++ b/src/lib/disk_ops.rs @@ -1,6 +1,5 @@ use log; use std::collections::HashMap; -use std::env; use std::fs; use std::fs::OpenOptions; use std::fs::{create_dir_all, read_dir, File}; @@ -20,7 +19,6 @@ use crate::bo::*; const SNAPSHOT_TIME: i64 = 3000; // 30 secounds const FILE_NAME: &'static str = "-nun.data"; const META_FILE_NAME: &'static str = "-nun.madadata"; -const DIR_NAME: &'static str = "dbs"; const KEYS_FILE: &'static str = "keys-nun.keys"; @@ -33,11 +31,11 @@ const OP_TIME_SIZE: usize = 8; const OP_OP_SIZE: usize = 1; const OP_RECORD_SIZE: usize = OP_TIME_SIZE + OP_DB_ID_SIZE + OP_KEY_SIZE + OP_OP_SIZE; + +use crate::lib::configuration::{NUN_DBS_DIR}; + pub fn get_dir_name() -> String { - match env::var_os("NUN_DBS_DIR") { - Some(dir_name) => dir_name.into_string().unwrap(), - None => DIR_NAME.to_string(), - } + NUN_DBS_DIR.to_string() } pub fn load_keys_map_from_disk() -> HashMap { @@ -204,6 +202,7 @@ pub fn start_snap_shot_timer(timer: timer::Timer, dbs: Arc) { pub fn snapshot_all_pendding_dbs(dbs: &Arc) { let queue_len = { dbs.to_snapshot.read().unwrap().len() }; + log::debug!("snapshot_all_pendding_dbs | queue_len == {}", queue_len); if queue_len > 0 { snapshot_keys(&dbs); let mut dbs_to_snapshot = { diff --git a/src/lib/mod.rs b/src/lib/mod.rs index 6d1e9b0..7c81d07 100644 --- a/src/lib/mod.rs +++ b/src/lib/mod.rs @@ -1,3 +1,4 @@ +pub mod configuration; pub mod bo; pub mod commad_line; pub mod db_ops; diff --git a/src/main.rs b/src/main.rs index a362ec0..14f47c8 100755 --- a/src/main.rs +++ b/src/main.rs @@ -13,8 +13,11 @@ use std::sync::Arc; use clap::ArgMatches; use env_logger::{Builder, Env, Target}; +use crate::lib::configuration::{NUN_LOG_LEVEL, NUN_USER, NUN_PWD, NUN_WS_ADDR, NUN_HTTP_ADDR, NUN_TCP_ADDR, NUN_REPLICATE_ADDR}; + fn init_logger() { - let env = Env::default().filter_or("NUN_LOG_LEVEL", "info"); + + let env = Env::default().filter_or("NUN_LOG_LEVEL", NUN_LOG_LEVEL.as_str()); Builder::from_env(env) .format_level(false) .target(Target::Stdout) @@ -23,21 +26,22 @@ fn init_logger() { } fn main() -> Result<(), String> { + init_logger(); log::info!("nundb starting!"); let matches: ArgMatches<'_> = lib::commad_line::commands::prepare_args(); if let Some(start_match) = matches.subcommand_matches("start") { return start_db( - matches.value_of("user").unwrap(), - matches.value_of("pwd").unwrap(), - start_match - .value_of("tcp-address") - .unwrap_or("0.0.0.0:3014"), - start_match.value_of("ws-address").unwrap_or("0.0.0.0:3012"), + matches.value_of("user").unwrap_or(NUN_USER.as_str()), + matches.value_of("pwd").unwrap_or(NUN_PWD.as_str()), + start_match.value_of("ws-address").unwrap_or(NUN_WS_ADDR.as_str()), start_match .value_of("http-address") - .unwrap_or("0.0.0.0:3013"), - start_match.value_of("replicate-address").unwrap_or(""), + .unwrap_or(NUN_HTTP_ADDR.as_str()), + start_match + .value_of("tcp-address") + .unwrap_or(NUN_TCP_ADDR.as_str()), + start_match.value_of("replicate-address").unwrap_or(NUN_REPLICATE_ADDR.as_str()), ); } else { return lib::commad_line::commands::exec_command(&matches); @@ -47,11 +51,12 @@ fn main() -> Result<(), String> { fn start_db( user: &str, pwd: &str, - tcp_address: &str, ws_address: &str, http_address: &str, + tcp_address: &str, replicate_address: &str, ) -> Result<(), String> { + let (replication_sender, replication_receiver): (Sender, Receiver) = channel(100); diff --git a/tests/createdb.sh b/tests/createdb.sh new file mode 100755 index 0000000..4a35c3a --- /dev/null +++ b/tests/createdb.sh @@ -0,0 +1 @@ +cargo run -- -u mateus -p mateus create-db -d todos -t tokenxyz \ No newline at end of file diff --git a/tests/debug.sh b/tests/debug.sh new file mode 100755 index 0000000..e533036 --- /dev/null +++ b/tests/debug.sh @@ -0,0 +1,2 @@ +export NUN_DBS_DIR=/tmp/data/nun_db +cargo run -- -u mateus -p mateus start --http-address 0.0.0.0:3013 --tcp-address 0.0.0.0:3014 --ws-address 0.0.0.0:3012 diff --git a/tests/snapshot.sh b/tests/snapshot.sh new file mode 100755 index 0000000..507d9f4 --- /dev/null +++ b/tests/snapshot.sh @@ -0,0 +1 @@ +cargo run -- -u mateus -p mateus exec "use-db todos tokenxyz; snapshot;" \ No newline at end of file