diff --git a/server/src/actions/strong/mod.rs b/server/src/actions/strong/mod.rs index b0428c47..ebe351ac 100644 --- a/server/src/actions/strong/mod.rs +++ b/server/src/actions/strong/mod.rs @@ -39,3 +39,6 @@ pub use self::{sdel::sdel, sset::sset, supdate::supdate}; mod sdel; mod sset; mod supdate; + +#[cfg(test)] +mod tests; diff --git a/server/src/actions/strong/sdel.rs b/server/src/actions/strong/sdel.rs index aa5d4e40..9b2b18bf 100644 --- a/server/src/actions/strong/sdel.rs +++ b/server/src/actions/strong/sdel.rs @@ -56,7 +56,11 @@ action! { /// Snapshot the current status and then delete maintaining concurrency /// guarantees. `(all_okay, enc_err)` -fn snapshot_and_del(kve: &KVEngine, key_encoder: SingleEncoder, act: ActionIter) -> (bool, bool) { +pub(super) fn snapshot_and_del( + kve: &KVEngine, + key_encoder: SingleEncoder, + act: ActionIter, +) -> (bool, bool) { let mut snapshots = Vec::with_capacity(act.len()); let mut err_enc = false; let iter_stat_ok; @@ -75,6 +79,10 @@ fn snapshot_and_del(kve: &KVEngine, key_encoder: SingleEncoder, act: ActionIter) } }); } + cfg_test!({ + // give the caller 10 seconds to do some crap + do_sleep!(10 s); + }); if iter_stat_ok { // nice, all keys exist; let's plonk 'em let kve = kve; diff --git a/server/src/actions/strong/sset.rs b/server/src/actions/strong/sset.rs index e7114d45..42720c35 100644 --- a/server/src/actions/strong/sset.rs +++ b/server/src/actions/strong/sset.rs @@ -35,7 +35,7 @@ action! { /// /// This either returns `Okay` if all the keys were set, or it returns an /// `Overwrite Error` or code `2` - fn sset(handle: &crate::corestore::Corestore, con: &mut T, mut act: ActionIter) { + fn sset(handle: &crate::corestore::Corestore, con: &mut T, act: ActionIter) { let howmany = act.len(); if is_lowbit_set!(howmany) || howmany == 0 { return con.write_response(responses::groups::ACTION_ERR).await; @@ -79,6 +79,10 @@ fn snapshot_and_insert( } }); } + cfg_test!({ + // give the caller 5 seconds to do some crap + do_sleep!(5 s); + }); if key_iter_stat_ok { let _kve = kve; let lowtable = lowtable; diff --git a/server/src/actions/strong/supdate.rs b/server/src/actions/strong/supdate.rs index e89b9493..47ed6fa4 100644 --- a/server/src/actions/strong/supdate.rs +++ b/server/src/actions/strong/supdate.rs @@ -35,7 +35,7 @@ action! { /// /// This either returns `Okay` if all the keys were updated, or it returns `Nil` /// or code `1` - fn supdate(handle: &crate::corestore::Corestore, con: &mut T, mut act: ActionIter) { + fn supdate(handle: &crate::corestore::Corestore, con: &mut T, act: ActionIter) { let howmany = act.len(); if is_lowbit_set!(howmany) || howmany == 0 { return con.write_response(responses::groups::ACTION_ERR).await; diff --git a/server/src/actions/strong/tests.rs b/server/src/actions/strong/tests.rs new file mode 100644 index 00000000..c363f963 --- /dev/null +++ b/server/src/actions/strong/tests.rs @@ -0,0 +1,63 @@ +/* + * Created on Fri Jul 30 2021 + * + * This file is a part of Skytable + * Skytable (formerly known as TerrabaseDB or Skybase) is a free and open-source + * NoSQL database written by Sayan Nandan ("the Author") with the + * vision to provide flexibility in data modelling without compromising + * on performance, queryability or scalability. + * + * Copyright (c) 2021, Sayan Nandan + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * +*/ + +mod sdel_concurrency_tests { + use super::super::sdel; + use crate::corestore::Data; + use crate::kvengine::KVEngine; + use std::sync::Arc; + use std::thread; + #[test] + fn test_snapshot_okay() { + let kve = KVEngine::init(true, true); + let encoder = kve.get_key_encoder(); + let it = bi!("k1", "v1", "k2", "v2"); + sdel::snapshot_and_del(&kve, encoder, it); + } + #[test] + fn test_snapshot_fail_with_t2() { + let kve = Arc::new(KVEngine::init(true, true)); + let kve1 = kve.clone(); + let encoder = kve.get_key_encoder(); + { + kve.upsert(Data::from("k1"), Data::from("v1")).unwrap(); + kve.upsert(Data::from("k2"), Data::from("v2")).unwrap(); + } + let it = bi!("k1", "k2"); + // sdel will wait 10s for us + let t1handle = thread::spawn(move || sdel::snapshot_and_del(&kve1, encoder, it)); + // we have 10s: we sleep 5 to let the snapshot complete (thread spawning takes time) + do_sleep!(5 s); + assert!(kve + .update(Data::from("k1"), Data::from("updated-v1")) + .unwrap()); + // let us join t1 + t1handle.join().unwrap(); + // although we told sdel to delete it, it shouldn't because we externally + // updated the value + assert!(kve.exists(Data::from("k1")).unwrap()); + } +} diff --git a/server/src/corestore/mod.rs b/server/src/corestore/mod.rs index 68b1b148..1eacf182 100644 --- a/server/src/corestore/mod.rs +++ b/server/src/corestore/mod.rs @@ -283,7 +283,7 @@ impl Corestore { Some(ks) => { let tbl = Table::from_model_code(modelcode, volatile); if let Some(tbl) = tbl { - if ks.create_table(tblid.clone(), tbl) { + if ks.create_table(tblid, tbl) { Ok(()) } else { Err(DdlError::AlreadyExists) @@ -300,7 +300,7 @@ impl Corestore { Some(kspace) => { let tbl = Table::from_model_code(modelcode, volatile); if let Some(tbl) = tbl { - if kspace.create_table(tblid.clone(), tbl) { + if kspace.create_table(tblid, tbl) { Ok(()) } else { Err(DdlError::AlreadyExists) diff --git a/server/src/queryengine/tests.rs b/server/src/queryengine/tests.rs index 788ce2aa..18084fcd 100644 --- a/server/src/queryengine/tests.rs +++ b/server/src/queryengine/tests.rs @@ -26,17 +26,6 @@ use super::parser; -macro_rules! byt { - ($f:expr) => { - bytes::Bytes::from($f) - }; -} -macro_rules! bi { - ($($x:expr),+ $(,)?) => {{ - vec![$(bytes::Bytes::from($x),)*].into_iter() - }}; -} - mod parser_ddl_tests { use super::parser::parse_table_args; use crate::corestore::memstore::ObjectID; diff --git a/server/src/util.rs b/server/src/util.rs index 0df9d95c..0fd3f34f 100644 --- a/server/src/util.rs +++ b/server/src/util.rs @@ -93,6 +93,10 @@ macro_rules! typedef { #[macro_export] macro_rules! cfg_test { + ($block:block) => { + #[cfg(test)] + $block + }; ($($item:item)*) => { $(#[cfg(test)] $item)* }; @@ -191,3 +195,23 @@ pub mod compiler { b } } + +#[macro_export] +macro_rules! byt { + ($f:expr) => { + bytes::Bytes::from($f) + }; +} +#[macro_export] +macro_rules! bi { + ($($x:expr),+ $(,)?) => {{ + vec![$(bytes::Bytes::from($x),)*].into_iter() + }}; +} + +#[macro_export] +macro_rules! do_sleep { + ($dur:literal s) => {{ + std::thread::sleep(std::time::Duration::from_secs($dur)); + }}; +}