From e8a64cb0a5048e9b57e233b78bc39ee506c59c7e Mon Sep 17 00:00:00 2001 From: Matt Schallert Date: Wed, 31 Jul 2019 23:21:42 -0400 Subject: [PATCH 1/8] 2018 edition updates --- foundationdb-bench/Cargo.toml | 1 + foundationdb-bench/src/bin/fdb-bench.rs | 10 +++--- foundationdb-gen/Cargo.toml | 1 + foundationdb-gen/src/lib.rs | 7 ++-- foundationdb-sys/Cargo.toml | 1 + foundationdb/Cargo.toml | 1 + foundationdb/src/bin/bindingtester.rs | 47 +++++++++++++++---------- foundationdb/src/cluster.rs | 8 ++--- foundationdb/src/database.rs | 10 +++--- foundationdb/src/error.rs | 6 ++-- foundationdb/src/fdb_api.rs | 4 +-- foundationdb/src/future.rs | 2 +- foundationdb/src/hca.rs | 12 +++---- foundationdb/src/lib.rs | 10 +++--- foundationdb/src/network.rs | 8 ++--- foundationdb/src/subspace.rs | 4 +-- foundationdb/src/transaction.rs | 14 ++++---- foundationdb/src/tuple/element.rs | 6 ++-- foundationdb/tests/atomic.rs | 2 +- 19 files changed, 84 insertions(+), 70 deletions(-) diff --git a/foundationdb-bench/Cargo.toml b/foundationdb-bench/Cargo.toml index be4be31..e9d2f6f 100644 --- a/foundationdb-bench/Cargo.toml +++ b/foundationdb-bench/Cargo.toml @@ -2,6 +2,7 @@ name = "foundationdb-bench" version = "0.1.0" authors = ["Benjamin Fry "] +edition = "2018" description = """ Bindings to the C api for FoundationDB diff --git a/foundationdb-bench/src/bin/fdb-bench.rs b/foundationdb-bench/src/bin/fdb-bench.rs index 43d6af9..b6fcaf6 100644 --- a/foundationdb-bench/src/bin/fdb-bench.rs +++ b/foundationdb-bench/src/bin/fdb-bench.rs @@ -16,8 +16,8 @@ use rand::rngs::mock::StepRng; use stopwatch::Stopwatch; use structopt::StructOpt; -use fdb::error::*; -use fdb::*; +use crate::fdb::error::*; +use crate::fdb::*; #[derive(Clone)] struct Counter { @@ -72,12 +72,12 @@ impl BenchRunner { } //TODO: impl future - fn run(self) -> Box> { + fn run(self) -> Box> { Box::new(loop_fn(self, Self::step)) } //TODO: impl future - fn step(mut self) -> Box, Error = Error>> { + fn step(mut self) -> Box, Error = Error>> { let trx = self.trx.take().unwrap(); for _ in 0..self.trx_batch_size { @@ -150,7 +150,7 @@ impl Bench { &self, r: std::ops::Range, counter: Counter, - ) -> Box> { + ) -> Box> { let runners = r .into_iter() .map(|n| { diff --git a/foundationdb-gen/Cargo.toml b/foundationdb-gen/Cargo.toml index ac891c4..46e570d 100644 --- a/foundationdb-gen/Cargo.toml +++ b/foundationdb-gen/Cargo.toml @@ -2,6 +2,7 @@ name = "foundationdb-gen" version = "0.1.0" authors = ["Jihyun Yu "] +edition = "2018" description = """ Binding generation helper for FoundationDB. diff --git a/foundationdb-gen/src/lib.rs b/foundationdb-gen/src/lib.rs index 9b4b028..4ccc92f 100644 --- a/foundationdb-gen/src/lib.rs +++ b/foundationdb-gen/src/lib.rs @@ -62,7 +62,7 @@ impl FdbScope { for option in self.options.iter() { let rs_name = match option.name.as_ref() { "AppendIfFit" => "AppendIfFits", - s => s + s => s, }; s += &format!("{}::{}", self.name, rs_name); @@ -352,7 +352,8 @@ const OPTIONS_DATA: &[u8] = include_bytes!("/usr/include/foundationdb/fdb.option const OPTIONS_DATA: &[u8] = include_bytes!("/usr/local/include/foundationdb/fdb.options"); #[cfg(target_os = "windows")] -const OPTIONS_DATA: &[u8] = include_bytes!("C:/Program Files/foundationdb/include/foundationdb/fdb.options"); +const OPTIONS_DATA: &[u8] = + include_bytes!("C:/Program Files/foundationdb/include/foundationdb/fdb.options"); pub fn emit() -> Result { let mut reader = OPTIONS_DATA.as_ref(); @@ -387,7 +388,7 @@ pub fn emit() -> Result { let mut result = format!( "{}\n{}\n{}\n\n", - "use std;", "use error;", "use foundationdb_sys as fdb;" + "use std;", "use crate::error;", "use foundationdb_sys as fdb;" ); for scope in scopes.iter() { diff --git a/foundationdb-sys/Cargo.toml b/foundationdb-sys/Cargo.toml index a05d540..0c928f7 100644 --- a/foundationdb-sys/Cargo.toml +++ b/foundationdb-sys/Cargo.toml @@ -2,6 +2,7 @@ name = "foundationdb-sys" version = "0.2.0" authors = ["Benjamin Fry "] +edition = "2018" description = """ Bindings to the C api for FoundationDB diff --git a/foundationdb/Cargo.toml b/foundationdb/Cargo.toml index b68d9fd..99b2118 100644 --- a/foundationdb/Cargo.toml +++ b/foundationdb/Cargo.toml @@ -2,6 +2,7 @@ name = "foundationdb" version = "0.3.0" authors = ["Benjamin Fry "] +edition = "2018" description = """ High level client bindings for FoundationDB. diff --git a/foundationdb/src/bin/bindingtester.rs b/foundationdb/src/bin/bindingtester.rs index c64a6b9..b142ae1 100644 --- a/foundationdb/src/bin/bindingtester.rs +++ b/foundationdb/src/bin/bindingtester.rs @@ -6,14 +6,14 @@ extern crate log; use std::collections::HashMap; -use fdb::error::Error; -use fdb::keyselector::KeySelector; -use fdb::tuple::*; -use fdb::*; +use crate::fdb::error::Error; +use crate::fdb::keyselector::KeySelector; +use crate::fdb::tuple::*; +use crate::fdb::*; use futures::future::*; use futures::prelude::*; -use fdb::options::{MutationType, StreamingMode}; +use crate::fdb::options::{MutationType, StreamingMode}; fn mutation_from_str(s: &str) -> MutationType { match s { "ADD" => MutationType::Add, @@ -174,7 +174,7 @@ fn has_opt<'a>(cmd: &'a str, opt: &'static str) -> (&'a str, bool) { impl Instr { fn from(data: &[u8]) -> Self { - use InstrCode::*; + use crate::InstrCode::*; let tup: Tuple = Decode::try_from(data).unwrap(); let cmd = match tup[0] { @@ -251,7 +251,7 @@ impl Instr { } } -type StackFuture = Box), Error = Error>>; +type StackFuture = Box), Error = Error>>; struct StackItem { number: usize, // TODO: enum @@ -350,14 +350,15 @@ impl StackMachine { } } - fn fetch_instr(&self) -> Box, Error = Error>> { + fn fetch_instr(&self) -> Box, Error = Error>> { let db = self.db.clone(); let prefix = self.prefix.clone(); let f = db.transact(move |trx| { let opt = transaction::RangeOptionBuilder::from(&prefix).build(); let instrs = Vec::new(); - let f = trx.get_ranges(opt) + let f = trx + .get_ranges(opt) .map_err(|(_opt, e)| e) .fold(instrs, |mut instrs, res| { let kvs = res.key_values(); @@ -431,7 +432,7 @@ impl StackMachine { } fn run_step(&mut self, number: usize, mut instr: Instr) { - use InstrCode::*; + use crate::InstrCode::*; let is_db = instr.pop_database(); let mut mutation = false; @@ -494,7 +495,8 @@ impl StackMachine { OnError => { let code: i64 = self.pop_item(); let trx0 = trx.clone(); - let f = trx.on_error(Error::from(code as i32)) + let f = trx + .on_error(Error::from(code as i32)) .map(move |_| (trx0, b"RESULT_NOT_PRESENT".to_vec())); self.push_fut(number, f); } @@ -520,7 +522,8 @@ impl StackMachine { let mut prefix: Vec = self.pop_item(); //TODO: wait - let key = trx.get_key(selector, instr.pop_snapshot()) + let key = trx + .get_key(selector, instr.pop_snapshot()) .map(move |res| res.value().to_vec()) .wait() .unwrap(); @@ -582,9 +585,10 @@ impl StackMachine { .snapshot(instr.pop_snapshot()) .build(); - let mut out = Vec::new(); + let out = Vec::new(); let trx0 = trx.clone(); - let f = trx.get_ranges(opt) + let f = trx + .get_ranges(opt) .map_err(|(_, e)| e) .fold(out, move |mut out, res| { let kvs = res.key_values(); @@ -620,7 +624,8 @@ impl StackMachine { GetReadVersion => { //TODO: wait - let version = trx.get_read_version() + let version = trx + .get_read_version() .wait() .expect("failed to get read version"); @@ -633,7 +638,8 @@ impl StackMachine { GetVersionstamp => { let trx0 = trx.clone(); - let f = trx.clone() + let f = trx + .clone() .get_versionstamp() .map(move |v| (trx0, v.versionstamp().to_vec())); self.push_fut(number, f); @@ -689,14 +695,16 @@ impl StackMachine { } Commit => { - let f = trx.clone() + let f = trx + .clone() .commit() .map(|trx| (trx, b"RESULT_NOT_PRESENT".to_vec())); self.push_fut(number, f); } GetCommittedVersion => { - let last_version = trx.committed_version() + let last_version = trx + .committed_version() .expect("failed to get committed version"); self.last_version = last_version; self.push_item(number, &b"GOT_COMMITTED_VERSION".to_vec()); @@ -775,7 +783,8 @@ impl StackMachine { } fn run(&mut self) { - let instrs = self.fetch_instr() + let instrs = self + .fetch_instr() .wait() .expect("failed to read instructions"); diff --git a/foundationdb/src/cluster.rs b/foundationdb/src/cluster.rs index da9ea94..d43ae0a 100644 --- a/foundationdb/src/cluster.rs +++ b/foundationdb/src/cluster.rs @@ -11,13 +11,13 @@ //! https://apple.github.io/foundationdb/api-c.html#cluster use foundationdb_sys as fdb; -use future::*; +use crate::future::*; use futures::{Async, Future}; use std; use std::sync::Arc; -use database::*; -use error::*; +use crate::database::*; +use crate::error::*; /// An opaque type that represents a Cluster in the FoundationDB C API. #[derive(Clone)] @@ -46,7 +46,7 @@ impl Cluster { /// Returns an `FdbFuture` which will be set to an `Database` object. /// /// TODO: impl Future - pub fn create_database(&self) -> Box> { + pub fn create_database(&self) -> Box> { let f = unsafe { let f_db = fdb::fdb_cluster_create_database(self.inner.inner, b"DB" as *const _, 2); let cluster = self.clone(); diff --git a/foundationdb/src/database.rs b/foundationdb/src/database.rs index a0410bc..a8d38c1 100644 --- a/foundationdb/src/database.rs +++ b/foundationdb/src/database.rs @@ -17,10 +17,10 @@ use foundationdb_sys as fdb; use futures::future::*; use futures::Future; -use cluster::*; -use error::{self, Error as FdbError, Result}; -use options; -use transaction::*; +use crate::cluster::*; +use crate::error::{self, Error as FdbError, Result}; +use crate::options; +use crate::transaction::*; /// Represents a FoundationDB database — a mutable, lexicographically ordered mapping from binary keys to binary values. /// @@ -68,7 +68,7 @@ impl Database { pub fn transact( &self, f: F, - ) -> Box> + ) -> Box> where F: FnMut(Transaction) -> Fut + 'static, Fut: IntoFuture + 'static, diff --git a/foundationdb/src/error.rs b/foundationdb/src/error.rs index 38f19c1..d4ae715 100644 --- a/foundationdb/src/error.rs +++ b/foundationdb/src/error.rs @@ -15,8 +15,8 @@ use std::fmt::{self, Display}; use failure::{Backtrace, Context, Fail}; use foundationdb_sys as fdb_sys; -use options; -use tuple; +use crate::options; +use crate::tuple; pub(crate) fn eval(error_code: fdb_sys::fdb_error_t) -> Result<()> { let rust_code = error_code as i32; @@ -61,7 +61,7 @@ pub enum ErrorKind { pub type Result = std::result::Result; impl Fail for Error { - fn cause(&self) -> Option<&Fail> { + fn cause(&self) -> Option<&dyn Fail> { self.kind.cause() } diff --git a/foundationdb/src/fdb_api.rs b/foundationdb/src/fdb_api.rs index 4011e73..fc7e082 100644 --- a/foundationdb/src/fdb_api.rs +++ b/foundationdb/src/fdb_api.rs @@ -10,9 +10,9 @@ //! //! https://apple.github.io/foundationdb/api-c.html#api-versioning -use error::{self, Result}; +use crate::error::{self, Result}; use foundationdb_sys as fdb_sys; -use network::NetworkBuilder; +use crate::network::NetworkBuilder; /// Returns the max api version of the underlying Fdb C API Client pub fn get_max_api_version() -> i32 { diff --git a/foundationdb/src/future.rs b/foundationdb/src/future.rs index 1dff0f4..bbf6b97 100644 --- a/foundationdb/src/future.rs +++ b/foundationdb/src/future.rs @@ -21,7 +21,7 @@ use foundationdb_sys as fdb; use futures; use futures::Async; -use error::{self, Error, Result}; +use crate::error::{self, Error, Result}; /// An opaque type that represents a Future in the FoundationDB C API. pub(crate) struct FdbFuture { diff --git a/foundationdb/src/hca.rs b/foundationdb/src/hca.rs index ab1e6f1..390d71b 100644 --- a/foundationdb/src/hca.rs +++ b/foundationdb/src/hca.rs @@ -31,12 +31,12 @@ use futures::future::Future; use futures::stream::Stream; use rand::Rng; -use error::Error; -use keyselector::KeySelector; -use options::{ConflictRangeType, MutationType, TransactionOption}; -use subspace::Subspace; -use transaction::{RangeOptionBuilder, Transaction}; -use tuple::Element; +use crate::error::Error; +use crate::keyselector::KeySelector; +use crate::options::{ConflictRangeType, MutationType, TransactionOption}; +use crate::subspace::Subspace; +use crate::transaction::{RangeOptionBuilder, Transaction}; +use crate::tuple::Element; const ONE_BYTES: &[u8] = &[0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; diff --git a/foundationdb/src/lib.rs b/foundationdb/src/lib.rs index d2cf925..97165aa 100644 --- a/foundationdb/src/lib.rs +++ b/foundationdb/src/lib.rs @@ -140,11 +140,11 @@ pub mod transaction; pub mod tuple; //move to prelude? -pub use cluster::Cluster; -pub use database::Database; -pub use error::Error; -pub use subspace::Subspace; -pub use transaction::Transaction; +pub use crate::cluster::Cluster; +pub use crate::database::Database; +pub use crate::error::Error; +pub use crate::subspace::Subspace; +pub use crate::transaction::Transaction; /// Initialize the FoundationDB Client API, this can only be called once per process. /// diff --git a/foundationdb/src/network.rs b/foundationdb/src/network.rs index 6f3cc9b..e5f0403 100644 --- a/foundationdb/src/network.rs +++ b/foundationdb/src/network.rs @@ -16,10 +16,10 @@ use std::thread; use failure; -use error::{self, Result}; -use fdb_api::FdbApi; +use crate::error::{self, Result}; +use crate::fdb_api::FdbApi; use foundationdb_sys as fdb_sys; -use options::NetworkOption; +use crate::options::NetworkOption; // The Fdb states that setting the Client version should happen only once // and is not thread-safe, thus the choice of a lazy static enforcing a single @@ -135,7 +135,7 @@ mod tests { use std::sync::Arc; use std::thread; - use fdb_api::*; + use crate::fdb_api::*; use super::*; diff --git a/foundationdb/src/subspace.rs b/foundationdb/src/subspace.rs index cb6f61d..c9d6d66 100644 --- a/foundationdb/src/subspace.rs +++ b/foundationdb/src/subspace.rs @@ -8,7 +8,7 @@ //! Implements the FDB Subspace Layer -use tuple::{Decode, Encode, Error, Result}; +use crate::tuple::{Decode, Encode, Error, Result}; /// Represents a well-defined region of keyspace in a FoundationDB database /// @@ -101,7 +101,7 @@ impl Subspace { #[cfg(test)] mod tests { - use tuple::Tuple; + use crate::tuple::Tuple; use super::*; diff --git a/foundationdb/src/transaction.rs b/foundationdb/src/transaction.rs index dc023c5..beb4785 100644 --- a/foundationdb/src/transaction.rs +++ b/foundationdb/src/transaction.rs @@ -15,13 +15,13 @@ use futures::{Async, Future, Stream}; use std; use std::sync::Arc; -use database::*; -use error::{self, *}; -use future::*; -use keyselector::*; -use options; -use subspace::Subspace; -use tuple::Encode; +use crate::database::*; +use crate::error::{self, *}; +use crate::future::*; +use crate::keyselector::*; +use crate::options; +use crate::subspace::Subspace; +use crate::tuple::Encode; /// In FoundationDB, a transaction is a mutable snapshot of a database. /// diff --git a/foundationdb/src/tuple/element.rs b/foundationdb/src/tuple/element.rs index 86ed16f..15529df 100644 --- a/foundationdb/src/tuple/element.rs +++ b/foundationdb/src/tuple/element.rs @@ -3,7 +3,7 @@ use std::{self, io::Write}; use uuid::Uuid; use byteorder::{self, ByteOrder}; -use tuple::{Decode, Encode, Error, Result, Tuple, TupleDepth}; +use crate::tuple::{Decode, Encode, Error, Result, Tuple, TupleDepth}; /// Various tuple types pub(super) const NIL: u8 = 0x00; @@ -594,7 +594,7 @@ impl Decode for Element { #[cfg(test)] mod tests { use super::*; - use tuple::Tuple; + use crate::tuple::Tuple; fn test_round_trip(val: S, buf: &[u8]) where @@ -706,7 +706,7 @@ mod tests { #[test] fn test_decode_nested() { - use tuple::Decode; + use crate::tuple::Decode; assert!(Tuple::try_from(&[NESTED]).is_err()); assert!(Tuple::try_from(&[NESTED, NIL]).is_ok()); diff --git a/foundationdb/tests/atomic.rs b/foundationdb/tests/atomic.rs index 5f9652e..e39ccee 100644 --- a/foundationdb/tests/atomic.rs +++ b/foundationdb/tests/atomic.rs @@ -22,7 +22,7 @@ fn atomic_add( db: Database, key: &[u8], value: i64, -) -> Box> { +) -> Box> { let trx = match db.create_trx() { Ok(trx) => trx, Err(e) => return Box::new(err(e)), From cb078afc38e70e2d1a6a9c876c63d0e4d78f8a14 Mon Sep 17 00:00:00 2001 From: Matt Schallert Date: Wed, 31 Jul 2019 23:27:24 -0400 Subject: [PATCH 2/8] 2018 idioms --- foundationdb-bench/src/bin/fdb-bench.rs | 10 +++++----- foundationdb-gen/src/bin/foundationdb-options-gen.rs | 2 +- foundationdb-gen/src/lib.rs | 4 ++-- foundationdb/examples/class-scheduling.rs | 6 +++--- foundationdb/src/bin/bindingtester.rs | 6 +++--- foundationdb/src/error.rs | 2 +- foundationdb/src/future.rs | 4 ++-- foundationdb/src/lib.rs | 10 +++++----- foundationdb/src/transaction.rs | 2 +- foundationdb/tests/atomic.rs | 6 +++--- foundationdb/tests/common/mod.rs | 4 ++-- foundationdb/tests/get.rs | 4 ++-- foundationdb/tests/hca.rs | 4 ++-- foundationdb/tests/network.rs | 2 +- foundationdb/tests/range.rs | 4 ++-- foundationdb/tests/transact.rs | 4 ++-- foundationdb/tests/watch.rs | 4 ++-- 17 files changed, 39 insertions(+), 39 deletions(-) diff --git a/foundationdb-bench/src/bin/fdb-bench.rs b/foundationdb-bench/src/bin/fdb-bench.rs index b6fcaf6..4afda6c 100644 --- a/foundationdb-bench/src/bin/fdb-bench.rs +++ b/foundationdb-bench/src/bin/fdb-bench.rs @@ -1,11 +1,11 @@ extern crate foundationdb as fdb; -extern crate futures; -extern crate rand; -extern crate stopwatch; + + + #[macro_use] extern crate log; -extern crate env_logger; -extern crate structopt; +use env_logger; +use structopt; use std::sync::atomic::*; use std::sync::Arc; diff --git a/foundationdb-gen/src/bin/foundationdb-options-gen.rs b/foundationdb-gen/src/bin/foundationdb-options-gen.rs index 400e246..3561b5a 100644 --- a/foundationdb-gen/src/bin/foundationdb-options-gen.rs +++ b/foundationdb-gen/src/bin/foundationdb-options-gen.rs @@ -1,4 +1,4 @@ -extern crate foundationdb_gen; +use foundationdb_gen; fn main() { let code = foundationdb_gen::emit().expect("couldn't generate options.rs code!"); diff --git a/foundationdb-gen/src/lib.rs b/foundationdb-gen/src/lib.rs index 4ccc92f..b711242 100644 --- a/foundationdb-gen/src/lib.rs +++ b/foundationdb-gen/src/lib.rs @@ -1,5 +1,5 @@ -extern crate inflector; -extern crate xml; + +use xml; #[macro_use] extern crate failure; diff --git a/foundationdb/examples/class-scheduling.rs b/foundationdb/examples/class-scheduling.rs index 0bb4623..6fa7fe0 100644 --- a/foundationdb/examples/class-scheduling.rs +++ b/foundationdb/examples/class-scheduling.rs @@ -10,9 +10,9 @@ extern crate lazy_static; #[macro_use] extern crate failure; -extern crate foundationdb; -extern crate futures; -extern crate rand; +use foundationdb; + +use rand; use std::borrow::Cow; use std::thread; diff --git a/foundationdb/src/bin/bindingtester.rs b/foundationdb/src/bin/bindingtester.rs index b142ae1..1a148d0 100644 --- a/foundationdb/src/bin/bindingtester.rs +++ b/foundationdb/src/bin/bindingtester.rs @@ -1,6 +1,6 @@ extern crate foundationdb as fdb; extern crate foundationdb_sys as fdb_sys; -extern crate futures; + #[macro_use] extern crate log; @@ -55,7 +55,7 @@ struct Instr { } impl std::fmt::Debug for Instr { - fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(fmt, "[{:?}", self.code)?; if self.database { write!(fmt, " db")?; @@ -293,7 +293,7 @@ impl StackItem { } impl std::fmt::Debug for StackItem { - fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(fmt, "[item num={}, data={:?}]", self.number, self.data) } } diff --git a/foundationdb/src/error.rs b/foundationdb/src/error.rs index d4ae715..925f38e 100644 --- a/foundationdb/src/error.rs +++ b/foundationdb/src/error.rs @@ -71,7 +71,7 @@ impl Fail for Error { } impl Display for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { Display::fmt(&self.kind, f) } } diff --git a/foundationdb/src/future.rs b/foundationdb/src/future.rs index bbf6b97..553a69f 100644 --- a/foundationdb/src/future.rs +++ b/foundationdb/src/future.rs @@ -252,7 +252,7 @@ impl FdbFutureResult { let out_len = out_len as usize; let out_keyvalues: &[fdb::keyvalue] = unsafe { std::slice::from_raw_parts(out_keyvalues, out_len) }; - let out_keyvalues: &[KeyValue] = unsafe { std::mem::transmute(out_keyvalues) }; + let out_keyvalues: &[KeyValue<'_>] = unsafe { std::mem::transmute(out_keyvalues) }; Ok(KeyValues { keyvalues: out_keyvalues, more: (more != 0), @@ -272,6 +272,6 @@ impl FdbFutureResult { fn test_keyvalue_size() { unsafe { // compile-time test for struct size - std::mem::transmute::(std::mem::uninitialized()); + std::mem::transmute::, fdb::keyvalue>(std::mem::uninitialized()); } } diff --git a/foundationdb/src/lib.rs b/foundationdb/src/lib.rs index 97165aa..1fbe592 100644 --- a/foundationdb/src/lib.rs +++ b/foundationdb/src/lib.rs @@ -114,13 +114,13 @@ extern crate failure; #[macro_use] extern crate failure_derive; -extern crate byteorder; -extern crate foundationdb_sys; + + #[macro_use] extern crate futures; -extern crate core; -extern crate lazy_static; -extern crate rand; + + + #[cfg(feature = "uuid")] extern crate uuid; diff --git a/foundationdb/src/transaction.rs b/foundationdb/src/transaction.rs index beb4785..ba53172 100644 --- a/foundationdb/src/transaction.rs +++ b/foundationdb/src/transaction.rs @@ -708,7 +708,7 @@ impl GetRangeResult { } /// Returns the values associated with this get - pub fn key_values(&self) -> KeyValues { + pub fn key_values(&self) -> KeyValues<'_> { self.inner .get_keyvalue_array() .expect("inner should resolve into keyvalue array") diff --git a/foundationdb/tests/atomic.rs b/foundationdb/tests/atomic.rs index e39ccee..c7dbc0d 100644 --- a/foundationdb/tests/atomic.rs +++ b/foundationdb/tests/atomic.rs @@ -5,9 +5,9 @@ // http://opensource.org/licenses/MIT>, at your option. This file may not be // copied, modified, or distributed except according to those terms. -extern crate byteorder; -extern crate foundationdb; -extern crate futures; +use byteorder; +use foundationdb; + #[macro_use] extern crate lazy_static; diff --git a/foundationdb/tests/common/mod.rs b/foundationdb/tests/common/mod.rs index 1e4ff6b..a378b6d 100644 --- a/foundationdb/tests/common/mod.rs +++ b/foundationdb/tests/common/mod.rs @@ -1,5 +1,5 @@ -extern crate rand; -extern crate std; +use rand; +use std; use foundationdb::*; diff --git a/foundationdb/tests/get.rs b/foundationdb/tests/get.rs index ceb47dc..3fea49a 100644 --- a/foundationdb/tests/get.rs +++ b/foundationdb/tests/get.rs @@ -5,8 +5,8 @@ // http://opensource.org/licenses/MIT>, at your option. This file may not be // copied, modified, or distributed except according to those terms. -extern crate foundationdb; -extern crate futures; +use foundationdb; + #[macro_use] extern crate lazy_static; diff --git a/foundationdb/tests/hca.rs b/foundationdb/tests/hca.rs index d5aae70..b6c5604 100644 --- a/foundationdb/tests/hca.rs +++ b/foundationdb/tests/hca.rs @@ -5,8 +5,8 @@ // http://opensource.org/licenses/MIT>, at your option. This file may not be // copied, modified, or distributed except according to those terms. -extern crate foundationdb; -extern crate futures; +use foundationdb; +use futures; #[macro_use] extern crate lazy_static; diff --git a/foundationdb/tests/network.rs b/foundationdb/tests/network.rs index 67e77dd..f3c9033 100644 --- a/foundationdb/tests/network.rs +++ b/foundationdb/tests/network.rs @@ -5,7 +5,7 @@ // http://opensource.org/licenses/MIT>, at your option. This file may not be // copied, modified, or distributed except according to those terms. -extern crate foundationdb; + #[macro_use] extern crate lazy_static; diff --git a/foundationdb/tests/range.rs b/foundationdb/tests/range.rs index 1654246..c309b80 100644 --- a/foundationdb/tests/range.rs +++ b/foundationdb/tests/range.rs @@ -5,8 +5,8 @@ // http://opensource.org/licenses/MIT>, at your option. This file may not be // copied, modified, or distributed except according to those terms. -extern crate foundationdb; -extern crate futures; +use foundationdb; + #[macro_use] extern crate lazy_static; diff --git a/foundationdb/tests/transact.rs b/foundationdb/tests/transact.rs index bd9f30e..45f3290 100644 --- a/foundationdb/tests/transact.rs +++ b/foundationdb/tests/transact.rs @@ -5,8 +5,8 @@ // http://opensource.org/licenses/MIT>, at your option. This file may not be // copied, modified, or distributed except according to those terms. -extern crate foundationdb; -extern crate futures; +use foundationdb; + #[macro_use] extern crate lazy_static; diff --git a/foundationdb/tests/watch.rs b/foundationdb/tests/watch.rs index 6c4e1a4..e5df6a9 100644 --- a/foundationdb/tests/watch.rs +++ b/foundationdb/tests/watch.rs @@ -5,8 +5,8 @@ // http://opensource.org/licenses/MIT>, at your option. This file may not be // copied, modified, or distributed except according to those terms. -extern crate foundationdb; -extern crate futures; +use foundationdb; + #[macro_use] extern crate lazy_static; From 04df0e0a4cfd7db4370d03f63270a0e3b592a013 Mon Sep 17 00:00:00 2001 From: Matt Schallert Date: Wed, 31 Jul 2019 23:34:15 -0400 Subject: [PATCH 3/8] 2018 edition + async-await WIP. TODO: - code cleanup - update docs - clippy --- .rustfmt.toml | 2 + Cargo.toml | 2 +- foundationdb-bench/Cargo.toml | 4 +- foundationdb-bench/src/bin/fdb-bench.rs | 124 ++++---- foundationdb-bench/src/bin/fdb-test.rs | 265 ++++++++++++++++ foundationdb-gen/src/lib.rs | 10 +- foundationdb-sys/build.rs | 5 +- foundationdb/Cargo.toml | 5 +- foundationdb/build.rs | 5 +- foundationdb/examples/class-scheduling.rs | 231 +++++++------- foundationdb/src/bin/bindingtester.rs | 241 ++++++++------- foundationdb/src/cluster.rs | 52 +--- foundationdb/src/database.rs | 70 ++--- foundationdb/src/error.rs | 11 +- foundationdb/src/fdb_api.rs | 6 +- foundationdb/src/future.rs | 81 +++-- foundationdb/src/hca.rs | 51 ++-- foundationdb/src/lib.rs | 94 +++--- foundationdb/src/network.rs | 19 +- foundationdb/src/transaction.rs | 337 ++++++++++---------- foundationdb/src/tuple/element.rs | 2 +- foundationdb/src/tuple/mod.rs | 14 +- foundationdb/tests/atomic.rs | 105 +++---- foundationdb/tests/common/mod.rs | 11 +- foundationdb/tests/get.rs | 355 ++++++++++------------ foundationdb/tests/hca.rs | 108 +++---- foundationdb/tests/network.rs | 1 + foundationdb/tests/range.rs | 76 +++-- foundationdb/tests/transact.rs | 22 +- foundationdb/tests/watch.rs | 106 +++---- scripts/install_foundationdb_macos.sh | 2 +- scripts/run_bindingtester.sh | 30 +- 32 files changed, 1324 insertions(+), 1123 deletions(-) create mode 100644 .rustfmt.toml create mode 100644 foundationdb-bench/src/bin/fdb-test.rs diff --git a/.rustfmt.toml b/.rustfmt.toml new file mode 100644 index 0000000..4afdfbd --- /dev/null +++ b/.rustfmt.toml @@ -0,0 +1,2 @@ +unstable_features = true +merge_imports = true diff --git a/Cargo.toml b/Cargo.toml index 695225b..889cd83 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,5 @@ [workspace] -members = ["foundationdb", +members = ["foundationdb", "foundationdb-sys", "foundationdb-gen", "foundationdb-bench"] diff --git a/foundationdb-bench/Cargo.toml b/foundationdb-bench/Cargo.toml index e9d2f6f..a5f58cb 100644 --- a/foundationdb-bench/Cargo.toml +++ b/foundationdb-bench/Cargo.toml @@ -22,11 +22,13 @@ travis-ci = { repository = "bluejekyll/foundationdb-rs" } [dependencies] rand = "0.7" -futures = "0.1" +futures-preview = { version = "0.3.0-alpha.17", features = ["compat", "nightly", "async-await"] } foundationdb = { version = "*", path = "../foundationdb" } stopwatch = "0" log = "0.4" env_logger = "0.6" structopt = "0.2" +tokio = "=0.2.0-alpha.2" +# tokio-sync = "=0.2.0-alpha.1" [build-dependencies] diff --git a/foundationdb-bench/src/bin/fdb-bench.rs b/foundationdb-bench/src/bin/fdb-bench.rs index 4afda6c..1789228 100644 --- a/foundationdb-bench/src/bin/fdb-bench.rs +++ b/foundationdb-bench/src/bin/fdb-bench.rs @@ -1,23 +1,19 @@ -extern crate foundationdb as fdb; +#![feature(async_await)] - - -#[macro_use] -extern crate log; use env_logger; +use log::info; use structopt; -use std::sync::atomic::*; -use std::sync::Arc; +use std::sync::{atomic::*, Arc}; use futures::future::*; -use rand::prelude::*; -use rand::rngs::mock::StepRng; +use rand::{prelude::*, rngs::mock::StepRng}; use stopwatch::Stopwatch; use structopt::StructOpt; -use crate::fdb::error::*; -use crate::fdb::*; +use tokio::{prelude::*, runtime::Builder}; + +use foundationdb::{self as fdb, error::*, *}; #[derive(Clone)] struct Counter { @@ -71,33 +67,22 @@ impl BenchRunner { } } - //TODO: impl future - fn run(self) -> Box> { - Box::new(loop_fn(self, Self::step)) - } - - //TODO: impl future - fn step(mut self) -> Box, Error = Error>> { - let trx = self.trx.take().unwrap(); - - for _ in 0..self.trx_batch_size { - self.rng.fill_bytes(&mut self.key_buf); - self.rng.fill_bytes(&mut self.val_buf); - self.key_buf[0] = 0x01; - trx.set(&self.key_buf, &self.val_buf); - } + async fn run(mut self) -> Result<()> { + let mut trx = self.trx.take().unwrap(); + loop { + for _ in 0..self.trx_batch_size { + self.rng.fill_bytes(&mut self.key_buf); + self.rng.fill_bytes(&mut self.val_buf); + self.key_buf[0] = 0x01; + trx.set(&self.key_buf, &self.val_buf); + } - let f = trx.commit().map(move |trx| { + trx = trx.commit().await?; trx.reset(); - self.trx = Some(trx); - - if self.counter.decr(self.trx_batch_size) { - Loop::Continue(self) - } else { - Loop::Break(()) + if !self.counter.decr(self.trx_batch_size) { + return Ok(()); } - }); - Box::new(f) + } } } @@ -108,34 +93,23 @@ struct Bench { } impl Bench { - fn run(self) { + async fn run(self) { let opt = &self.opt; let counter = Counter::new(opt.count); - let mut handles = Vec::new(); - let sw = Stopwatch::start_new(); let step = (opt.queue_depth + opt.threads - 1) / opt.threads; - let mut start = 0; - for _ in 0..opt.threads { - let end = std::cmp::min(start + step, opt.queue_depth); - - let range = start..end; - let counter = counter.clone(); - let b = self.clone(); - let handle = std::thread::spawn(move || b.run_range(range, counter).wait()); - handles.push(handle); - start = end; - } + let start = 0; + let end = std::cmp::min(start + step, opt.queue_depth); + let range = start..end; + let counter = counter.clone(); + let b = self.clone(); - for handle in handles { - handle - .join() - .expect("failed to join") - .expect("failed to run bench"); - } + b.run_range(range, counter) + .await + .expect("error running range"); let elapsed = sw.elapsed_ms() as usize; @@ -146,11 +120,7 @@ impl Bench { ); } - fn run_range( - &self, - r: std::ops::Range, - counter: Counter, - ) -> Box> { + async fn run_range(&self, r: std::ops::Range, counter: Counter) -> Result<()> { let runners = r .into_iter() .map(|n| { @@ -158,10 +128,11 @@ impl Bench { // of keys again, which makes benchmark result stable. let rng = StepRng::new(n as u64, 1); BenchRunner::new(self.db.clone(), rng, counter.clone(), &self.opt).run() - }).collect::>(); + }) + .collect::>(); - let f = join_all(runners).map(|_| ()); - Box::new(f) + join_all(runners).await; + Ok(()) } } @@ -192,6 +163,11 @@ fn main() { info!("opt: {:?}", opt); + let rt = Builder::new() + .core_threads(opt.threads) + .build() + .expect("failed to build tokio runtime"); + let network = fdb::init().expect("failed to init network"); let handle = std::thread::spawn(move || { @@ -204,18 +180,20 @@ fn main() { network.wait(); - let cluster_path = fdb::default_config_path(); - let cluster = Cluster::new(cluster_path) - .wait() - .expect("failed to create cluster"); + rt.block_on(async { + let cluster_path = fdb::default_config_path(); + let cluster = Cluster::new(cluster_path) + .await + .expect("failed to create cluster"); - let db = cluster - .create_database() - .wait() - .expect("failed to get database"); + let db = cluster + .create_database() + .await + .expect("failed to get database"); - let bench = Bench { db, opt }; - bench.run(); + let bench = Bench { db, opt }; + bench.run().await; + }); network.stop().expect("failed to stop network"); handle.join().expect("failed to join fdb thread"); diff --git a/foundationdb-bench/src/bin/fdb-test.rs b/foundationdb-bench/src/bin/fdb-test.rs new file mode 100644 index 0000000..3674c01 --- /dev/null +++ b/foundationdb-bench/src/bin/fdb-test.rs @@ -0,0 +1,265 @@ +#![feature(async_await)] + +extern crate foundationdb as fdb; + +#[macro_use] +extern crate log; +use env_logger; +use structopt; + +use std::sync::{atomic::*, Arc}; + +use futures::future::*; +use rand::{prelude::*, rngs::mock::StepRng}; +use stopwatch::Stopwatch; +use structopt::StructOpt; + +use tokio::{prelude::*, runtime::Builder}; + +use crate::fdb::{error::*, *}; + +#[derive(Clone)] +struct Counter { + size: usize, + inner: Arc, +} +impl Counter { + fn new(size: usize) -> Self { + Self { + size, + inner: Default::default(), + } + } + + fn decr(&self, n: usize) -> bool { + let val = self.inner.fetch_add(n, Ordering::SeqCst); + val < self.size + } +} + +struct BenchRunner { + #[allow(unused)] + db: Database, + counter: Counter, + key_buf: Vec, + val_buf: Vec, + rng: StepRng, + trx: Option, + trx_batch_size: usize, +} + +impl BenchRunner { + fn new(db: Database, rng: StepRng, counter: Counter, opt: &Opt) -> Self { + let mut key_buf = Vec::with_capacity(opt.key_len); + key_buf.resize(opt.key_len, 0u8); + + let mut val_buf = Vec::with_capacity(opt.val_len); + val_buf.resize(opt.val_len, 0u8); + + let trx = db.create_trx().expect("failed to create trx"); + + Self { + db, + counter, + key_buf, + val_buf, + + rng, + trx: Some(trx), + trx_batch_size: opt.trx_batch_size, + } + } + + async fn run(mut self) -> Result<()> { + let mut trx = self.trx.take().unwrap(); + loop { + for _ in 0..self.trx_batch_size { + self.rng.fill_bytes(&mut self.key_buf); + self.rng.fill_bytes(&mut self.val_buf); + self.key_buf[0] = 0x01; + trx.set(&self.key_buf, &self.val_buf); + } + + trx = trx.commit().await?; + trx.reset(); + if !self.counter.decr(self.trx_batch_size) { + return Ok(()); + } + } + } + + // fn step(mut self) -> Box, Error = Error>> { + // let trx = self.trx.take().unwrap(); + + // for _ in 0..self.trx_batch_size { + // self.rng.fill_bytes(&mut self.key_buf); + // self.rng.fill_bytes(&mut self.val_buf); + // self.key_buf[0] = 0x01; + // trx.set(&self.key_buf, &self.val_buf); + // } + + // let f = trx.commit().map(move |trx| { + // trx.reset(); + // self.trx = Some(trx); + + // if self.counter.decr(self.trx_batch_size) { + // Loop::Continue(self) + // } else { + // Loop::Break(()) + // } + // }); + // Box::new(f) + // } +} + +#[derive(Clone)] +struct Bench { + db: Database, + opt: Opt, +} + +impl Bench { + async fn run(self) { + let opt = &self.opt; + let counter = Counter::new(opt.count); + + let mut handles = Vec::new(); + + let sw = Stopwatch::start_new(); + + let step = (opt.queue_depth + opt.threads - 1) / opt.threads; + let mut start = 0; + for _ in 0..opt.threads { + let end = std::cmp::min(start + step, opt.queue_depth); + + let range = start..end; + let counter = counter.clone(); + let b = self.clone(); + // let handle = std::thread::spawn(move || b.run_range(range, counter).await); + // let handle = + // spawn( + // async move { poll_fn(move |_| blocking(|| b.run_range(range, counter))).await }, + // ); + let handle = async move { b.run_range(range, counter).await }; + handles.push(handle); + + start = end; + } + + for handle in handles { + handle.await.expect("failed to run bench"); + } + + let elapsed = sw.elapsed_ms() as usize; + + info!( + "bench took: {:?} ms, {:?} tps", + elapsed, + 1000 * opt.count / elapsed + ); + } + + async fn run_range(&self, r: std::ops::Range, counter: Counter) -> Result<()> { + let runners = r + .into_iter() + .map(|n| { + // With deterministic Rng, benchmark with same parameters will overwrite same set + // of keys again, which makes benchmark result stable. + let rng = StepRng::new(n as u64, 1); + BenchRunner::new(self.db.clone(), rng, counter.clone(), &self.opt).run() + }) + .collect::>(); + + join_all(runners).await; + Ok(()) + } +} + +#[derive(StructOpt, Debug, Clone)] +#[structopt(name = "fdb-bench")] +struct Opt { + #[structopt(short = "t", long = "threads", default_value = "1")] + threads: usize, + + #[structopt(short = "q", long = "queue-depth", default_value = "1000")] + queue_depth: usize, + + #[structopt(short = "c", long = "count", default_value = "300000")] + count: usize, + + #[structopt(long = "trx-batch-size", default_value = "10")] + trx_batch_size: usize, + + #[structopt(long = "key-len", default_value = "10")] + key_len: usize, + #[structopt(long = "val-len", default_value = "100")] + val_len: usize, +} + +fn main() { + env_logger::init(); + let opt = Opt::from_args(); + + info!("opt: {:?}", opt); + + let rt = Builder::new() + .build() + .expect("failed to build tokio runtime"); + + let network = fdb::init().expect("failed to init network"); + + let handle = std::thread::spawn(move || { + let error = network.run(); + + if let Err(error) = error { + panic!("fdb_run_network: {}", error); + } + }); + + network.wait(); + + rt.block_on(async { + let cluster_path = fdb::default_config_path(); + let cluster = Cluster::new(cluster_path) + .await + .expect("failed to create cluster"); + + let db = Arc::new( + cluster + .create_database() + .await + .expect("failed to get database"), + ); + + // { + // let trx = db.create_trx().unwrap(); + // let begin = keyselector::KeySelector::first_greater_or_equal(b"\x00"); + // let end = keyselector::KeySelector::first_greater_or_equal(b"\xff"); + // let range = RangeOptionBuilder::new(begin, end).build(); + // let res = trx.get_range(range, 100).await.unwrap(); + + // while let Some(kv) = res.next() { + // println!("{:?}", kv); + // } + // } + // return; + + let mut handles = Vec::new(); + for i in 0..opt.count { + let s = format!("foo-{}", i); + let db = db.clone(); + handles.push(async move { + let s = s.as_bytes(); + let trx = db.create_trx().unwrap(); + trx.set(s, s); + trx.commit().await.unwrap(); + }); + } + + join_all(handles).await; + println!("put foo at bar"); + }); + + network.stop().expect("failed to stop network"); + handle.join().expect("failed to join fdb thread"); +} diff --git a/foundationdb-gen/src/lib.rs b/foundationdb-gen/src/lib.rs index b711242..6dc3fda 100644 --- a/foundationdb-gen/src/lib.rs +++ b/foundationdb-gen/src/lib.rs @@ -1,14 +1,14 @@ - use xml; #[macro_use] extern crate failure; type Result = std::result::Result; -use inflector::cases::classcase; -use inflector::cases::screamingsnakecase; -use xml::attribute::OwnedAttribute; -use xml::reader::{EventReader, XmlEvent}; +use inflector::cases::{classcase, screamingsnakecase}; +use xml::{ + attribute::OwnedAttribute, + reader::{EventReader, XmlEvent}, +}; #[derive(Debug)] struct FdbScope { diff --git a/foundationdb-sys/build.rs b/foundationdb-sys/build.rs index 22d825f..5d53362 100644 --- a/foundationdb-sys/build.rs +++ b/foundationdb-sys/build.rs @@ -1,9 +1,6 @@ extern crate bindgen; -use std::env; -use std::fs::File; -use std::io::prelude::*; -use std::path::PathBuf; +use std::{env, fs::File, io::prelude::*, path::PathBuf}; #[cfg(target_os = "linux")] const INCLUDE_PATH: &str = "-I/usr/include/foundationdb/"; diff --git a/foundationdb/Cargo.toml b/foundationdb/Cargo.toml index 99b2118..2f224e0 100644 --- a/foundationdb/Cargo.toml +++ b/foundationdb/Cargo.toml @@ -34,12 +34,15 @@ foundationdb-gen = { version = "0.1.0", path = "../foundationdb-gen" } failure = "0.1" failure_derive = "0.1" foundationdb-sys = { version = "0.2.0", path = "../foundationdb-sys", default-features = false } -futures = "0.1" +futures-preview = { version = "0.3.0-alpha.17", features = ["compat", "nightly", "async-await"] } +futures01 = { package = "futures", version = "0.1", optional = true } lazy_static = "1.0" byteorder = "1.2" log = "0.4" uuid = { version = "0.7", optional = true } rand = "0.7.0" +tokio = "=0.2.0-alpha.2" +tokio-sync = "=0.2.0-alpha.2" [dev-dependencies] rand = "0.7" diff --git a/foundationdb/build.rs b/foundationdb/build.rs index 4a9ea6d..31d65fe 100644 --- a/foundationdb/build.rs +++ b/foundationdb/build.rs @@ -1,9 +1,6 @@ extern crate foundationdb_gen; -use std::env; -use std::fs::File; -use std::io::prelude::*; -use std::path::PathBuf; +use std::{env, fs::File, io::prelude::*, path::PathBuf}; fn main() { let out_path = PathBuf::from(env::var("OUT_DIR").expect("OUT_DIR is undefined!")); diff --git a/foundationdb/examples/class-scheduling.rs b/foundationdb/examples/class-scheduling.rs index 6fa7fe0..333f4b1 100644 --- a/foundationdb/examples/class-scheduling.rs +++ b/foundationdb/examples/class-scheduling.rs @@ -6,6 +6,8 @@ // http://opensource.org/licenses/MIT>, at your option. This file may not be // copied, modified, or distributed except according to those terms. +#![feature(async_await, async_closure)] + #[macro_use] extern crate lazy_static; #[macro_use] @@ -14,16 +16,19 @@ use foundationdb; use rand; -use std::borrow::Cow; -use std::thread; +use std::{borrow::Cow, thread}; use self::rand::{rngs::ThreadRng, seq::SliceRandom}; -use futures::future::{self, Future}; use foundationdb as fdb; -use foundationdb::transaction::RangeOptionBuilder; -use foundationdb::tuple::{Decode, Encode}; -use foundationdb::{Cluster, Database, Subspace, Transaction}; +use foundationdb::{ + error::Result as FdbResult, + transaction::RangeOptionBuilder, + tuple::{Decode, Encode}, + Cluster, Database, Subspace, Transaction, +}; + +use tokio::prelude::*; // Data model: // ("attends", student, class) = "" @@ -76,45 +81,42 @@ fn init_classes(trx: &Transaction, all_classes: &[String]) { } } -fn init(db: &Database, all_classes: &[String]) { +async fn init(db: &Database, all_classes: &[String]) { let trx = db.create_trx().expect("could not create transaction"); trx.clear_subspace_range("attends"); trx.clear_subspace_range("class"); init_classes(&trx, all_classes); - trx.commit().wait().expect("failed to initialize data"); + trx.commit().await.expect("failed to initialize data"); } -fn get_available_classes(db: &Database) -> Vec { +async fn get_available_classes(db: &Database) -> FdbResult> { let trx = db.create_trx().expect("could not create transaction"); let range = RangeOptionBuilder::from("class"); - trx.get_range(range.build(), 1_024) - .and_then(|got_range| { - let mut available_classes = Vec::::new(); + let got_range = trx.get_range(range.build(), 1_024).await?; + let mut available_classes = Vec::::new(); - for key_value in got_range.key_values().as_ref() { - let count = i64::try_from(key_value.value()).expect("failed to decode count"); + for key_value in got_range.key_values().as_ref() { + let count = i64::try_from(key_value.value()).expect("failed to decode count"); - if count > 0 { - let class = String::try_from(key_value.key()).expect("failed to decode class"); - available_classes.push(class); - } - } + if count > 0 { + let class = String::try_from(key_value.key()).expect("failed to decode class"); + available_classes.push(class); + } + } - future::ok(available_classes) - }).wait() - .expect("failed to get classes") + Ok(available_classes) } -fn ditch_trx(trx: &Transaction, student: &str, class: &str) { +async fn ditch_trx(trx: &Transaction, student: &str, class: &str) { let attends_key = ("attends", student, class).to_vec(); // TODO: should get take an &Encode? current impl does encourage &[u8] reuse... if trx .get(&attends_key, true) - .wait() + .await .expect("get failed") .value() .is_none() @@ -125,11 +127,12 @@ fn ditch_trx(trx: &Transaction, student: &str, class: &str) { let class_key = ("class", class).to_vec(); let available_seats: i64 = i64::try_from( trx.get(&class_key, true) - .wait() + .await .expect("get failed") .value() .expect("class seats were not initialized"), - ).expect("failed to decode i64") + ) + .expect("failed to decode i64") + 1; //println!("{} ditching class: {}", student, class); @@ -137,19 +140,22 @@ fn ditch_trx(trx: &Transaction, student: &str, class: &str) { trx.clear(&attends_key); } -fn ditch(db: &Database, student: String, class: String) -> Result<(), failure::Error> { - db.transact(move |trx| { - ditch_trx(&trx, &student, &class); - future::result(Ok::<(), failure::Error>(())) - }).wait() - .map_err(|e| format_err!("error in signup: {}", e)) +async fn ditch(db: &Database, student: String, class: String) -> Result<(), failure::Error> { + let student = &student.clone(); + let class = &class.clone(); + db.transact(async move |trx| { + ditch_trx(&trx, student, class); + Ok::<(), failure::Error>(()) + }) + .await?; + Ok(()) } -fn signup_trx(trx: &Transaction, student: &str, class: &str) -> Result<(), failure::Error> { +async fn signup_trx(trx: &Transaction, student: &str, class: &str) -> Result<(), failure::Error> { let attends_key = ("attends", student, class).to_vec(); if trx .get(&attends_key, true) - .wait() + .await .expect("get failed") .value() .is_some() @@ -161,11 +167,12 @@ fn signup_trx(trx: &Transaction, student: &str, class: &str) -> Result<(), failu let class_key = ("class", class).to_vec(); let available_seats: i64 = i64::try_from( trx.get(&class_key, true) - .wait() + .await .expect("get failed") .value() .expect("class seats were not initialized"), - ).expect("failed to decode i64"); + ) + .expect("failed to decode i64"); if available_seats <= 0 { bail!("No remaining seats"); @@ -174,7 +181,7 @@ fn signup_trx(trx: &Transaction, student: &str, class: &str) -> Result<(), failu let attends_range = RangeOptionBuilder::from(("attends", student)).build(); if trx .get_range(attends_range, 1_024) - .wait() + .await .expect("get_range failed") .key_values() .len() @@ -189,23 +196,33 @@ fn signup_trx(trx: &Transaction, student: &str, class: &str) -> Result<(), failu Ok(()) } -fn signup(db: &Database, student: String, class: String) -> Result<(), failure::Error> { - db.transact(move |trx| future::result(signup_trx(&trx, &student, &class))) - .wait() - .map_err(|e| format_err!("error in signup: {}", e)) +async fn signup(db: &Database, student: String, class: String) -> Result<(), failure::Error> { + let student = &student.clone(); + let class = &class.clone(); + db.transact(async move |trx| { + signup_trx(&trx, student, class).await?; + Ok::<(), failure::Error>(()) + }) + .await?; + Ok(()) } -fn switch_classes( +async fn switch_classes( db: &Database, student_id: String, old_class: String, new_class: String, ) -> Result<(), failure::Error> { - db.transact(move |trx| { - ditch_trx(&trx, &student_id, &old_class); - future::result(signup_trx(&trx, &student_id, &new_class)) - }).wait() - .map_err(|e| format_err!("error in switch: {}", e)) + let student_id = &student_id.clone(); + let old_class = &old_class.clone(); + let new_class = &new_class.clone(); + db.transact(async move |trx| { + ditch_trx(&trx, student_id, old_class).await; + signup_trx(&trx, student_id, new_class).await?; + Ok::<(), failure::Error>(()) + }) + .await?; + Ok(()) } #[derive(Clone, Copy, Debug, Eq, PartialEq)] @@ -215,7 +232,7 @@ enum Mood { Switch, } -fn perform_op( +async fn perform_op( db: &Database, rng: &mut ThreadRng, mood: Mood, @@ -226,12 +243,12 @@ fn perform_op( match mood { Mood::Add => { let class = all_classes.choose(rng).unwrap(); - signup(&db, student_id.to_string(), class.to_string())?; + signup(&db, student_id.to_string(), class.to_string()).await?; my_classes.push(class.to_string()); } Mood::Ditch => { let class = all_classes.choose(rng).unwrap(); - ditch(&db, student_id.to_string(), class.to_string())?; + ditch(&db, student_id.to_string(), class.to_string()).await?; my_classes.retain(|s| s != class); } Mood::Switch => { @@ -242,7 +259,8 @@ fn perform_op( student_id.to_string(), old_class.to_string(), new_class.to_string(), - )?; + ) + .await?; my_classes.retain(|s| s != &old_class); my_classes.push(new_class.to_string()); } @@ -250,51 +268,55 @@ fn perform_op( Ok(()) } -fn simulate_students(student_id: usize, num_ops: usize) { - Cluster::new(foundationdb::default_config_path()) - .and_then(|cluster| cluster.create_database()) - .and_then(|db| { - let student_id = format!("s{}", student_id); - let mut rng = rand::thread_rng(); - - let mut available_classes = Cow::Borrowed(&*ALL_CLASSES); - let mut my_classes = Vec::::new(); - - for _ in 0..num_ops { - let mut moods = Vec::::new(); - - if my_classes.len() > 0 { - moods.push(Mood::Ditch); - moods.push(Mood::Switch); - } - - if my_classes.len() < 5 { - moods.push(Mood::Add); - } - - let mood = moods.choose(&mut rng).map(|mood| *mood).unwrap(); - - // on errors we recheck for available classes - if perform_op( - &db, - &mut rng, - mood, - &student_id, - &available_classes, - &mut my_classes, - ).is_err() - { - println!("getting available classes"); - available_classes = Cow::Owned(get_available_classes(&db)); - } - } +async fn simulate_students(student_id: usize, num_ops: usize) { + let db = Cluster::new(foundationdb::default_config_path()) + .await + .expect("expected new cluster"); + let db = db.create_database().await.expect("expected DB"); + + let student_id = format!("s{}", student_id); + let mut rng = rand::thread_rng(); - future::ok(()) - }).wait() - .expect("got error in simulation"); + let mut available_classes = Cow::Borrowed(&*ALL_CLASSES); + let mut my_classes = Vec::::new(); + + for _ in 0..num_ops { + let mut moods = Vec::::new(); + + if my_classes.len() > 0 { + moods.push(Mood::Ditch); + moods.push(Mood::Switch); + } + + if my_classes.len() < 5 { + moods.push(Mood::Add); + } + + let mood = moods.choose(&mut rng).map(|mood| *mood).unwrap(); + + // on errors we recheck for available classes + if perform_op( + &db, + &mut rng, + mood, + &student_id, + &available_classes, + &mut my_classes, + ) + .await + .is_err() + { + println!("getting available classes"); + available_classes = Cow::Owned( + get_available_classes(&db) + .await + .expect("error getting classes"), + ); + } + } } -fn run_sim(db: &Database, students: usize, ops_per_student: usize) { +async fn run_sim(db: &Database, students: usize, ops_per_student: usize) { let mut threads: Vec<(usize, thread::JoinHandle<()>)> = Vec::with_capacity(students); for i in 0..students { // TODO: ClusterInner has a mutable pointer reference, if thread-safe, mark that trait as Sync, then we can clone DB here... @@ -317,7 +339,7 @@ fn run_sim(db: &Database, students: usize, ops_per_student: usize) { .create_trx() .unwrap() .get_range(attends_range, 1_024) - .wait() + .await .expect("get_range failed") .key_values() .into_iter() @@ -332,7 +354,8 @@ fn run_sim(db: &Database, students: usize, ops_per_student: usize) { println!("Ran {} transactions", students * ops_per_student); } -fn main() { +#[tokio::main] +async fn main() { let network = fdb::init().expect("failed to initialize FoundationDB"); let handle = thread::spawn(move || { @@ -345,16 +368,14 @@ fn main() { network.wait(); // run scheduling - Cluster::new(foundationdb::default_config_path()) - .and_then(|cluster| cluster.create_database()) - .and_then(|db| { - init(&db, &*ALL_CLASSES); - println!("Initialized"); - run_sim(&db, 10, 10); - - future::ok(()) - }).wait() - .expect("failed to create cluster"); + let db = Cluster::new(foundationdb::default_config_path()) + .await + .expect("expected new cluster"); + let db = db.create_database().await.expect("expected DB"); + + init(&db, &*ALL_CLASSES); + println!("Initialized"); + run_sim(&db, 10, 10).await; // shutdown network.stop().expect("failed to stop network"); diff --git a/foundationdb/src/bin/bindingtester.rs b/foundationdb/src/bin/bindingtester.rs index 1a148d0..7362074 100644 --- a/foundationdb/src/bin/bindingtester.rs +++ b/foundationdb/src/bin/bindingtester.rs @@ -1,17 +1,20 @@ +#![feature(async_await, async_closure)] extern crate foundationdb as fdb; extern crate foundationdb_sys as fdb_sys; #[macro_use] extern crate log; -use std::collections::HashMap; +use std::{collections::HashMap, pin::Pin}; -use crate::fdb::error::Error; -use crate::fdb::keyselector::KeySelector; -use crate::fdb::tuple::*; -use crate::fdb::*; -use futures::future::*; -use futures::prelude::*; +use crate::fdb::{ + error::{Error, Result as FdbResult}, + keyselector::KeySelector, + tuple::*, + *, +}; +use futures::{future::*, prelude::*}; +use tokio::{prelude::*, runtime::Runtime}; use crate::fdb::options::{MutationType, StreamingMode}; fn mutation_from_str(s: &str) -> MutationType { @@ -46,6 +49,7 @@ pub fn streaming_from_value(val: i32) -> StreamingMode { } } +#[derive(Clone)] struct Instr { code: InstrCode, database: bool, @@ -112,7 +116,7 @@ impl Instr { } } -#[derive(Debug)] +#[derive(Clone, Debug)] enum InstrCode { // data operations Push(Vec), @@ -251,7 +255,9 @@ impl Instr { } } -type StackFuture = Box), Error = Error>>; +type StackFuture = + Pin), Error>>>>; + struct StackItem { number: usize, // TODO: enum @@ -273,13 +279,13 @@ impl Clone for StackItem { } impl StackItem { - fn data(self) -> Vec { + async fn data(self) -> Vec { if let Some(data) = self.data { return data; } //TODO: wait - match self.fut.unwrap().wait() { + match self.fut.unwrap().await { Ok((_trx, data)) => data.to_vec(), Err(e) => { let code = format!("{}", e.code()); @@ -350,39 +356,42 @@ impl StackMachine { } } - fn fetch_instr(&self) -> Box, Error = Error>> { + // fn fetch_instr(&self) -> Box, Error = Error>> { + async fn fetch_instr(&self) -> FdbResult> { let db = self.db.clone(); - - let prefix = self.prefix.clone(); - let f = db.transact(move |trx| { - let opt = transaction::RangeOptionBuilder::from(&prefix).build(); - let instrs = Vec::new(); - let f = trx + let prefix = &self.prefix.clone(); + let fun = async move |trx: Transaction| { + let opt = transaction::RangeOptionBuilder::from(prefix).build(); + let res = trx .get_ranges(opt) - .map_err(|(_opt, e)| e) - .fold(instrs, |mut instrs, res| { - let kvs = res.key_values(); - + .try_fold(Vec::new(), |mut out, range_res| { + let kvs = range_res.key_values(); for kv in kvs.as_ref() { let instr = Instr::from(kv.value()); - instrs.push(instr); + out.push(instr); } - Ok::<_, Error>(instrs) - }); - f - }); - Box::new(f) + ready(Ok(out)) + }) + .await; + + match res { + Ok(v) => Ok(v), + Err((_, e)) => Err(e), + } + }; + + db.transact(fun).await } fn pop(&mut self) -> StackItem { self.stack.pop().expect("stack empty") } - fn pop_item(&mut self) -> S + async fn pop_item(&mut self) -> S where S: Decode, { - let data = self.pop().data(); + let data = self.pop().data().await; match Decode::try_from(&data) { Ok(v) => v, Err(e) => { @@ -391,14 +400,14 @@ impl StackMachine { } } - fn pop_data(&mut self) -> Vec { - self.pop().data() + async fn pop_data(&mut self) -> Vec { + self.pop().data().await } - fn pop_selector(&mut self) -> KeySelector { - let key: Vec = self.pop_item(); - let or_equal: i64 = self.pop_item(); - let offset: i64 = self.pop_item(); + async fn pop_selector(&mut self) -> KeySelector { + let key: Vec = self.pop_item().await; + let or_equal: i64 = self.pop_item().await; + let offset: i64 = self.pop_item().await; KeySelector::new(key, or_equal != 0, offset as usize) } @@ -421,17 +430,17 @@ impl StackMachine { fn push_fut(&mut self, number: usize, fut: F) where - F: Future), Error = Error> + 'static, + F: Future), Error>> + 'static, { let item = StackItem { number, data: None, - fut: Some(Box::new(fut)), + fut: Some(Box::pin(fut)), }; self.stack.push(item); } - fn run_step(&mut self, number: usize, mut instr: Instr) { + async fn run_step(&mut self, number: usize, mut instr: Instr) { use crate::InstrCode::*; let is_db = instr.pop_database(); @@ -454,7 +463,7 @@ impl StackMachine { } EmptyStack => self.stack.clear(), Swap => { - let idx: i64 = self.pop_item(); + let idx: i64 = self.pop_item().await; { let len = self.stack.len(); let idx1 = len - 1; @@ -470,14 +479,14 @@ impl StackMachine { self.pop(); } Sub => { - let a: i64 = self.pop_item(); - let b: i64 = self.pop_item(); + let a: i64 = self.pop_item().await; + let b: i64 = self.pop_item().await; self.push_item(number, &(a - b)); } Concat => { - let mut a: Vec = self.pop_item(); - let mut b: Vec = self.pop_item(); + let mut a: Vec = self.pop_item().await; + let mut b: Vec = self.pop_item().await; a.append(&mut b); self.push_item(number, &a); } @@ -489,21 +498,24 @@ impl StackMachine { self.transactions.insert(name, trx); } UseTransacton => { - let name: Vec = self.pop_item(); + let name: Vec = self.pop_item().await; self.cur_transaction = name; } OnError => { - let code: i64 = self.pop_item(); + let code: i64 = self.pop_item().await; let trx0 = trx.clone(); - let f = trx - .on_error(Error::from(code as i32)) - .map(move |_| (trx0, b"RESULT_NOT_PRESENT".to_vec())); + let f = async move { + trx0.on_error(Error::from(code as i32)).await?; + Ok((trx0, b"RESULT_NOT_PRESENT".to_vec())) + }; self.push_fut(number, f); } Get => { - let key: Vec = self.pop_item(); + let key: Vec = self.pop_item().await; let trx0 = trx.clone(); - let f = trx.get(&key, instr.pop_snapshot()).map(move |res| { + let v = instr.pop_snapshot(); + let f = async move { + let res = trx0.get(&key, v).await?; let val = res.value(); let val = match val { Some(v) => v.to_vec(), @@ -511,22 +523,23 @@ impl StackMachine { }; debug!("get : key={:?}, value={:?}", key, val); - (trx0, val) - }); + Ok((trx0, val)) + }; self.push_fut(number, f); } GetKey => { - let selector = self.pop_selector(); - let mut prefix: Vec = self.pop_item(); + let selector = self.pop_selector().await; + let mut prefix: Vec = self.pop_item().await; //TODO: wait let key = trx .get_key(selector, instr.pop_snapshot()) - .map(move |res| res.value().to_vec()) - .wait() - .unwrap(); + .await + .unwrap() + .value() + .to_vec(); if key.starts_with(&prefix) { self.push_item(number, &key); @@ -542,7 +555,7 @@ impl StackMachine { let selector = instr.pop_selector(); let (begin, end) = if instr.pop_starts_with() { - let begin: Vec = self.pop_item(); + let begin: Vec = self.pop_item().await; let mut end = begin.clone(); strinc(&mut end); ( @@ -550,21 +563,21 @@ impl StackMachine { KeySelector::first_greater_or_equal(&end), ) } else if selector { - let begin = self.pop_selector(); - let end = self.pop_selector(); + let begin = self.pop_selector().await; + let end = self.pop_selector().await; (begin, end) } else { - let begin: Vec = self.pop_item(); - let end: Vec = self.pop_item(); + let begin: Vec = self.pop_item().await; + let end: Vec = self.pop_item().await; ( KeySelector::first_greater_or_equal(&begin), KeySelector::first_greater_or_equal(&end), ) }; - let limit: i64 = self.pop_item(); - let reverse: i64 = self.pop_item(); - let streaming_mode: i64 = self.pop_item(); + let limit: i64 = self.pop_item().await; + let reverse: i64 = self.pop_item().await; + let streaming_mode: i64 = self.pop_item().await; let mode = streaming_from_value(streaming_mode as i32); debug!( @@ -573,7 +586,7 @@ impl StackMachine { ); let prefix: Option> = if selector { - Some(self.pop_item()) + Some(self.pop_item().await) } else { None }; @@ -585,16 +598,13 @@ impl StackMachine { .snapshot(instr.pop_snapshot()) .build(); - let out = Vec::new(); let trx0 = trx.clone(); + // Future), Error>> + 'static, let f = trx .get_ranges(opt) - .map_err(|(_, e)| e) - .fold(out, move |mut out, res| { + .try_fold(Vec::new(), move |mut out, res| { let kvs = res.key_values(); - debug!("range: len={:?}", kvs.as_ref().len()); - for kv in kvs.as_ref() { let key = kv.key(); let value = kv.value(); @@ -610,23 +620,23 @@ impl StackMachine { .encode_to(&mut out) .expect("failed to encode"); } - Ok::<_, Error>(out) - }) - .map(|out| (trx0, out)); + + ready(Ok(out)) + }); //TODO: wait - self.push_fut(number, f); + self.push_fut(number, async { Ok((trx0, f.await.unwrap())) }); let item = self.pop(); let number = item.number; - self.push(number, item.data()); + self.push(number, item.data().await); } GetReadVersion => { //TODO: wait let version = trx .get_read_version() - .wait() + .await .expect("failed to get read version"); //TODO @@ -638,16 +648,17 @@ impl StackMachine { GetVersionstamp => { let trx0 = trx.clone(); - let f = trx - .clone() - .get_versionstamp() - .map(move |v| (trx0, v.versionstamp().to_vec())); + let f = async { + let trx = trx0.clone(); + let vs = trx.get_versionstamp().await?; + Ok((trx0, vs.versionstamp().to_vec())) + }; self.push_fut(number, f); } Set => { - let key: Vec = self.pop_item(); - let value: Vec = self.pop_item(); + let key: Vec = self.pop_item().await; + let value: Vec = self.pop_item().await; debug!("set : key={:?}, value={:?}", key, value); trx.set(&key, &value); @@ -659,7 +670,7 @@ impl StackMachine { } Clear => { - let key: Vec = self.pop_item(); + let key: Vec = self.pop_item().await; trx.clear(&key); debug!("clear: key={:?}", key); @@ -667,13 +678,13 @@ impl StackMachine { } ClearRange => { - let begin: Vec = self.pop_item(); + let begin: Vec = self.pop_item().await; let end = if instr.pop_starts_with() { let mut end = begin.clone(); strinc(&mut end); end } else { - let end: Vec = self.pop_item(); + let end: Vec = self.pop_item().await; end }; trx.clear_range(&begin, &end); @@ -681,9 +692,9 @@ impl StackMachine { } AtomicOp => { - let optype: String = self.pop_item(); - let key: Vec = self.pop_item(); - let value: Vec = self.pop_item(); + let optype: String = self.pop_item().await; + let key: Vec = self.pop_item().await; + let value: Vec = self.pop_item().await; let op = mutation_from_str(&optype); trx.atomic_op(&key, &value, op); @@ -695,10 +706,11 @@ impl StackMachine { } Commit => { - let f = trx - .clone() - .commit() - .map(|trx| (trx, b"RESULT_NOT_PRESENT".to_vec())); + let trx = trx.clone(); + let f = async { + trx.clone().commit().await?; + Ok((trx, b"RESULT_NOT_PRESENT".to_vec())) + }; self.push_fut(number, f); } @@ -714,22 +726,22 @@ impl StackMachine { //TODO let item = self.pop(); let number = item.number; - self.push(number, item.data()); + self.push(number, item.data().await); } TuplePack => { - let n: i64 = self.pop_item(); + let n: i64 = self.pop_item().await; let mut buf = Vec::new(); for _ in 0..n { - let mut data = self.pop_data(); + let mut data = self.pop_data().await; buf.append(&mut data); } self.push_item(number, &buf); } TupleUnpack => { - let data: Vec = self.pop_item(); + let data: Vec = self.pop_item().await; let mut data = data.as_slice(); while !data.is_empty() { @@ -741,11 +753,11 @@ impl StackMachine { } TupleRange => { - let n: i64 = self.pop_item(); + let n: i64 = self.pop_item().await; let mut tup = Vec::new(); for _ in 0..n { - let mut data = self.pop_data(); + let mut data = self.pop_data().await; tup.append(&mut data); } @@ -773,7 +785,7 @@ impl StackMachine { if is_db && mutation { //TODO - trx.commit().wait().expect("failed to commit"); + trx.commit().await.expect("failed to commit"); self.push_item(number, &b"RESULT_NOT_PRESENT".to_vec()); } @@ -782,15 +794,15 @@ impl StackMachine { } } - fn run(&mut self) { + async fn run(&mut self) { let instrs = self .fetch_instr() - .wait() + .await .expect("failed to read instructions"); for (i, instr) in instrs.into_iter().enumerate() { debug!("{}/{}, {:?}", i, self.stack.len(), instr); - self.run_step(i, instr); + self.run_step(i, instr).await; /* if i == 135 { @@ -830,18 +842,21 @@ fn main() { network.wait(); - let cluster = Cluster::new(cluster_path) - .wait() - .expect("failed to create cluster"); + let rt = Runtime::new().unwrap(); + rt.block_on(async { + let cluster = Cluster::new(cluster_path) + .await + .expect("failed to create cluster"); - let db = cluster - .create_database() - .wait() - .expect("failed to get database"); + let db = cluster + .create_database() + .await + .expect("failed to get database"); - let mut sm = StackMachine::new(db, prefix.to_owned()); + let mut sm = StackMachine::new(db, prefix.to_owned()); - sm.run(); + sm.run().await + }); network.stop().expect("failed to stop network"); handle.join().expect("failed to join fdb thread"); diff --git a/foundationdb/src/cluster.rs b/foundationdb/src/cluster.rs index d43ae0a..349e4be 100644 --- a/foundationdb/src/cluster.rs +++ b/foundationdb/src/cluster.rs @@ -10,14 +10,11 @@ //! //! https://apple.github.io/foundationdb/api-c.html#cluster -use foundationdb_sys as fdb; use crate::future::*; -use futures::{Async, Future}; -use std; -use std::sync::Arc; +use foundationdb_sys as fdb; +use std::{self, sync::Arc}; -use crate::database::*; -use crate::error::*; +use crate::{database::*, error::*}; /// An opaque type that represents a Cluster in the FoundationDB C API. #[derive(Clone)] @@ -32,13 +29,17 @@ impl Cluster { /// * `path` - A string giving a local path of a cluster file (often called ‘fdb.cluster’) which contains connection information for the FoundationDB cluster. See `foundationdb::default_config_path()` /// /// TODO: implement Default for Cluster where: If cluster_file_path is NULL or an empty string, then a default cluster file will be used. see - pub fn new(path: &str) -> ClusterGet { + pub async fn new(path: &str) -> Result { let path_str = std::ffi::CString::new(path).unwrap(); - let inner = unsafe { + let fut = unsafe { let f = fdb::fdb_create_cluster(path_str.as_ptr()); FdbFuture::new(f) }; - ClusterGet { inner } + let result = fut.await?; + let cluster = unsafe { result.get_cluster()? }; + Ok(Cluster { + inner: Arc::new(ClusterInner::new(cluster)), + }) } // TODO: fdb_cluster_set_option impl @@ -46,36 +47,13 @@ impl Cluster { /// Returns an `FdbFuture` which will be set to an `Database` object. /// /// TODO: impl Future - pub fn create_database(&self) -> Box> { - let f = unsafe { + pub async fn create_database(&self) -> Result { + unsafe { let f_db = fdb::fdb_cluster_create_database(self.inner.inner, b"DB" as *const _, 2); let cluster = self.clone(); - FdbFuture::new(f_db) - .and_then(|f| f.get_database()) - .map(|db| Database::new(cluster, db)) - }; - Box::new(f) - } -} - -/// A future result of the `Cluster::new` -pub struct ClusterGet { - inner: FdbFuture, -} -impl Future for ClusterGet { - type Item = Cluster; - type Error = Error; - - fn poll(&mut self) -> Result> { - match self.inner.poll() { - Ok(Async::Ready(r)) => match unsafe { r.get_cluster() } { - Ok(c) => Ok(Async::Ready(Cluster { - inner: Arc::new(ClusterInner::new(c)), - })), - Err(e) => Err(e), - }, - Ok(Async::NotReady) => Ok(Async::NotReady), - Err(e) => Err(e), + let res = FdbFuture::new(f_db).await?; + let db = res.get_database()?; + Ok(Database::new(cluster, db)) } } } diff --git a/foundationdb/src/database.rs b/foundationdb/src/database.rs index a8d38c1..3e184ec 100644 --- a/foundationdb/src/database.rs +++ b/foundationdb/src/database.rs @@ -10,17 +10,17 @@ //! //! https://apple.github.io/foundationdb/api-c.html#database -use std; -use std::sync::Arc; +use std::{self, sync::Arc}; use foundationdb_sys as fdb; -use futures::future::*; -use futures::Future; +use futures::future::{poll_fn, ready, Future, TryFuture}; -use crate::cluster::*; -use crate::error::{self, Error as FdbError, Result}; -use crate::options; -use crate::transaction::*; +use crate::{ + cluster::*, + error::{self, Error as FdbError, Result}, + options, + transaction::*, +}; /// Represents a FoundationDB database — a mutable, lexicographically ordered mapping from binary keys to binary values. /// @@ -65,43 +65,29 @@ impl Database { /// It might retry indefinitely if the transaction is highly contentious. It is recommended to /// set `TransactionOption::RetryLimit` or `TransactionOption::SetTimeout` on the transaction /// if the task need to be guaranteed to finish. - pub fn transact( - &self, - f: F, - ) -> Box> + pub async fn transact(&self, mut f: F) -> Fut::Output where - F: FnMut(Transaction) -> Fut + 'static, - Fut: IntoFuture + 'static, - Item: 'static, - Error: From + 'static, + F: FnMut(Transaction) -> Fut, + Fut: Future>, + // Item: 'static, + Error: From, { let db = self.clone(); - - let f = result(db.create_trx()) - .map_err(Error::from) - .and_then(|trx| { - loop_fn((trx, f), |(trx, mut f)| { - let trx0 = trx.clone(); - f(trx.clone()).into_future().and_then(move |res| { - // try to commit the transaction - trx0.commit().map(|_| res).then(|res| match res { - Ok(v) => { - // committed - Ok(Loop::Break(v)) - } - Err(e) => { - if e.should_retry() { - Ok(Loop::Continue((trx, f))) - } else { - Err(Error::from(e)) - } - } - }) - }) - }) - }); - - Box::new(f) + let trx = db.create_trx()?; + loop { + let trx = trx.clone(); + let res = f(trx.clone()).await?; + match trx.commit().await { + Ok(_) => return Ok(res), + Err(e) => { + if e.should_retry() { + continue; + } else { + return Err(Error::from(e)); + } + } + } + } } } diff --git a/foundationdb/src/error.rs b/foundationdb/src/error.rs index 925f38e..af1d88f 100644 --- a/foundationdb/src/error.rs +++ b/foundationdb/src/error.rs @@ -8,15 +8,16 @@ //! Error types for the Fdb crate -use std; -use std::ffi::CStr; -use std::fmt::{self, Display}; +use std::{ + self, + ffi::CStr, + fmt::{self, Display}, +}; use failure::{Backtrace, Context, Fail}; +use crate::{options, tuple}; use foundationdb_sys as fdb_sys; -use crate::options; -use crate::tuple; pub(crate) fn eval(error_code: fdb_sys::fdb_error_t) -> Result<()> { let rust_code = error_code as i32; diff --git a/foundationdb/src/fdb_api.rs b/foundationdb/src/fdb_api.rs index fc7e082..d3308e9 100644 --- a/foundationdb/src/fdb_api.rs +++ b/foundationdb/src/fdb_api.rs @@ -10,9 +10,11 @@ //! //! https://apple.github.io/foundationdb/api-c.html#api-versioning -use crate::error::{self, Result}; +use crate::{ + error::{self, Result}, + network::NetworkBuilder, +}; use foundationdb_sys as fdb_sys; -use crate::network::NetworkBuilder; /// Returns the max api version of the underlying Fdb C API Client pub fn get_max_api_version() -> i32 { diff --git a/foundationdb/src/future.rs b/foundationdb/src/future.rs index 553a69f..03341c0 100644 --- a/foundationdb/src/future.rs +++ b/foundationdb/src/future.rs @@ -14,27 +14,41 @@ //! //! The Rust API Client has been implemented to use the Rust futures crate, and should work within that ecosystem (suchas Tokio). See Rust [futures](https://docs.rs/crate/futures/0.1.21) documentation. -use std; -use std::ops::Deref; +use std::{self, ops::Deref}; use foundationdb_sys as fdb; -use futures; -use futures::Async; -use crate::error::{self, Error, Result}; +use std::{ + collections::hash_map::DefaultHasher, + future::Future, + hash::Hasher, + mem::{self, ManuallyDrop}, + pin::Pin, + sync::{ + atomic::{AtomicBool, AtomicUsize, Ordering}, + Arc, Once, + }, + task::{self, Poll}, +}; +use tokio_sync::AtomicWaker; + +use crate::error::{self, Result}; /// An opaque type that represents a Future in the FoundationDB C API. pub(crate) struct FdbFuture { f: Option<*mut fdb::FDBFuture>, - task: Option>, + waker: Arc, + registered: bool, } impl FdbFuture { // `new` is marked as unsafe because it's lifetime is not well-defined. pub(crate) unsafe fn new(f: *mut fdb::FDBFuture) -> Self { + let aw = AtomicWaker::new(); Self { f: Some(f), - task: None, + waker: Arc::new(aw), + registered: false, } } } @@ -46,50 +60,55 @@ impl Drop for FdbFuture { // `fdb_future_cancel` explicitly. unsafe { fdb::fdb_future_destroy(f) } } + // self.waker.do_drop(); } } -impl futures::Future for FdbFuture { - type Item = FdbFutureResult; - type Error = Error; +impl Future for FdbFuture { + type Output = error::Result; - fn poll(&mut self) -> std::result::Result, Self::Error> { + fn poll(self: Pin<&mut Self>, cx: &mut task::Context) -> Poll { let f = self.f.expect("cannot poll after resolve"); + let fut = self.get_mut(); - if self.task.is_none() { - let task = futures::task::current(); - let task = Box::new(task); - let task_ptr = task.as_ref() as *const _; - unsafe { - fdb::fdb_future_set_callback(f, Some(fdb_future_callback), task_ptr as *mut _); - } - self.task = Some(task); - - return Ok(Async::NotReady); - } + fut.waker.register_by_ref(cx.waker()); let ready = unsafe { fdb::fdb_future_is_ready(f) }; - if ready == 0 { - return Ok(Async::NotReady); + if ready != 0 { + unsafe { error::eval(fdb::fdb_future_get_error(f))? }; + + // The result is taking ownership of fdb::FDBFuture + let g = FdbFutureResult::new(fut.f.take().unwrap()); + // fut.waker.do_drop(); + return Poll::Ready(Ok(g)); } - unsafe { error::eval(fdb::fdb_future_get_error(f))? }; + if !fut.registered { + // let waker_ptr = &fut.waker as *const _; + let w = fut.waker.clone(); + let wp = Arc::into_raw(w); + + unsafe { + let ret = fdb::fdb_future_set_callback(f, Some(fdb_future_callback), wp as *mut _); + debug_assert!(ret == 0); + } + fut.registered = true; + } - // The result is taking ownership of fdb::FDBFuture - let g = FdbFutureResult::new(self.f.take().unwrap()); - Ok(Async::Ready(g)) + return Poll::Pending; } } // The callback from fdb C API can be called from multiple threads. so this callback should be // thread-safe. +#[no_mangle] extern "C" fn fdb_future_callback( _f: *mut fdb::FDBFuture, callback_parameter: *mut ::std::os::raw::c_void, ) { - let task: *const futures::task::Task = callback_parameter as *const _; - let task: &futures::task::Task = unsafe { &*task }; - task.notify(); + let waker_ptr: *const AtomicWaker = callback_parameter as *const _; + let waker = unsafe { Arc::from_raw(waker_ptr) }; + waker.wake(); } /// Represents the output of fdb_future_get_keyvalue_array(). diff --git a/foundationdb/src/hca.rs b/foundationdb/src/hca.rs index 390d71b..24c3d62 100644 --- a/foundationdb/src/hca.rs +++ b/foundationdb/src/hca.rs @@ -27,16 +27,19 @@ use std::sync::Mutex; use byteorder::ByteOrder; -use futures::future::Future; -use futures::stream::Stream; +use futures::future::ready; + +use futures::TryStreamExt; use rand::Rng; -use crate::error::Error; -use crate::keyselector::KeySelector; -use crate::options::{ConflictRangeType, MutationType, TransactionOption}; -use crate::subspace::Subspace; -use crate::transaction::{RangeOptionBuilder, Transaction}; -use crate::tuple::Element; +use crate::{ + error::Error, + keyselector::KeySelector, + options::{ConflictRangeType, MutationType, TransactionOption}, + subspace::Subspace, + transaction::{RangeOptionBuilder, Transaction}, + tuple::Element, +}; const ONE_BYTES: &[u8] = &[0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; @@ -62,7 +65,7 @@ impl HighContentionAllocator { /// Returns a byte string that /// 1) has never and will never be returned by another call to this method on the same subspace /// 2) is nearly as short as possible given the above - pub fn allocate(&self, transaction: &mut Transaction) -> Result { + pub async fn allocate(&self, transaction: &mut Transaction) -> Result { let (begin, end) = self.counters.range(); loop { @@ -74,21 +77,18 @@ impl HighContentionAllocator { .snapshot(true) .build(); - let kvs: Vec = transaction + let kvs = transaction .get_ranges(range_option) - .map_err(|(_, e)| e) - .fold(Vec::new(), move |mut out, range_result| { + .try_fold(Vec::new(), |mut out, range_result| { let kvs = range_result.key_values(); - for kv in kvs.as_ref() { - if let Element::I64(counter) = self.counters.unpack(kv.key())? { + if let Ok(Element::I64(counter)) = self.counters.unpack(kv.key()) { out.push(counter); } } - - Ok::<_, Error>(out) + ready(Ok(out)) }) - .wait()?; + .await?; let mut start: i64 = 0; let mut window: i64; @@ -121,7 +121,7 @@ impl HighContentionAllocator { let subspace_start_trx = transaction .get(counters_subspace_with_start.bytes(), true) - .wait()?; + .await?; let count = byteorder::LittleEndian::read_i64(subspace_start_trx.value().unwrap()); drop(mutex_guard); @@ -157,21 +157,18 @@ impl HighContentionAllocator { .snapshot(true) .build(); - let kvs: Vec = transaction + let kvs = transaction .get_ranges(range_option) - .map_err(|(_, e)| e) - .fold(Vec::new(), move |mut out, range_result| { + .try_fold(Vec::new(), |mut out, range_result| { let kvs = range_result.key_values(); - for kv in kvs.as_ref() { - if let Element::I64(counter) = self.counters.unpack(kv.key())? { + if let Ok(Element::I64(counter)) = self.counters.unpack(kv.key()) { out.push(counter); } } - - Ok::<_, Error>(out) + ready(Ok(out)) }) - .wait()?; + .await?; let candidate_value_trx = transaction.get(candidate_subspace, false); @@ -188,7 +185,7 @@ impl HighContentionAllocator { } } - let candidate_value = candidate_value_trx.wait()?; + let candidate_value = candidate_value_trx.await?; match candidate_value.value() { Some(_) => (), diff --git a/foundationdb/src/lib.rs b/foundationdb/src/lib.rs index 1fbe592..75305c3 100644 --- a/foundationdb/src/lib.rs +++ b/foundationdb/src/lib.rs @@ -52,56 +52,60 @@ //! ## Example //! //! ```rust -//! extern crate futures; -//! extern crate foundationdb; -//! -//! # fn main() { +//! #![feature(async_await)] +//! use foundationdb::*; //! +//! use futures::executor::block_on; //! use std::thread; -//! use futures::future::*; -//! use foundationdb::{self, *}; //! -//! let network = foundationdb::init().expect("failed to initialize Fdb client"); +//! fn main() { +//! let network = foundationdb::init().expect("failed to initialize Fdb client"); //! -//! let handle = std::thread::spawn(move || { +//! let handle = thread::spawn(move || { //! let error = network.run(); //! //! if let Err(error) = error { -//! panic!("fdb_run_network: {}", error); +//! panic!("fdb_run_network: {}", error); //! } -//! }); +//! }); //! -//! // wait for the network thread to be started -//! network.wait(); +//! // wait for the network thread to be started +//! network.wait(); //! -//! // work with Fdb -//! let db = Cluster::new(foundationdb::default_config_path()) -//! .and_then(|cluster| cluster.create_database()) -//! .wait().expect("failed to create Cluster"); +//! block_on(async { +//! // work with Fdb +//! let db = Cluster::new(foundationdb::default_config_path()) +//! .await +//! .expect("failed to create Cluster"); +//! let db = db +//! .create_database() +//! .await +//! .expect("failed to create database"); //! -//! // set a value -//! let trx = db.create_trx().expect("failed to create transaction"); +//! // set a value +//! let trx = db.create_trx().expect("failed to create transaction"); //! -//! trx.set(b"hello", b"world"); // errors will be returned in the future result -//! trx.commit() -//! .wait() -//! .expect("failed to set hello to world"); +//! trx.set(b"hello", b"world"); // errors will be returned in the future result +//! trx.commit().await.expect("failed to set hello to world"); //! -//! // read a value -//! let trx = db.create_trx().expect("failed to create transaction"); -//! let result = trx.get(b"hello", false).wait().expect("failed to read world from hello"); +//! // read a value +//! let trx = db.create_trx().expect("failed to create transaction"); +//! let result = trx +//! .get(b"hello", false) +//! .await +//! .expect("failed to read world from hello"); //! -//! let value: &[u8] = result.value() -//! .unwrap(); // unwrap the option +//! let value: &[u8] = result.value().unwrap(); // unwrap the option //! -//! // should print "hello world" -//! println!("hello {}", String::from_utf8_lossy(value)); +//! // should print "hello world" +//! println!("hello {}", String::from_utf8_lossy(value)); +//! }); //! -//! // cleanly shutdown the client -//! network.stop().expect("failed to stop Fdb client"); -//! handle.join(); +//! // cleanly shutdown the client +//! network.stop().expect("failed to stop Fdb client"); +//! handle.join().expect("failed to stop network thread"); +//! } //! -//! # } //! ``` //! //! ## API stability @@ -109,18 +113,16 @@ //! *WARNING* Until the 1.0 release of this library, the API may be in constant flux. #![deny(missing_docs)] +#![feature(async_await)] #[macro_use] extern crate failure; #[macro_use] extern crate failure_derive; - #[macro_use] extern crate futures; - - #[cfg(feature = "uuid")] extern crate uuid; @@ -140,11 +142,9 @@ pub mod transaction; pub mod tuple; //move to prelude? -pub use crate::cluster::Cluster; -pub use crate::database::Database; -pub use crate::error::Error; -pub use crate::subspace::Subspace; -pub use crate::transaction::Transaction; +pub use crate::{ + cluster::Cluster, database::Database, error::Error, subspace::Subspace, transaction::Transaction, +}; /// Initialize the FoundationDB Client API, this can only be called once per process. /// @@ -177,7 +177,7 @@ pub use crate::transaction::Transaction; /// handle.join().expect("failed to join fdb thread"); /// ``` pub fn init() -> error::Result { - fdb_api::FdbApiBuilder::default().build()?.network().build() + fdb_api::FdbApiBuilder::default().build()?.network().build() } /// Initialize the FoundationDB Client API, this can only be called once per process. @@ -202,7 +202,7 @@ pub fn init() -> error::Result { /// // see example on `init` /// ``` pub fn default_api() -> error::Result { - Ok(fdb_api::FdbApiBuilder::default().build()?.network()) + Ok(fdb_api::FdbApiBuilder::default().build()?.network()) } /// Allows the API version, etc, to be configured before starting. @@ -211,23 +211,23 @@ pub fn default_api() -> error::Result { /// /// A `FdbApiBuilder` which can be used to configure the FoundationDB Client API version, etc. pub fn builder() -> fdb_api::FdbApiBuilder { - fdb_api::FdbApiBuilder::default() + fdb_api::FdbApiBuilder::default() } /// Returns the default Fdb cluster configuration file path #[cfg(target_os = "linux")] pub fn default_config_path() -> &'static str { - "/etc/foundationdb/fdb.cluster" + "/etc/foundationdb/fdb.cluster" } /// Returns the default Fdb cluster configuration file path #[cfg(target_os = "macos")] pub fn default_config_path() -> &'static str { - "/usr/local/etc/foundationdb/fdb.cluster" + "/usr/local/etc/foundationdb/fdb.cluster" } /// Returns the default Fdb cluster configuration file path #[cfg(target_os = "windows")] pub fn default_config_path() -> &'static str { - "C:/ProgramData/foundationdb/fdb.cluster" + "C:/ProgramData/foundationdb/fdb.cluster" } diff --git a/foundationdb/src/network.rs b/foundationdb/src/network.rs index e5f0403..a69ed80 100644 --- a/foundationdb/src/network.rs +++ b/foundationdb/src/network.rs @@ -10,16 +10,20 @@ //! //! see https://apple.github.io/foundationdb/api-c.html#network -use std; -use std::sync::atomic::{AtomicBool, Ordering}; -use std::thread; +use std::{ + self, + sync::atomic::{AtomicBool, Ordering}, + thread, +}; use failure; -use crate::error::{self, Result}; -use crate::fdb_api::FdbApi; +use crate::{ + error::{self, Result}, + fdb_api::FdbApi, + options::NetworkOption, +}; use foundationdb_sys as fdb_sys; -use crate::options::NetworkOption; // The Fdb states that setting the Client version should happen only once // and is not thread-safe, thus the choice of a lazy static enforcing a single @@ -132,8 +136,7 @@ impl From for NetworkBuilder { #[cfg(test)] mod tests { - use std::sync::Arc; - use std::thread; + use std::{sync::Arc, thread}; use crate::fdb_api::*; diff --git a/foundationdb/src/transaction.rs b/foundationdb/src/transaction.rs index ba53172..456f97b 100644 --- a/foundationdb/src/transaction.rs +++ b/foundationdb/src/transaction.rs @@ -11,17 +11,25 @@ //! https://apple.github.io/foundationdb/api-c.html#transaction use foundationdb_sys as fdb; -use futures::{Async, Future, Stream}; -use std; -use std::sync::Arc; - -use crate::database::*; -use crate::error::{self, *}; -use crate::future::*; -use crate::keyselector::*; -use crate::options; -use crate::subspace::Subspace; -use crate::tuple::Encode; +use futures::{ready, Stream}; +use std::{ + self, fmt, + future::Future, + pin::Pin, + result::Result as StdResult, + sync::Arc, + task::{self, Poll}, +}; + +use crate::{ + database::*, + error::{self, *}, + future::*, + keyselector::*, + options, + subspace::Subspace, + tuple::Encode, +}; /// In FoundationDB, a transaction is a mutable snapshot of a database. /// @@ -40,6 +48,13 @@ pub struct Transaction { database: Database, } +impl std::fmt::Debug for Transaction { + fn fmt(&self, f: &mut fmt::Formatter) -> StdResult<(), fmt::Error> { + f.write_str("Transaction")?; + Ok(()) + } +} + /// Converts Rust `bool` into `fdb::fdb_bool_t` fn fdb_bool(v: bool) -> fdb::fdb_bool_t { if v { @@ -383,12 +398,12 @@ impl Transaction { /// As with other client/server databases, in some failure scenarios a client may be unable to determine whether a transaction succeeded. In these cases, `Transaction::commit` will return a commit_unknown_result error. The fdb_transaction_on_error() function treats this error as retryable, so retry loops that don’t check for commit_unknown_result could execute the transaction twice. In these cases, you must consider the idempotence of the transaction. For more information, see Transactions with unknown results. /// /// Normally, commit will wait for outstanding reads to return. However, if those reads were snapshot reads or the transaction option for disabling “read-your-writes” has been invoked, any outstanding reads will immediately return errors. - pub fn commit(self) -> TrxCommit { + pub async fn commit(self) -> Result { let trx = self.inner.inner; let f = unsafe { fdb::fdb_transaction_commit(trx) }; - let f = self.new_fut_trx(f); - TrxCommit { inner: f } + let (trx, _) = self.new_fut_trx(f).await?; + Ok(trx) } /// Cancels the transaction. All pending or future uses of the transaction will return a @@ -439,7 +454,7 @@ impl Transaction { /// Returns an FDBFuture which will be set to an array of strings. You must first wait for the /// FDBFuture to be ready, check for errors, call fdb_future_get_string_array() to extract the /// string array, and then destroy the FDBFuture with fdb_future_destroy(). - pub fn get_addresses_for_key(&self, key: &[u8]) -> TrxGetAddressesForKey { + pub async fn get_addresses_for_key(&self, key: &[u8]) -> Result { let trx = self.inner.inner; let f = unsafe { @@ -449,9 +464,8 @@ impl Transaction { key.len() as i32, ) }; - TrxGetAddressesForKey { - inner: self.new_fut_trx(f), - } + let (trx, inner) = self.new_fut_trx(f).await?; + Ok(GetAddressResult { trx, inner }) } /// A watch’s behavior is relative to the transaction that created it. A watch will report a @@ -480,14 +494,13 @@ impl Transaction { /// too_many_watches error. This limit can be changed using the MAX_WATCHES database option. /// Because a watch outlives the transaction that creates it, any watch that is no longer /// needed should be cancelled by calling fdb_future_cancel() on its returned future. - pub fn watch(&self, key: &[u8]) -> TrxWatch { + pub fn watch(&self, key: &[u8]) -> impl Future> { let trx = self.inner.inner; let f = unsafe { fdb::fdb_transaction_watch(trx, key.as_ptr() as *const _, key.len() as i32) }; - TrxWatch { - inner: self.new_fut_non_trx(f), - } + let f = self.new_fut_non_trx(f); + WatchFuture::new(f) } /// Returns an FDBFuture which will be set to the versionstamp which was used by any @@ -502,26 +515,23 @@ impl Transaction { /// optimized to a read-only transaction. /// /// Most applications will not call this function. - pub fn get_versionstamp(&self) -> TrxVersionstamp { + pub fn get_versionstamp(&self) -> impl Future> { let trx = self.inner.inner; let f = unsafe { fdb::fdb_transaction_get_versionstamp(trx) }; - TrxVersionstamp { - inner: self.new_fut_non_trx(f), - } + VersionstampFuture::new(self.new_fut_non_trx(f)) } /// The transaction obtains a snapshot read version automatically at the time of the first call /// to fdb_transaction_get_*() (including this one) and (unless causal consistency has been /// deliberately compromised by transaction options) is guaranteed to represent all /// transactions which were reported committed before that call. - pub fn get_read_version(&self) -> TrxReadVersion { + pub async fn get_read_version(&self) -> Result { let trx = self.inner.inner; let f = unsafe { fdb::fdb_transaction_get_read_version(trx) }; - TrxReadVersion { - inner: self.new_fut_trx(f), - } + let (_, res) = self.new_fut_trx(f).await?; + res.get_version() } /// Sets the snapshot read version used by a transaction. This is not needed in simple cases. @@ -637,20 +647,29 @@ impl GetResult { } } +impl std::fmt::Debug for GetResult { + fn fmt(&self, f: &mut fmt::Formatter) -> StdResult<(), fmt::Error> { + f.write_str("GetResult")?; + Ok(()) + } +} + /// A future results of a get operation pub struct TrxGet { inner: TrxFuture, } impl Future for TrxGet { - type Item = GetResult; - type Error = Error; - - fn poll(&mut self) -> std::result::Result, Self::Error> { - let (trx, inner) = try_ready!(self.inner.poll()); - // check if a future resolves to value type - inner.get_value()?; - - Ok(Async::Ready(GetResult { trx, inner })) + type Output = error::Result; + + fn poll(self: Pin<&mut Self>, cx: &mut task::Context) -> Poll { + match Pin::new(&mut self.get_mut().inner).poll(cx) { + Poll::Pending => Poll::Pending, + Poll::Ready(Err(e)) => Poll::Ready(Err(e)), + Poll::Ready(Ok((trx, inner))) => { + inner.get_value()?; + Poll::Ready(Ok(GetResult { trx, inner })) + } + } } } @@ -676,13 +695,16 @@ pub struct TrxGetKey { inner: TrxFuture, } impl Future for TrxGetKey { - type Item = GetKeyResult; - type Error = Error; - - fn poll(&mut self) -> std::result::Result, Self::Error> { - let (trx, inner) = try_ready!(self.inner.poll()); - inner.get_key()?; - Ok(Async::Ready(GetKeyResult { trx, inner })) + type Output = Result; + + fn poll(self: Pin<&mut Self>, cx: &mut task::Context) -> Poll { + // TODO(schallert): use the new futures "ready!" macro once this + // compiles. + match Pin::new(&mut self.get_mut().inner).poll(cx) { + Poll::Pending => Poll::Pending, + Poll::Ready(Err(e)) => Poll::Ready(Err(e)), + Poll::Ready(Ok((trx, inner))) => Poll::Ready(Ok(GetKeyResult { trx, inner })), + } } } @@ -755,16 +777,22 @@ pub struct TrxGetRange { } impl Future for TrxGetRange { - type Item = GetRangeResult; - type Error = Error; - - fn poll(&mut self) -> std::result::Result, Self::Error> { - let (trx, inner) = try_ready!(self.inner.poll()); - // tests if the future resolves to keyvalue array. - inner.get_keyvalue_array()?; - - let opt = self.opt.take().expect("should not poll after ready"); - Ok(Async::Ready(GetRangeResult { trx, inner, opt })) + type Output = Result; + + fn poll(self: Pin<&mut Self>, cx: &mut task::Context) -> Poll { + let fut = self.get_mut(); + match Pin::new(&mut fut.inner).poll(cx) { + Poll::Pending => Poll::Pending, + Poll::Ready(res) => match res { + Err(e) => Poll::Ready(Result::Err(e)), + Ok((trx, inner)) => { + // tests if the future resolves to keyvalue array. + inner.get_keyvalue_array()?; + let opt = fut.opt.take().expect("should not poll after ready"); + Poll::Ready(Result::Ok(GetRangeResult { trx, inner, opt })) + } + }, + } } } @@ -790,48 +818,40 @@ impl RangeStream { } } +impl From<(RangeOption, Error)> for error::Error { + fn from(error: (RangeOption, Error)) -> Self { + error.1 + } +} + impl<'a> Stream for RangeStream { - type Item = GetRangeResult; - type Error = (RangeOption, Error); + type Item = std::result::Result; - fn poll(&mut self) -> std::result::Result>, Self::Error> { + // fn poll(&mut self) -> std::result::Result>, Self::Error> { + fn poll_next(self: Pin<&mut Self>, cx: &mut task::Context) -> Poll> { if self.inner.is_none() { - return Ok(Async::Ready(None)); + return Poll::Ready(None); } - let mut inner = self.inner.take().unwrap(); - match inner.poll() { - Ok(Async::NotReady) => { - self.inner = Some(inner); - Ok(Async::NotReady) + let stream = self.get_mut(); + let mut inner = stream.inner.take().unwrap(); + match Pin::new(&mut inner).poll(cx) { + Poll::Pending => { + stream.inner = Some(inner); + Poll::Pending } - Ok(Async::Ready(res)) => { - self.advance(&res); - Ok(Async::Ready(Some(res))) + Poll::Ready(Ok(res)) => { + stream.advance(&res); + Poll::Ready(Some(Ok(res))) } - Err(e) => { + Poll::Ready(Err(e)) => { // `inner.opt == None` after it resolves, so `inner.opt.unwrap()` should not fail. - Err((inner.opt.unwrap(), e)) + Poll::Ready(Some(Err((inner.opt.unwrap(), e)))) } } } } -/// A future result of a `Transaction::commit` -pub struct TrxCommit { - inner: TrxFuture, -} - -impl Future for TrxCommit { - type Item = Transaction; - type Error = Error; - - fn poll(&mut self) -> std::result::Result, Self::Error> { - let (trx, _res) = try_ready!(self.inner.poll()); - Ok(Async::Ready(trx)) - } -} - /// Represents the data of a `Transaction::get_addresses_for_key` pub struct GetAddressResult { trx: Transaction, @@ -851,34 +871,46 @@ impl GetAddressResult { } } -/// A future result of a `Transaction::get_addresses_for_key` -pub struct TrxGetAddressesForKey { - inner: TrxFuture, +struct WatchFuture { + inner: NonTrxFuture, +} + +impl WatchFuture { + fn new(inner: NonTrxFuture) -> Self { + Self { inner } + } } -impl Future for TrxGetAddressesForKey { - type Item = GetAddressResult; - type Error = Error; - fn poll(&mut self) -> std::result::Result, Self::Error> { - let (trx, inner) = try_ready!(self.inner.poll()); - inner.get_string_array()?; - Ok(Async::Ready(GetAddressResult { trx, inner })) +impl Future for WatchFuture { + type Output = Result<()>; + + fn poll(self: Pin<&mut Self>, cx: &mut task::Context) -> Poll { + let fut = self.get_mut(); + ready!(Pin::new(&mut fut.inner).poll(cx))?; + Poll::Ready(Ok(())) } } -/// A future result of a `Transaction::watch` -pub struct TrxWatch { - // `TrxWatch` can live longer then a parent transaction that registhers the watch, so it should - // not maintain a reference to the transaction, which will prevent the transcation to be freed. +struct VersionstampFuture { inner: NonTrxFuture, } -impl Future for TrxWatch { - type Item = (); - type Error = Error; - fn poll(&mut self) -> std::result::Result, Self::Error> { - try_ready!(self.inner.poll()); - Ok(Async::Ready(())) +impl VersionstampFuture { + fn new(inner: NonTrxFuture) -> Self { + Self { inner } + } +} + +impl Future for VersionstampFuture { + type Output = Result; + + fn poll(self: Pin<&mut Self>, cx: &mut task::Context) -> Poll { + let fut = self.get_mut(); + let res = ready!(Pin::new(&mut fut.inner).poll(cx))?; + let key = res.get_key()?; + let mut buf: [u8; 10] = Default::default(); + buf.copy_from_slice(key); + Poll::Ready(Ok(Versionstamp(buf))) } } @@ -894,42 +926,6 @@ impl Versionstamp { } } -/// A future result of a `Transaction::watch` -pub struct TrxVersionstamp { - // `TrxVersionstamp` resolves after `Transaction::commit`, so like `TrxWatch` it does not - // not maintain a reference to the transaction. - inner: NonTrxFuture, -} -impl Future for TrxVersionstamp { - type Item = Versionstamp; - type Error = Error; - - fn poll(&mut self) -> std::result::Result, Self::Error> { - let r = try_ready!(self.inner.poll()); - // returning future should resolve to key - let key = r.get_key()?; - let mut buf: [u8; 10] = Default::default(); - buf.copy_from_slice(key); - Ok(Async::Ready(Versionstamp(buf))) - } -} - -/// A future result of a `Transaction::watch` -pub struct TrxReadVersion { - inner: TrxFuture, -} - -impl Future for TrxReadVersion { - type Item = i64; - type Error = Error; - - fn poll(&mut self) -> std::result::Result, Self::Error> { - let (_trx, r) = try_ready!(self.inner.poll()); - let version = r.get_version()?; - Ok(Async::Ready(version)) - } -} - /// Futures that could be outlive transaction. struct NonTrxFuture { // Order of fields should not be changed, because Rust drops field top-to-bottom, and future @@ -948,11 +944,10 @@ impl NonTrxFuture { } impl Future for NonTrxFuture { - type Item = FdbFutureResult; - type Error = Error; + type Output = Result; - fn poll(&mut self) -> std::result::Result, Self::Error> { - self.inner.poll() + fn poll(self: Pin<&mut Self>, cx: &mut task::Context) -> Poll { + Pin::new(&mut self.get_mut().inner).poll(cx) } } @@ -964,6 +959,7 @@ pub struct TrxErrFuture { inner: NonTrxFuture, err: Option, } + impl TrxErrFuture { fn new(trx: Transaction, err: Error) -> Self { let inner = unsafe { fdb::fdb_transaction_on_error(trx.inner.inner, err.code()) }; @@ -976,13 +972,14 @@ impl TrxErrFuture { } impl Future for TrxErrFuture { - type Item = Error; - type Error = Error; - fn poll(&mut self) -> std::result::Result, Self::Error> { - try_ready!(self.inner.poll()); - let mut e = self.err.take().expect("should not poll after ready"); + type Output = Result; + + fn poll(self: Pin<&mut Self>, cx: &mut task::Context) -> Poll { + let fut = self.get_mut(); + ready!(Pin::new(&mut fut.inner).poll(cx))?; + let mut e = fut.err.take().expect("should not poll after ready"); e.set_should_retry(true); - Ok(Async::Ready(e)) + Poll::Ready(Ok(e)) } } @@ -1007,34 +1004,34 @@ impl TrxFuture { } impl Future for TrxFuture { - type Item = (Transaction, FdbFutureResult); - type Error = Error; - - fn poll(&mut self) -> std::result::Result, Self::Error> { - if self.f_err.is_none() { - match self.inner.poll() { - Ok(Async::Ready(res)) => { - return Ok(Async::Ready(( - self.trx.take().expect("should not poll after ready"), + type Output = Result<(Transaction, FdbFutureResult)>; + + fn poll(self: Pin<&mut Self>, cx: &mut task::Context) -> Poll { + let mut fut = self.get_mut(); + if fut.f_err.is_none() { + match Pin::new(&mut fut.inner).poll(cx) { + Poll::Pending => return Poll::Pending, + Poll::Ready(Ok(res)) => { + return Poll::Ready(Ok(( + fut.trx.take().expect("should not poll after ready"), res, - ))) + ))); } - Ok(Async::NotReady) => return Ok(Async::NotReady), - Err(e) => { + Poll::Ready(Err(e)) => { // The transaction will be dropped on `TrxErrFuture::new`. The `trx` is a last // reference for the transaction, undering transaction will be destroyed at // this point. - let trx = self.trx.take().expect("should not poll after error"); - self.f_err = Some(TrxErrFuture::new(trx, e)); - return self.poll(); + let trx = fut.trx.take().expect("should not poll after error"); + fut.f_err = Some(TrxErrFuture::new(trx, e)); + return Pin::new(fut).poll(cx); } } } - match self.f_err.as_mut().unwrap().poll() { - Ok(Async::Ready(e)) => Err(e), - Ok(Async::NotReady) => Ok(Async::NotReady), - Err(e) => Err(e), + match Pin::new(&mut fut.f_err.as_mut().unwrap()).poll(cx) { + Poll::Pending => Poll::Pending, + Poll::Ready(Ok(res)) => Poll::Ready(Err(res)), + Poll::Ready(Err(e)) => Poll::Ready(Err(e)), } } } diff --git a/foundationdb/src/tuple/element.rs b/foundationdb/src/tuple/element.rs index 15529df..19e6d59 100644 --- a/foundationdb/src/tuple/element.rs +++ b/foundationdb/src/tuple/element.rs @@ -2,8 +2,8 @@ use std::{self, io::Write}; #[cfg(feature = "uuid")] use uuid::Uuid; -use byteorder::{self, ByteOrder}; use crate::tuple::{Decode, Encode, Error, Result, Tuple, TupleDepth}; +use byteorder::{self, ByteOrder}; /// Various tuple types pub(super) const NIL: u8 = 0x00; diff --git a/foundationdb/src/tuple/mod.rs b/foundationdb/src/tuple/mod.rs index 2a78775..9b6c09f 100644 --- a/foundationdb/src/tuple/mod.rs +++ b/foundationdb/src/tuple/mod.rs @@ -10,8 +10,12 @@ mod element; -use std::ops::{Deref, DerefMut}; -use std::{self, io::Write, string::FromUtf8Error}; +use std::{ + self, + io::Write, + ops::{Deref, DerefMut}, + string::FromUtf8Error, +}; #[cfg(feature = "uuid")] use uuid; @@ -351,7 +355,8 @@ mod tests { fn test_decode_recursive_tuple() { let two_decode = <(String, (String, i64))>::try_from(&[ 2, 111, 110, 101, 0, 5, 2, 116, 119, 111, 0, 21, 42, 0, - ]).expect("failed two"); + ]) + .expect("failed two"); // TODO: can we get eq for borrows of the inner types? assert_eq!(("one".to_string(), ("two".to_string(), 42)), two_decode); @@ -359,7 +364,8 @@ mod tests { let three_decode = <(String, (String, i64, (String, i64)))>::try_from(&[ 2, 111, 110, 101, 0, 5, 2, 116, 119, 111, 0, 21, 42, 5, 2, 116, 104, 114, 101, 101, 0, 21, 33, 0, 0, - ]).expect("failed three"); + ]) + .expect("failed three"); assert_eq!( &( diff --git a/foundationdb/tests/atomic.rs b/foundationdb/tests/atomic.rs index c7dbc0d..17300d7 100644 --- a/foundationdb/tests/atomic.rs +++ b/foundationdb/tests/atomic.rs @@ -5,6 +5,8 @@ // http://opensource.org/licenses/MIT>, at your option. This file may not be // copied, modified, or distributed except according to those terms. +#![feature(async_await)] + use byteorder; use foundationdb; @@ -14,19 +16,13 @@ extern crate lazy_static; use byteorder::ByteOrder; use foundationdb::*; use futures::future::*; +use tokio::prelude::*; mod common; //TODO: impl Future -fn atomic_add( - db: Database, - key: &[u8], - value: i64, -) -> Box> { - let trx = match db.create_trx() { - Ok(trx) => trx, - Err(e) => return Box::new(err(e)), - }; +async fn atomic_add(db: Database, key: &[u8], value: i64) -> error::Result<()> { + let trx = db.create_trx()?; let val = { let mut buf = [0u8; 8]; @@ -34,61 +30,50 @@ fn atomic_add( buf }; trx.atomic_op(key, &val, options::MutationType::Add); + trx.commit().await?; - let fut = trx.commit().map(|_trx| ()); - Box::new(fut) + Ok(()) } -#[test] -fn test_atomic() { +#[tokio::test] +async fn test_atomic() -> error::Result<()> { common::setup_static(); const KEY: &[u8] = b"test-atomic"; - let fut = Cluster::new(foundationdb::default_config_path()) - .and_then(|cluster| cluster.create_database()) - .and_then(|db| { - // clear key before run example - result(db.create_trx()) - .and_then(|trx| { - trx.clear(KEY); - trx.commit() - }) - .map(|trx| trx.database()) - }) - .and_then(|db| { - let n = 1000usize; - - // Run `n` add(1) operations in parallel - let db0 = db.clone(); - let fut_add_list = (0..n) - .into_iter() - .map(move |_| atomic_add(db0.clone(), KEY, 1)) - .collect::>(); - let fut_add = join_all(fut_add_list); - - // Run `n` add(-1) operations in parallel - let db0 = db.clone(); - let fut_sub_list = (0..n) - .into_iter() - .map(move |_| atomic_add(db0.clone(), KEY, -1)) - .collect::>(); - let fut_sub = join_all(fut_sub_list); - - // Wait for all atomic operations - fut_add.join(fut_sub).map(move |_| db) - }) - .and_then(|db| result(db.create_trx()).and_then(|trx| trx.get(KEY, false))) - .and_then(|res| { - let value = res.value().expect("value should exists"); - - // A value should be zero, as same number of atomic add/sub operations are done. - let v: i64 = byteorder::LE::read_i64(&value); - if v != 0 { - panic!("expected 0, found {}", v); - } - - Ok(()) - }); - - fut.wait().expect("failed to run"); + let db = common::create_db().await?; + let trx = db.create_trx()?; + // clear key before run example + trx.clear(KEY); + let trx = trx.commit().await?; + let db = trx.database(); + let n = 1000usize; + + // Run `n` add(1) operations in parallel + let db0 = db.clone(); + let fut_add_list = (0..n) + .into_iter() + .map(move |_| atomic_add(db0.clone(), KEY, 1)) + .collect::>(); + let fut_add = join_all(fut_add_list); + + // Run `n` add(-1) operations in parallel + let db0 = db.clone(); + let fut_sub_list = (0..n) + .into_iter() + .map(move |_| atomic_add(db0.clone(), KEY, -1)) + .collect::>(); + let fut_sub = join_all(fut_sub_list); + + // Wait for all atomic operations + join(fut_add, fut_sub).await; + + let trx = db.create_trx()?; + let res = trx.get(KEY, false).await?; + let val = res.value().expect("value should exist"); + + // A value should be zero, as same number of atomic add/sub operations are done. + let v: i64 = byteorder::LE::read_i64(&val); + assert_eq!(v, 0); + + Ok(()) } diff --git a/foundationdb/tests/common/mod.rs b/foundationdb/tests/common/mod.rs index a378b6d..35986e6 100644 --- a/foundationdb/tests/common/mod.rs +++ b/foundationdb/tests/common/mod.rs @@ -8,8 +8,7 @@ use foundationdb::*; /// string as a value. #[allow(unused)] pub fn random_str(len: usize) -> String { - use self::rand::distributions::Alphanumeric; - use self::rand::Rng; + use self::rand::{distributions::Alphanumeric, Rng}; let mut rng = rand::thread_rng(); ::std::iter::repeat(()) @@ -23,6 +22,14 @@ pub fn setup_static() { let _env = &*ENV; } +#[allow(unused)] +pub async fn create_db() -> error::Result { + let db = Cluster::new(foundationdb::default_config_path()) + .await + .expect("expected new cluster"); + db.create_database().await +} + lazy_static! { static ref ENV: TestEnv = { TestEnv::new() }; } diff --git a/foundationdb/tests/get.rs b/foundationdb/tests/get.rs index 3fea49a..78f5e70 100644 --- a/foundationdb/tests/get.rs +++ b/foundationdb/tests/get.rs @@ -5,155 +5,132 @@ // http://opensource.org/licenses/MIT>, at your option. This file may not be // copied, modified, or distributed except according to those terms. -use foundationdb; +#![feature(async_await, async_closure)] + +use foundationdb as fdb; #[macro_use] extern crate lazy_static; -use foundationdb::*; +use fdb::*; use futures::future::*; mod common; -#[test] -fn test_set_get() { +use tokio::prelude::*; + +#[tokio::test] +async fn test_set_get() -> error::Result<()> { common::setup_static(); - let fut = Cluster::new(foundationdb::default_config_path()) - .and_then(|cluster| cluster.create_database()) - .and_then(|db| result(db.create_trx())) - .and_then(|trx| { - trx.set(b"hello", b"world"); - trx.commit() - }) - .and_then(|trx| result(trx.database().create_trx())) - .and_then(|trx| trx.get(b"hello", false)) - .and_then(|res| { - let val = res.value(); - eprintln!("value: {:?}", val); + let db = common::create_db().await?; + let trx = db.create_trx()?; - let trx = res.transaction(); - trx.clear(b"hello"); - trx.commit() - }) - .and_then(|trx| result(trx.database().create_trx())) - .and_then(|trx| trx.get(b"hello", false)) - .and_then(|res| { - eprintln!("value: {:?}", res.value()); - Ok(()) - }); - - fut.wait().expect("failed to run") + trx.set(b"hello", b"world"); + let trx = trx.commit().await?; + + let trx = trx.database().create_trx()?; + + let res = trx.get(b"hello", false).await?; + let val = res.value(); + eprintln!("value: {:?}", val); + + let trx = res.transaction(); + trx.clear(b"hello"); + let trx = trx.commit().await?; + + let trx = trx.database().create_trx()?; + let res = trx.get(b"hello", false).await?; + eprintln!("value: {:?}", res.value()); + Ok(()) } -#[test] -fn test_get_multi() { +#[tokio::test] +async fn test_get_multi() -> fdb::error::Result<()> { common::setup_static(); - let fut = Cluster::new(foundationdb::default_config_path()) - .and_then(|cluster| cluster.create_database()) - .and_then(|db| result(db.create_trx())) - .and_then(|trx| { - let keys: &[&[u8]] = &[b"hello", b"world", b"foo", b"bar"]; - - let futs = keys.iter().map(|k| trx.get(k, false)).collect::>(); - join_all(futs) - }) - .and_then(|results| { - for (i, res) in results.into_iter().enumerate() { - eprintln!("res[{}]: {:?}", i, res.value()); - } - Ok(()) - }); - - fut.wait().expect("failed to run") + let db = common::create_db().await?; + + let trx = db.create_trx()?; + let keys: &[&[u8]] = &[b"hello", b"world", b"foo", b"bar"]; + + let futs = keys.iter().map(|k| trx.get(k, false)).collect::>(); + let results = join_all(futs).await; + + for (i, res) in results.into_iter().enumerate() { + let res = res?; + eprintln!("res[{}]: {:?}", i, res.value()); + } + + Ok(()) } -#[test] -fn test_set_conflict() { +#[tokio::test] +async fn test_set_conflict() -> fdb::error::Result<()> { common::setup_static(); let key = b"test-conflict"; - let fut = Cluster::new(foundationdb::default_config_path()) - .and_then(|cluster| cluster.create_database()) - .and_then(|db| { - // First transaction. It will be committed before second one. - let fut_set1 = result(db.create_trx()).and_then(|trx1| { - trx1.set(key, common::random_str(10).as_bytes()); - trx1.commit() - }); - - // Second transaction. There will be conflicted by first transaction before commit. - result(db.create_trx()) - .and_then(|trx2| { - // try to read value to set conflict range - trx2.get(key, false) - }) - .and_then(move |val| { - // commit first transaction to create conflict - fut_set1.map(move |_trx1| val.transaction()) - }) - .and_then(|trx2| { - // commit seconds transaction, which will cause conflict - trx2.set(key, common::random_str(10).as_bytes()); - trx2.commit() - }) - .map(|_v| { - panic!("should not be committed without conflict"); - }) - .or_else(|e| { - eprintln!("error as expected: {:?}", e); - Ok(()) - }) - }); - - fut.wait().expect("failed to run") + let db = common::create_db().await?; + + // First transaction. It will be committed before second one. + let trx1 = db.create_trx()?; + trx1.set(key, common::random_str(10).as_bytes()); + let trx1_fut = trx1.commit(); + + // Second transaction. There will be conflicted by first transaction before commit. + let trx2 = db.create_trx()?; + // try to read value to set conflict range + trx2.get(key, false).await?; + // commit first transaction to create conflict + trx1_fut.await?; + + // commit seconds transaction, which will cause conflict + trx2.set(key, common::random_str(10).as_bytes()); + let res = trx2.commit().await; + + let e = res.expect_err("expected transaction conflict"); + eprintln!("error as expected: {:?}", e); + // 1020 is fdb error code for transaction conflict. + assert_eq!(e.code(), 1020); + + Ok(()) } -#[test] -fn test_set_conflict_snapshot() { +#[tokio::test] +async fn test_set_conflict_snapshot() -> fdb::error::Result<()> { common::setup_static(); let key = b"test-conflict-snapshot"; - let fut = Cluster::new(foundationdb::default_config_path()) - .and_then(|cluster| cluster.create_database()) - .and_then(|db| { - // First transaction. It will be committed before second one. - let fut_set1 = result(db.create_trx()).and_then(|trx1| { - trx1.set(key, common::random_str(10).as_bytes()); - trx1.commit() - }); - - // Second transaction. - result(db.create_trx()) - .and_then(|trx2| { - // snapshot read does not set conflict range, so both transaction will be - // committed. - trx2.get(key, true) - }) - .and_then(move |val| { - // commit first transaction - fut_set1.map(move |_trx1| val.transaction()) - }) - .and_then(|trx2| { - // commit seconds transaction, which will *not* cause conflict because of - // snapshot read - trx2.set(key, common::random_str(10).as_bytes()); - trx2.commit() - }) - .map(|_v| ()) - }); - - fut.wait().expect("failed to run") + let db = common::create_db().await?; + + // First transaction. It will be committed before second one. + let trx = db.create_trx()?; + trx.set(key, common::random_str(10).as_bytes()); + trx.commit().await?; + + // Second transaction. + let val = db.create_trx()?.get(key, true).await?; + // snapshot read does not set conflict range, so both transaction will be + // committed. + + // commit first transaction + let trx = val.transaction(); + + // commit seconds transaction, which will *not* cause conflict because of + // snapshot read + trx.set(key, common::random_str(10).as_bytes()); + trx.commit().await?; + + Ok(()) } // Makes the key dirty. It will abort transactions which performs non-snapshot read on the `key`. -fn make_dirty(db: &Database, key: &[u8]) { +async fn make_dirty(db: &Database, key: &[u8]) { let trx = db.create_trx().unwrap(); trx.set(key, b""); - trx.commit().wait().unwrap(); + trx.commit().await.unwrap(); } -#[test] -fn test_transact() { +#[tokio::test] +async fn test_transact() -> error::Result<()> { use std::sync::{atomic::*, Arc}; const KEY: &[u8] = b"test-transact"; @@ -161,100 +138,88 @@ fn test_transact() { common::setup_static(); let try_count = Arc::new(AtomicUsize::new(0)); - let try_count0 = try_count.clone(); - - let fut = Cluster::new(foundationdb::default_config_path()) - .and_then(|cluster| cluster.create_database()) - .and_then(|db| { - // start tranasction with retry - db.transact(move |trx| { - // increment try counter - try_count0.fetch_add(1, Ordering::SeqCst); - - trx.set_option(options::TransactionOption::RetryLimit(RETRY_COUNT as u32)) - .expect("failed to set retry limit"); - - let db = trx.database(); - - // update conflict range - trx.get(KEY, false).and_then(move |res| { - // make current transaction invalid by making conflict - make_dirty(&db, KEY); - - let trx = res.transaction(); - trx.set(KEY, common::random_str(10).as_bytes()); - // `Database::transact` will handle commit by itself, so returns without commit - Ok(()) - }) - }).then(|res| match res { - Ok(_) => panic!("should not be able to commit"), - Err(e) => { - eprintln!("failed as expected: {:?}", e); - Ok(()) - } - }) - }); - - fut.wait().expect("failed to run"); + let try_count0 = &try_count.clone(); + + let db = common::create_db().await?; + + // start tranasction with retry + let res = db + .transact(async move |trx| { + let try_count0 = Arc::clone(try_count0); + // increment try counter + try_count0.fetch_add(1, Ordering::SeqCst); + + trx.set_option(options::TransactionOption::RetryLimit(RETRY_COUNT as u32)) + .expect("failed to set retry limit"); + + let db = trx.database(); + + // update conflict range + let res = trx.get(KEY, false).await?; + // make current transaction invalid by making conflict + make_dirty(&db, KEY).await; + + let trx = res.transaction(); + trx.set(KEY, common::random_str(10).as_bytes()); + // `Database::transact` will handle commit by itself, so returns without commit + error::Result::Ok(()) + }) + .await; + + let e = res.expect_err("should not be able to commit"); + eprintln!("failed as expected: {:?}", e); + // `TransactionOption::RetryCount` does not count first try, so `try_count` should be equal to // `RETRY_COUNT+1` assert_eq!(try_count.load(Ordering::SeqCst), RETRY_COUNT + 1); + Ok(()) } -#[test] -fn test_versionstamp() { +#[tokio::test] +async fn test_versionstamp() -> error::Result<()> { const KEY: &[u8] = b"test-versionstamp"; common::setup_static(); - let fut = Cluster::new(foundationdb::default_config_path()) - .and_then(|cluster| cluster.create_database()) - .and_then(|db| result(db.create_trx())) - .and_then(|trx| { - trx.set(KEY, common::random_str(10).as_bytes()); - let f_version = trx.get_versionstamp(); - trx.commit().and_then(move |_trx| f_version) - }) - .map(|r| { - eprintln!("versionstamp: {:?}", r.versionstamp()); - }); + let db = common::create_db().await?; + + let trx = db.create_trx()?; + + trx.set(KEY, common::random_str(10).as_bytes()); + // NB(schallert): MUST construct versionstamp future before commit() is polled, but VS future is + // only ready once commit is complete. + let vs = trx.get_versionstamp(); + trx.commit().await?; + let vs = vs.await?; - fut.wait().expect("failed to run"); + eprintln!("versionstamp: {:?}", vs.versionstamp()); + + Ok(()) } -#[test] -fn test_read_version() { +#[tokio::test] +async fn test_read_version() -> error::Result<()> { common::setup_static(); - let fut = Cluster::new(foundationdb::default_config_path()) - .and_then(|cluster| cluster.create_database()) - .and_then(|db| result(db.create_trx())) - .and_then(|trx| trx.get_read_version()) - .map(|v| { - eprintln!("read version: {:?}", v); - }); + let db = common::create_db().await?; + let trx = db.create_trx()?; + let v = trx.get_read_version().await?; + eprintln!("read version: {:?}", v); - fut.wait().expect("failed to run"); + Ok(()) } -#[test] -fn test_set_read_version() { +#[tokio::test] +async fn test_set_read_version() -> error::Result<()> { const KEY: &[u8] = b"test-versionstamp"; common::setup_static(); - let fut = Cluster::new(foundationdb::default_config_path()) - .and_then(|cluster| cluster.create_database()) - .and_then(|db| result(db.create_trx())) - .and_then(|trx| { - trx.set_read_version(0); - trx.get(KEY, false) - }) - .map(|_v| { - panic!("should fail with past_version"); - }) - .or_else(|e| { - eprintln!("failed as expeced: {:?}", e); - Ok::<(), ()>(()) - }); + let db = common::create_db().await?; + let trx = db.create_trx()?; + trx.set_read_version(0); + let res = trx.get(KEY, false).await; + + let e = res.expect_err("should fail with past_version"); + eprintln!("failed as expeced: {:?}", e); - fut.wait().expect("failed to run"); + Ok(()) } diff --git a/foundationdb/tests/hca.rs b/foundationdb/tests/hca.rs index b6c5604..0cd1d3b 100644 --- a/foundationdb/tests/hca.rs +++ b/foundationdb/tests/hca.rs @@ -5,103 +5,91 @@ // http://opensource.org/licenses/MIT>, at your option. This file may not be // copied, modified, or distributed except according to those terms. +#![feature(async_await, async_closure)] + use foundationdb; use futures; #[macro_use] extern crate lazy_static; -use std::collections::HashSet; -use std::iter::FromIterator; - -use futures::future::*; +use foundationdb::{hca::HighContentionAllocator, *}; -use foundationdb::error::Error; -use foundationdb::hca::HighContentionAllocator; -use foundationdb::*; +use std::{collections::HashSet, iter::FromIterator}; mod common; -#[test] -fn test_hca_many_sequential_allocations() { +#[tokio::test] +async fn test_hca_many_sequential_allocations() -> error::Result<()> { common::setup_static(); const N: usize = 6000; const KEY: &[u8] = b"test-hca-allocate"; - let fut = Cluster::new(foundationdb::default_config_path()) - .and_then(|cluster| cluster.create_database()) - .and_then(|db: Database| { - let cleared_range = db - .transact(move |tx| { - tx.clear_subspace_range(Subspace::from_bytes(KEY)); - futures::future::result(Ok::<(), failure::Error>(())) - }) - .wait(); + let db = common::create_db().await?; - cleared_range.expect("unable to clear hca test range"); - - let hca = HighContentionAllocator::new(Subspace::from_bytes(KEY)); + db.transact(async move |tx| { + tx.clear_subspace_range(Subspace::from_bytes(KEY)); + error::Result::Ok(()) + }) + .await + .expect("unable to clear hca test range"); - let mut all_ints = Vec::new(); + let hca = HighContentionAllocator::new(Subspace::from_bytes(KEY)); - for _ in 0..N { - let mut tx: Transaction = db.create_trx()?; + let mut all_ints = Vec::new(); - let next_int: i64 = hca.allocate(&mut tx)?; - all_ints.push(next_int); + for _ in 0..N { + let mut tx: Transaction = db.create_trx()?; - tx.commit().wait()?; - } + let next_int: i64 = hca.allocate(&mut tx).await?; + all_ints.push(next_int); - Ok::<_, Error>(all_ints) - }); + tx.commit().await?; + } - let all_ints: Vec = fut.wait().expect("failed to run"); check_hca_result_uniqueness(&all_ints); eprintln!("ran test {:?}", all_ints); + + Ok(()) } -#[test] -fn test_hca_concurrent_allocations() { +#[tokio::test] +async fn test_hca_concurrent_allocations() -> error::Result<()> { common::setup_static(); const N: usize = 1000; const KEY: &[u8] = b"test-hca-allocate-concurrent"; + let db = common::create_db().await?; - let fut = Cluster::new(foundationdb::default_config_path()) - .and_then(|cluster| cluster.create_database()) - .and_then(|db: Database| { - let cleared_range = db - .transact(move |tx| { - tx.clear_subspace_range(Subspace::from_bytes(KEY)); - futures::future::result(Ok::<(), failure::Error>(())) - }) - .wait(); - - cleared_range.expect("unable to clear hca test range"); + db.transact(async move |tx| { + tx.clear_subspace_range(Subspace::from_bytes(KEY)); + error::Result::Ok(()) + }) + .await + .expect("unable to clear hca test range"); - let mut futures = Vec::new(); - let mut all_ints: Vec = Vec::new(); + let mut futures = Vec::new(); + let mut all_ints: Vec = Vec::new(); - for _ in 0..N { - let f = db.transact(move |mut tx| { - HighContentionAllocator::new(Subspace::from_bytes(KEY)).allocate(&mut tx) - }); - - futures.push(f); - } + for _ in 0..N { + let f = db.transact(async move |mut tx| { + let hca = HighContentionAllocator::new(Subspace::from_bytes(KEY)); + let i = hca.allocate(&mut tx).await?; + error::Result::Ok(i) + }); - for allocation in futures { - let i = allocation.wait().expect("unable to get allocation"); - all_ints.push(i); - } + futures.push(f); + } - Ok::<_, Error>(all_ints) - }); + for allocation in futures { + let i = allocation.await.expect("unable to get allocation"); + all_ints.push(i); + } - let all_ints: Vec = fut.wait().expect("failed to run"); check_hca_result_uniqueness(&all_ints); eprintln!("ran test {:?}", all_ints); + + Ok(()) } fn check_hca_result_uniqueness(results: &Vec) { diff --git a/foundationdb/tests/network.rs b/foundationdb/tests/network.rs index f3c9033..718c915 100644 --- a/foundationdb/tests/network.rs +++ b/foundationdb/tests/network.rs @@ -5,6 +5,7 @@ // http://opensource.org/licenses/MIT>, at your option. This file may not be // copied, modified, or distributed except according to those terms. +#![feature(async_await)] #[macro_use] extern crate lazy_static; diff --git a/foundationdb/tests/range.rs b/foundationdb/tests/range.rs index c309b80..e4e65e4 100644 --- a/foundationdb/tests/range.rs +++ b/foundationdb/tests/range.rs @@ -5,57 +5,55 @@ // http://opensource.org/licenses/MIT>, at your option. This file may not be // copied, modified, or distributed except according to those terms. +#![feature(async_await)] + use foundationdb; #[macro_use] extern crate lazy_static; -use foundationdb::error::Error; use foundationdb::*; -use futures::future::*; -use futures::prelude::*; +use futures::{future::*, prelude::*}; mod common; -#[test] -fn test_get_range() { +#[tokio::test] +async fn test_get_range() -> error::Result<()> { use foundationdb::keyselector::KeySelector; common::setup_static(); const N: usize = 10000; - let fut = Cluster::new(foundationdb::default_config_path()) - .and_then(|cluster| cluster.create_database()) - .and_then(|db| result(db.create_trx())) - .and_then(|trx| { - let key_begin = "test-range-"; - let key_end = "test-range."; - - trx.clear_range(key_begin.as_bytes(), key_end.as_bytes()); - - for _ in 0..N { - let key = format!("{}-{}", key_begin, common::random_str(10)); - let value = common::random_str(10); - trx.set(key.as_bytes(), value.as_bytes()); - } - - let begin = KeySelector::first_greater_or_equal(key_begin.as_bytes()); - let end = KeySelector::first_greater_than(key_end.as_bytes()); - let opt = transaction::RangeOptionBuilder::new(begin, end).build(); - - trx.get_ranges(opt) - .map_err(|(_opt, e)| e) - .fold(0, |count, item| { - let kvs = item.key_values(); - Ok::<_, Error>(count + kvs.as_ref().len()) - }) - .map(|count| { - if count != N { - panic!("count expected={}, found={}", N, count); - } - eprintln!("count: {:?}", count); - }) - }); - - fut.wait().expect("failed to run") + let fut = Cluster::new(foundationdb::default_config_path()); + let db = common::create_db().await?; + let trx = db.create_trx()?; + + let key_begin = "test-range-"; + let key_end = "test-range."; + + trx.clear_range(key_begin.as_bytes(), key_end.as_bytes()); + + for _ in 0..N { + let key = format!("{}-{}", key_begin, common::random_str(10)); + let value = common::random_str(10); + trx.set(key.as_bytes(), value.as_bytes()); + } + + let begin = KeySelector::first_greater_or_equal(key_begin.as_bytes()); + let end = KeySelector::first_greater_than(key_end.as_bytes()); + let opt = transaction::RangeOptionBuilder::new(begin, end).build(); + + let count = trx + .get_ranges(opt) + .try_fold(0, |mut count, item| { + let kvs = item.key_values(); + count += kvs.as_ref().len(); + ready(Ok(count)) + }) + .await?; + + assert_eq!(count, N); + eprintln!("count: {:?}", count); + + Ok(()) } diff --git a/foundationdb/tests/transact.rs b/foundationdb/tests/transact.rs index 45f3290..b1dbd14 100644 --- a/foundationdb/tests/transact.rs +++ b/foundationdb/tests/transact.rs @@ -5,6 +5,8 @@ // http://opensource.org/licenses/MIT>, at your option. This file may not be // copied, modified, or distributed except according to those terms. +#![feature(async_await, async_closure)] + use foundationdb; #[macro_use] @@ -13,18 +15,18 @@ extern crate lazy_static; #[macro_use] extern crate failure; -use foundationdb::*; -use futures::future::*; - mod common; -#[test] -fn test_transact_error() { +#[tokio::test] +async fn test_transact_error() -> Result<(), failure::Error> { common::setup_static(); - let fut = Cluster::new(foundationdb::default_config_path()) - .and_then(|cluster| cluster.create_database()) - .map_err(failure::Error::from) - .and_then(|db| db.transact(|_trx| -> Result<(), failure::Error> { bail!("failed") })); + let db = common::create_db().await?; + + let res = db + .transact(async move |_| -> Result<(), failure::Error> { bail!("failed") }) + .await; + + assert!(res.is_err()); - assert!(fut.wait().is_err(), "should return error"); + Ok(()) } diff --git a/foundationdb/tests/watch.rs b/foundationdb/tests/watch.rs index e5df6a9..cb81d7d 100644 --- a/foundationdb/tests/watch.rs +++ b/foundationdb/tests/watch.rs @@ -5,81 +5,63 @@ // http://opensource.org/licenses/MIT>, at your option. This file may not be // copied, modified, or distributed except according to those terms. -use foundationdb; +#![feature(async_await)] + +use foundationdb as fdb; #[macro_use] extern crate lazy_static; -use foundationdb::*; -use futures::future::*; +use fdb::*; mod common; -#[test] -fn test_watch() { +#[tokio::test] +async fn test_watch() -> error::Result<()> { common::setup_static(); const KEY: &'static [u8] = b"test-watch"; - let fut = Cluster::new(foundationdb::default_config_path()) - .and_then(|cluster| cluster.create_database()) - .and_then(|db| { - let watch = result(db.create_trx()).and_then(|trx| { - eprintln!("setting watch"); - let watch = trx.watch(KEY); - trx.commit().map(|_| { - eprintln!("watch committed"); - watch - }) - }); - - let write = result(db.create_trx()).and_then(|trx| { - eprintln!("writing value"); - - let value = common::random_str(10); - trx.set(KEY, value.as_bytes()); - trx.commit().map(|_| { - eprintln!("write committed"); - }) - }); - - // 1. Setup a watch with a key - watch.and_then(move |watch| { - // 2. After the watch is installed, try to update the key. - write - .and_then(move |_| { - // 3. After updating the key, waiting for the watch - watch - }) - .map(|_| { - // 4. watch fired as expected - eprintln!("watch fired"); - }) - }) - }); - - fut.wait().expect("failed to run") + let db = common::create_db().await?; + + let trx = db.create_trx()?; + eprintln!("setting watch"); + let watch = trx.watch(KEY); + trx.commit().await?; + eprintln!("watch committed"); + + let trx2 = db.create_trx()?; + eprintln!("writing value"); + + let value = common::random_str(10); + trx2.set(KEY, value.as_bytes()); + trx2.commit().await?; + eprintln!("write committed"); + + // 1. Setup a watch with a key + // 2. After the watch is installed, try to update the key. + // 3. After updating the key, waiting for the watch + watch.await?; + Ok(()) } -#[test] -fn test_watch_without_commit() { +#[tokio::test] +async fn test_watch_without_commit() -> error::Result<()> { common::setup_static(); const KEY: &'static [u8] = b"test-watch-2"; - let fut = Cluster::new(foundationdb::default_config_path()) - .and_then(|cluster| cluster.create_database()) - .and_then(|db| result(db.create_trx())) - .and_then(|trx| { - eprintln!("setting watch"); - - // trx will be dropped without `commit`, so a watch will be canceled - trx.watch(KEY) - }) - .or_else(|e| { - // should return error_code=1025, `Operation aborted because the transaction was - // canceled` - eprintln!("error as expected: {:?}", e); - Ok::<(), error::Error>(()) - }); - - fut.wait().expect("failed to run") + let db = common::create_db().await?; + let trx = db.create_trx()?; + eprintln!("setting watch"); + + // trx will be dropped without `commit`, so a watch will be canceled + let watch = trx.watch(KEY); + drop(trx); + let watch = watch.await; + + // should return error_code=1025, `Operation aborted because the transaction was + // canceled` + let e = watch.expect_err("should be cancelled"); + eprintln!("error as expected: {:?}", e); + + Ok(()) } diff --git a/scripts/install_foundationdb_macos.sh b/scripts/install_foundationdb_macos.sh index c14cb24..f8def9d 100755 --- a/scripts/install_foundationdb_macos.sh +++ b/scripts/install_foundationdb_macos.sh @@ -2,6 +2,6 @@ set -x -curl -O https://www.foundationdb.org/downloads/6.0.15/macOS/installers/FoundationDB-6.0.15.pkg +curl -O https://www.foundationdb.org/downloads/6.0.18/macOS/installers/FoundationDB-6.0.18.pkg sudo installer -pkg FoundationDB-6.0.15.pkg -target / diff --git a/scripts/run_bindingtester.sh b/scripts/run_bindingtester.sh index d0b84ee..acb7631 100755 --- a/scripts/run_bindingtester.sh +++ b/scripts/run_bindingtester.sh @@ -4,21 +4,23 @@ set -x fdb_rs_dir=$(pwd) -case $(uname) in - Darwin) - brew install mono - ;; - Linux) - sudo apt update - sudo apt install mono-devel -y - ;; - *) echo "only macOS or Ubuntu is supported" -esac +if ! command -v mono; then + case $(uname) in + Darwin) + brew install mono + ;; + Linux) + sudo apt update + sudo apt install mono-devel -y + ;; + *) echo "only macOS or Ubuntu is supported" + esac +fi ## build the rust bindingtester ( cd ${fdb_rs_dir:?} - cargo build --manifest-path foundationdb/Cargo.toml --bin bindingtester + cargo build --manifest-path foundationdb/Cargo.toml --bin bindingtester ) ## build the python bindings @@ -28,13 +30,15 @@ esac cd ${fdb_builddir:?} ## Get foundationdb source - git clone --depth 1 https://github.com/apple/foundationdb.git -b release-6.0 + if ! [[ -d foundationdb ]]; then + git clone --depth 1 https://github.com/apple/foundationdb.git -b release-6.0 + fi cd foundationdb git checkout release-6.0 ## need the python api bindings make fdb_python - + ## Run the test ./bindings/bindingtester/bindingtester.py --no-threads --seed 100 ${fdb_rs_dir:?}/target/debug/bindingtester ) From dbf6a4348e283e9218eb4a76427ee75ece7abb14 Mon Sep 17 00:00:00 2001 From: Matt Schallert Date: Sat, 7 Sep 2019 11:53:07 -0400 Subject: [PATCH 4/8] cleanup comments --- foundationdb-bench/src/bin/fdb-bench.rs | 1 + foundationdb-bench/src/bin/fdb-test.rs | 41 ------------------------- 2 files changed, 1 insertion(+), 41 deletions(-) diff --git a/foundationdb-bench/src/bin/fdb-bench.rs b/foundationdb-bench/src/bin/fdb-bench.rs index 1789228..dfe7a20 100644 --- a/foundationdb-bench/src/bin/fdb-bench.rs +++ b/foundationdb-bench/src/bin/fdb-bench.rs @@ -121,6 +121,7 @@ impl Bench { } async fn run_range(&self, r: std::ops::Range, counter: Counter) -> Result<()> { + println!("range: {:?}", r); let runners = r .into_iter() .map(|n| { diff --git a/foundationdb-bench/src/bin/fdb-test.rs b/foundationdb-bench/src/bin/fdb-test.rs index 3674c01..8d364c6 100644 --- a/foundationdb-bench/src/bin/fdb-test.rs +++ b/foundationdb-bench/src/bin/fdb-test.rs @@ -87,29 +87,6 @@ impl BenchRunner { } } } - - // fn step(mut self) -> Box, Error = Error>> { - // let trx = self.trx.take().unwrap(); - - // for _ in 0..self.trx_batch_size { - // self.rng.fill_bytes(&mut self.key_buf); - // self.rng.fill_bytes(&mut self.val_buf); - // self.key_buf[0] = 0x01; - // trx.set(&self.key_buf, &self.val_buf); - // } - - // let f = trx.commit().map(move |trx| { - // trx.reset(); - // self.trx = Some(trx); - - // if self.counter.decr(self.trx_batch_size) { - // Loop::Continue(self) - // } else { - // Loop::Break(()) - // } - // }); - // Box::new(f) - // } } #[derive(Clone)] @@ -135,11 +112,6 @@ impl Bench { let range = start..end; let counter = counter.clone(); let b = self.clone(); - // let handle = std::thread::spawn(move || b.run_range(range, counter).await); - // let handle = - // spawn( - // async move { poll_fn(move |_| blocking(|| b.run_range(range, counter))).await }, - // ); let handle = async move { b.run_range(range, counter).await }; handles.push(handle); @@ -231,19 +203,6 @@ fn main() { .expect("failed to get database"), ); - // { - // let trx = db.create_trx().unwrap(); - // let begin = keyselector::KeySelector::first_greater_or_equal(b"\x00"); - // let end = keyselector::KeySelector::first_greater_or_equal(b"\xff"); - // let range = RangeOptionBuilder::new(begin, end).build(); - // let res = trx.get_range(range, 100).await.unwrap(); - - // while let Some(kv) = res.next() { - // println!("{:?}", kv); - // } - // } - // return; - let mut handles = Vec::new(); for i in 0..opt.count { let s = format!("foo-{}", i); From 0d8396cf557b8844c3c308975e33353af8b67db5 Mon Sep 17 00:00:00 2001 From: Matt Schallert Date: Sat, 7 Sep 2019 12:27:13 -0400 Subject: [PATCH 5/8] cleanup cargo check --- foundationdb-bench/src/bin/fdb-bench.rs | 2 +- foundationdb-bench/src/bin/fdb-test.rs | 137 +--------------------- foundationdb/examples/class-scheduling.rs | 31 +++-- foundationdb/src/bin/bindingtester.rs | 2 +- foundationdb/src/database.rs | 2 +- foundationdb/src/future.rs | 12 +- foundationdb/src/lib.rs | 1 - foundationdb/tests/atomic.rs | 1 - foundationdb/tests/get.rs | 2 - foundationdb/tests/hca.rs | 1 - foundationdb/tests/range.rs | 2 +- foundationdb/tests/transact.rs | 2 - 12 files changed, 25 insertions(+), 170 deletions(-) diff --git a/foundationdb-bench/src/bin/fdb-bench.rs b/foundationdb-bench/src/bin/fdb-bench.rs index dfe7a20..dde05eb 100644 --- a/foundationdb-bench/src/bin/fdb-bench.rs +++ b/foundationdb-bench/src/bin/fdb-bench.rs @@ -11,7 +11,7 @@ use rand::{prelude::*, rngs::mock::StepRng}; use stopwatch::Stopwatch; use structopt::StructOpt; -use tokio::{prelude::*, runtime::Builder}; +use tokio::runtime::Builder; use foundationdb::{self as fdb, error::*, *}; diff --git a/foundationdb-bench/src/bin/fdb-test.rs b/foundationdb-bench/src/bin/fdb-test.rs index 8d364c6..a01dd7a 100644 --- a/foundationdb-bench/src/bin/fdb-test.rs +++ b/foundationdb-bench/src/bin/fdb-test.rs @@ -7,145 +7,14 @@ extern crate log; use env_logger; use structopt; -use std::sync::{atomic::*, Arc}; +use std::sync::Arc; use futures::future::*; -use rand::{prelude::*, rngs::mock::StepRng}; -use stopwatch::Stopwatch; use structopt::StructOpt; -use tokio::{prelude::*, runtime::Builder}; +use tokio::runtime::Builder; -use crate::fdb::{error::*, *}; - -#[derive(Clone)] -struct Counter { - size: usize, - inner: Arc, -} -impl Counter { - fn new(size: usize) -> Self { - Self { - size, - inner: Default::default(), - } - } - - fn decr(&self, n: usize) -> bool { - let val = self.inner.fetch_add(n, Ordering::SeqCst); - val < self.size - } -} - -struct BenchRunner { - #[allow(unused)] - db: Database, - counter: Counter, - key_buf: Vec, - val_buf: Vec, - rng: StepRng, - trx: Option, - trx_batch_size: usize, -} - -impl BenchRunner { - fn new(db: Database, rng: StepRng, counter: Counter, opt: &Opt) -> Self { - let mut key_buf = Vec::with_capacity(opt.key_len); - key_buf.resize(opt.key_len, 0u8); - - let mut val_buf = Vec::with_capacity(opt.val_len); - val_buf.resize(opt.val_len, 0u8); - - let trx = db.create_trx().expect("failed to create trx"); - - Self { - db, - counter, - key_buf, - val_buf, - - rng, - trx: Some(trx), - trx_batch_size: opt.trx_batch_size, - } - } - - async fn run(mut self) -> Result<()> { - let mut trx = self.trx.take().unwrap(); - loop { - for _ in 0..self.trx_batch_size { - self.rng.fill_bytes(&mut self.key_buf); - self.rng.fill_bytes(&mut self.val_buf); - self.key_buf[0] = 0x01; - trx.set(&self.key_buf, &self.val_buf); - } - - trx = trx.commit().await?; - trx.reset(); - if !self.counter.decr(self.trx_batch_size) { - return Ok(()); - } - } - } -} - -#[derive(Clone)] -struct Bench { - db: Database, - opt: Opt, -} - -impl Bench { - async fn run(self) { - let opt = &self.opt; - let counter = Counter::new(opt.count); - - let mut handles = Vec::new(); - - let sw = Stopwatch::start_new(); - - let step = (opt.queue_depth + opt.threads - 1) / opt.threads; - let mut start = 0; - for _ in 0..opt.threads { - let end = std::cmp::min(start + step, opt.queue_depth); - - let range = start..end; - let counter = counter.clone(); - let b = self.clone(); - let handle = async move { b.run_range(range, counter).await }; - handles.push(handle); - - start = end; - } - - for handle in handles { - handle.await.expect("failed to run bench"); - } - - let elapsed = sw.elapsed_ms() as usize; - - info!( - "bench took: {:?} ms, {:?} tps", - elapsed, - 1000 * opt.count / elapsed - ); - } - - async fn run_range(&self, r: std::ops::Range, counter: Counter) -> Result<()> { - let runners = r - .into_iter() - .map(|n| { - // With deterministic Rng, benchmark with same parameters will overwrite same set - // of keys again, which makes benchmark result stable. - let rng = StepRng::new(n as u64, 1); - BenchRunner::new(self.db.clone(), rng, counter.clone(), &self.opt).run() - }) - .collect::>(); - - join_all(runners).await; - Ok(()) - } -} +use crate::fdb::*; #[derive(StructOpt, Debug, Clone)] #[structopt(name = "fdb-bench")] diff --git a/foundationdb/examples/class-scheduling.rs b/foundationdb/examples/class-scheduling.rs index 333f4b1..ecd5c61 100644 --- a/foundationdb/examples/class-scheduling.rs +++ b/foundationdb/examples/class-scheduling.rs @@ -12,7 +12,6 @@ extern crate lazy_static; #[macro_use] extern crate failure; -use foundationdb; use rand; @@ -20,15 +19,14 @@ use std::{borrow::Cow, thread}; use self::rand::{rngs::ThreadRng, seq::SliceRandom}; -use foundationdb as fdb; use foundationdb::{ + self as fdb, error::Result as FdbResult, transaction::RangeOptionBuilder, tuple::{Decode, Encode}, Cluster, Database, Subspace, Transaction, }; - -use tokio::prelude::*; +use futures::future::join_all; // Data model: // ("attends", student, class) = "" @@ -144,7 +142,7 @@ async fn ditch(db: &Database, student: String, class: String) -> Result<(), fail let student = &student.clone(); let class = &class.clone(); db.transact(async move |trx| { - ditch_trx(&trx, student, class); + ditch_trx(&trx, student, class).await; Ok::<(), failure::Error>(()) }) .await?; @@ -317,21 +315,22 @@ async fn simulate_students(student_id: usize, num_ops: usize) { } async fn run_sim(db: &Database, students: usize, ops_per_student: usize) { - let mut threads: Vec<(usize, thread::JoinHandle<()>)> = Vec::with_capacity(students); + let mut futs = Vec::with_capacity(students); + + // let mut threads: Vec<(usize, thread::JoinHandle<()>)> = Vec::with_capacity(students); for i in 0..students { // TODO: ClusterInner has a mutable pointer reference, if thread-safe, mark that trait as Sync, then we can clone DB here... - threads.push(( - i, - thread::spawn(move || { - simulate_students(i, ops_per_student); - }), - )); + // threads.push(( + // i, + // thread::spawn(move || { + // simulate_students(i, ops_per_student); + // }), + // )); + futs.push(simulate_students(i, ops_per_student)); } // explicitly join... - for (id, thread) in threads { - thread.join().expect("failed to join thread"); - + for (id, _) in join_all(futs).await.iter().enumerate() { let student_id = format!("s{}", id); let attends_range = RangeOptionBuilder::from(("attends", &student_id)).build(); @@ -373,7 +372,7 @@ async fn main() { .expect("expected new cluster"); let db = db.create_database().await.expect("expected DB"); - init(&db, &*ALL_CLASSES); + init(&db, &*ALL_CLASSES).await; println!("Initialized"); run_sim(&db, 10, 10).await; diff --git a/foundationdb/src/bin/bindingtester.rs b/foundationdb/src/bin/bindingtester.rs index 7362074..3c7a021 100644 --- a/foundationdb/src/bin/bindingtester.rs +++ b/foundationdb/src/bin/bindingtester.rs @@ -14,7 +14,7 @@ use crate::fdb::{ *, }; use futures::{future::*, prelude::*}; -use tokio::{prelude::*, runtime::Runtime}; +use tokio::runtime::Runtime; use crate::fdb::options::{MutationType, StreamingMode}; fn mutation_from_str(s: &str) -> MutationType { diff --git a/foundationdb/src/database.rs b/foundationdb/src/database.rs index 3e184ec..771e72c 100644 --- a/foundationdb/src/database.rs +++ b/foundationdb/src/database.rs @@ -13,7 +13,7 @@ use std::{self, sync::Arc}; use foundationdb_sys as fdb; -use futures::future::{poll_fn, ready, Future, TryFuture}; +use futures::future::Future; use crate::{ cluster::*, diff --git a/foundationdb/src/future.rs b/foundationdb/src/future.rs index 03341c0..6996765 100644 --- a/foundationdb/src/future.rs +++ b/foundationdb/src/future.rs @@ -14,20 +14,14 @@ //! //! The Rust API Client has been implemented to use the Rust futures crate, and should work within that ecosystem (suchas Tokio). See Rust [futures](https://docs.rs/crate/futures/0.1.21) documentation. -use std::{self, ops::Deref}; - use foundationdb_sys as fdb; use std::{ - collections::hash_map::DefaultHasher, + self, future::Future, - hash::Hasher, - mem::{self, ManuallyDrop}, + ops::Deref, pin::Pin, - sync::{ - atomic::{AtomicBool, AtomicUsize, Ordering}, - Arc, Once, - }, + sync::Arc, task::{self, Poll}, }; use tokio_sync::AtomicWaker; diff --git a/foundationdb/src/lib.rs b/foundationdb/src/lib.rs index 75305c3..c65a1c7 100644 --- a/foundationdb/src/lib.rs +++ b/foundationdb/src/lib.rs @@ -120,7 +120,6 @@ extern crate failure; #[macro_use] extern crate failure_derive; -#[macro_use] extern crate futures; #[cfg(feature = "uuid")] diff --git a/foundationdb/tests/atomic.rs b/foundationdb/tests/atomic.rs index 17300d7..f818bbe 100644 --- a/foundationdb/tests/atomic.rs +++ b/foundationdb/tests/atomic.rs @@ -16,7 +16,6 @@ extern crate lazy_static; use byteorder::ByteOrder; use foundationdb::*; use futures::future::*; -use tokio::prelude::*; mod common; diff --git a/foundationdb/tests/get.rs b/foundationdb/tests/get.rs index 78f5e70..d57b477 100644 --- a/foundationdb/tests/get.rs +++ b/foundationdb/tests/get.rs @@ -17,8 +17,6 @@ use futures::future::*; mod common; -use tokio::prelude::*; - #[tokio::test] async fn test_set_get() -> error::Result<()> { common::setup_static(); diff --git a/foundationdb/tests/hca.rs b/foundationdb/tests/hca.rs index 0cd1d3b..051870f 100644 --- a/foundationdb/tests/hca.rs +++ b/foundationdb/tests/hca.rs @@ -8,7 +8,6 @@ #![feature(async_await, async_closure)] use foundationdb; -use futures; #[macro_use] extern crate lazy_static; diff --git a/foundationdb/tests/range.rs b/foundationdb/tests/range.rs index e4e65e4..d707825 100644 --- a/foundationdb/tests/range.rs +++ b/foundationdb/tests/range.rs @@ -24,7 +24,7 @@ async fn test_get_range() -> error::Result<()> { common::setup_static(); const N: usize = 10000; - let fut = Cluster::new(foundationdb::default_config_path()); + Cluster::new(foundationdb::default_config_path()).await?; let db = common::create_db().await?; let trx = db.create_trx()?; diff --git a/foundationdb/tests/transact.rs b/foundationdb/tests/transact.rs index b1dbd14..e770aab 100644 --- a/foundationdb/tests/transact.rs +++ b/foundationdb/tests/transact.rs @@ -7,8 +7,6 @@ #![feature(async_await, async_closure)] -use foundationdb; - #[macro_use] extern crate lazy_static; From 00fd51ffd93b7637fc6ddd51cb9cb98c1a39d054 Mon Sep 17 00:00:00 2001 From: Matt Schallert Date: Sun, 8 Sep 2019 12:09:45 -0400 Subject: [PATCH 6/8] more clippy cleanup --- foundationdb-bench/src/bin/fdb-bench.rs | 8 ++------ foundationdb-gen/src/lib.rs | 8 ++++---- foundationdb-sys/src/lib.rs | 1 + foundationdb/examples/class-scheduling.rs | 6 +++--- foundationdb/src/bin/bindingtester.rs | 10 ++++++---- foundationdb/src/cluster.rs | 3 ++- foundationdb/src/future.rs | 10 ++++++---- foundationdb/src/lib.rs | 2 +- foundationdb/src/network.rs | 6 +++--- foundationdb/src/tuple/element.rs | 14 ++++++++------ foundationdb/src/tuple/mod.rs | 4 ++-- foundationdb/tests/atomic.rs | 2 -- foundationdb/tests/hca.rs | 4 ++-- foundationdb/tests/watch.rs | 4 ++-- 14 files changed, 42 insertions(+), 40 deletions(-) diff --git a/foundationdb-bench/src/bin/fdb-bench.rs b/foundationdb-bench/src/bin/fdb-bench.rs index dde05eb..92adf8a 100644 --- a/foundationdb-bench/src/bin/fdb-bench.rs +++ b/foundationdb-bench/src/bin/fdb-bench.rs @@ -47,11 +47,8 @@ struct BenchRunner { impl BenchRunner { fn new(db: Database, rng: StepRng, counter: Counter, opt: &Opt) -> Self { - let mut key_buf = Vec::with_capacity(opt.key_len); - key_buf.resize(opt.key_len, 0u8); - - let mut val_buf = Vec::with_capacity(opt.val_len); - val_buf.resize(opt.val_len, 0u8); + let key_buf = vec![0; opt.key_len]; + let val_buf = vec![0; opt.val_len]; let trx = db.create_trx().expect("failed to create trx"); @@ -123,7 +120,6 @@ impl Bench { async fn run_range(&self, r: std::ops::Range, counter: Counter) -> Result<()> { println!("range: {:?}", r); let runners = r - .into_iter() .map(|n| { // With deterministic Rng, benchmark with same parameters will overwrite same set // of keys again, which makes benchmark result stable. diff --git a/foundationdb-gen/src/lib.rs b/foundationdb-gen/src/lib.rs index 6dc3fda..4b27696 100644 --- a/foundationdb-gen/src/lib.rs +++ b/foundationdb-gen/src/lib.rs @@ -320,7 +320,7 @@ where I: Iterator>, { let mut options = Vec::new(); - while let Some(e) = parser.next() { + for e in parser { let e = e?; match e { XmlEvent::StartElement { @@ -356,7 +356,7 @@ const OPTIONS_DATA: &[u8] = include_bytes!("C:/Program Files/foundationdb/include/foundationdb/fdb.options"); pub fn emit() -> Result { - let mut reader = OPTIONS_DATA.as_ref(); + let mut reader = OPTIONS_DATA; let parser = EventReader::new(&mut reader); let mut iter = parser.into_iter(); let mut scopes = Vec::new(); @@ -392,8 +392,8 @@ pub fn emit() -> Result { ); for scope in scopes.iter() { - result.push_str(&format!("{}", scope.gen_ty())); - result.push_str(&format!("{}", scope.gen_impl())); + result.push_str(&scope.gen_ty().to_string()); + result.push_str(&scope.gen_impl().to_string()); } Ok(result) diff --git a/foundationdb-sys/src/lib.rs b/foundationdb-sys/src/lib.rs index a38a13a..b35149f 100644 --- a/foundationdb-sys/src/lib.rs +++ b/foundationdb-sys/src/lib.rs @@ -1,5 +1,6 @@ #![allow(non_upper_case_globals)] #![allow(non_camel_case_types)] #![allow(non_snake_case)] +#![allow(clippy::all)] include!(concat!(env!("OUT_DIR"), "/bindings.rs")); diff --git a/foundationdb/examples/class-scheduling.rs b/foundationdb/examples/class-scheduling.rs index ecd5c61..d7b189b 100644 --- a/foundationdb/examples/class-scheduling.rs +++ b/foundationdb/examples/class-scheduling.rs @@ -281,7 +281,7 @@ async fn simulate_students(student_id: usize, num_ops: usize) { for _ in 0..num_ops { let mut moods = Vec::::new(); - if my_classes.len() > 0 { + if !my_classes.is_empty() { moods.push(Mood::Ditch); moods.push(Mood::Switch); } @@ -290,7 +290,7 @@ async fn simulate_students(student_id: usize, num_ops: usize) { moods.push(Mood::Add); } - let mood = moods.choose(&mut rng).map(|mood| *mood).unwrap(); + let mood = moods.choose(&mut rng).copied().unwrap(); // on errors we recheck for available classes if perform_op( @@ -341,7 +341,7 @@ async fn run_sim(db: &Database, students: usize, ops_per_student: usize) { .await .expect("get_range failed") .key_values() - .into_iter() + .iter() { let (_, s, class) = <(String, String, String)>::try_from(key_value.key()).unwrap(); assert_eq!(student_id, s); diff --git a/foundationdb/src/bin/bindingtester.rs b/foundationdb/src/bin/bindingtester.rs index 3c7a021..039b14b 100644 --- a/foundationdb/src/bin/bindingtester.rs +++ b/foundationdb/src/bin/bindingtester.rs @@ -440,6 +440,7 @@ impl StackMachine { self.stack.push(item); } + #[allow(clippy::cognitive_complexity)] async fn run_step(&mut self, number: usize, mut instr: Instr) { use crate::InstrCode::*; @@ -817,10 +818,11 @@ fn main() { let args = std::env::args().collect::>(); let prefix = &args[1]; - let mut cluster_path = fdb::default_config_path(); - if args.len() > 3 { - cluster_path = &args[3]; - } + let cluster_path = if args.len() > 3 { + &args[3] + } else { + fdb::default_config_path() + }; let api_version = args[2].parse::().expect("failed to parse api version"); diff --git a/foundationdb/src/cluster.rs b/foundationdb/src/cluster.rs index 349e4be..04c1735 100644 --- a/foundationdb/src/cluster.rs +++ b/foundationdb/src/cluster.rs @@ -29,6 +29,7 @@ impl Cluster { /// * `path` - A string giving a local path of a cluster file (often called ‘fdb.cluster’) which contains connection information for the FoundationDB cluster. See `foundationdb::default_config_path()` /// /// TODO: implement Default for Cluster where: If cluster_file_path is NULL or an empty string, then a default cluster file will be used. see + #[allow(clippy::new_ret_no_self)] pub async fn new(path: &str) -> Result { let path_str = std::ffi::CString::new(path).unwrap(); let fut = unsafe { @@ -37,7 +38,7 @@ impl Cluster { }; let result = fut.await?; let cluster = unsafe { result.get_cluster()? }; - Ok(Cluster { + Ok(Self { inner: Arc::new(ClusterInner::new(cluster)), }) } diff --git a/foundationdb/src/future.rs b/foundationdb/src/future.rs index 6996765..745b2ea 100644 --- a/foundationdb/src/future.rs +++ b/foundationdb/src/future.rs @@ -89,7 +89,7 @@ impl Future for FdbFuture { fut.registered = true; } - return Poll::Pending; + Poll::Pending } } @@ -241,8 +241,8 @@ impl FdbFutureResult { unsafe { std::slice::from_raw_parts(out_strings, out_len) }; let mut v = Vec::with_capacity(out_len); - for i in 0..out_len { - let cstr = unsafe { std::ffi::CStr::from_ptr(out_strings[i]) }; + for s in out_strings.iter().take(out_len) { + let cstr = unsafe { std::ffi::CStr::from_ptr(*s) }; v.push(cstr.to_bytes()); } Ok(v) @@ -265,7 +265,9 @@ impl FdbFutureResult { let out_len = out_len as usize; let out_keyvalues: &[fdb::keyvalue] = unsafe { std::slice::from_raw_parts(out_keyvalues, out_len) }; - let out_keyvalues: &[KeyValue<'_>] = unsafe { std::mem::transmute(out_keyvalues) }; + let out_keyvalues: &[KeyValue<'_>] = unsafe { + &*(out_keyvalues as *const [foundationdb_sys::keyvalue] as *const [KeyValue<'a>]) + }; Ok(KeyValues { keyvalues: out_keyvalues, more: (more != 0), diff --git a/foundationdb/src/lib.rs b/foundationdb/src/lib.rs index c65a1c7..03f06ea 100644 --- a/foundationdb/src/lib.rs +++ b/foundationdb/src/lib.rs @@ -134,7 +134,7 @@ pub mod hca; pub mod keyselector; pub mod network; /// Generated configuration types for use with the various `set_option` functions -#[allow(missing_docs)] +#[allow(missing_docs, clippy::all)] pub mod options; pub mod subspace; pub mod transaction; diff --git a/foundationdb/src/network.rs b/foundationdb/src/network.rs index a69ed80..d869363 100644 --- a/foundationdb/src/network.rs +++ b/foundationdb/src/network.rs @@ -50,7 +50,7 @@ impl Network { /// Unless your program is entirely event-driven based on results of asynchronous functions in this API and has no event loop of its own, you will want to invoke this function on an auxiliary thread (which it is your responsibility to create). /// /// This function will not return until `Network::stop` is called by you or a serious error occurs. You must not invoke `run` concurrently or reentrantly while it is already running. - pub fn run(&self) -> std::result::Result<(), failure::Error> { + pub fn run(self) -> std::result::Result<(), failure::Error> { if HAS_BEEN_RUN.compare_and_swap(false, true, Ordering::AcqRel) { return Err(format_err!("the network can only be run once per process")); } @@ -64,7 +64,7 @@ impl Network { } /// Waits for run to have started - pub fn wait(&self) { + pub fn wait(self) { // TODO: rather than a hot loop, consider a condvar here... while !HAS_BEEN_RUN.load(Ordering::Acquire) { thread::yield_now(); @@ -98,7 +98,7 @@ impl Network { /// network.stop().expect("failed to stop network"); /// handle.join().expect("failed to join fdb thread"); /// ``` - pub fn stop(&self) -> std::result::Result<(), failure::Error> { + pub fn stop(self) -> std::result::Result<(), failure::Error> { if !HAS_BEEN_RUN.load(Ordering::Acquire) { return Err(format_err!( "the network must be runn before trying to stop" diff --git a/foundationdb/src/tuple/element.rs b/foundationdb/src/tuple/element.rs index 19e6d59..5d2c1ef 100644 --- a/foundationdb/src/tuple/element.rs +++ b/foundationdb/src/tuple/element.rs @@ -23,6 +23,7 @@ const VERSIONSTAMP: u8 = 0x33; pub(super) const ESCAPE: u8 = 0xff; +#[allow(clippy::identity_op)] const SIZE_LIMITS: &[u64] = &[ 0, (1 << (1 * 8)) - 1, @@ -106,15 +107,16 @@ fn decode_bytes(buf: &[u8]) -> Result<(Vec, usize)> { Ok((out, offset + 1)) } +#[allow(clippy::nonminimal_bool)] fn adjust_float_bytes(b: &mut [u8], encode: bool) { if (encode && b[0] & 0x80 != 0x00) || (!encode && b[0] & 0x80 == 0x00) { // Negative numbers: flip all of the bytes. for byte in b.iter_mut() { - *byte = *byte ^ 0xff + *byte ^= 0xff } } else { // Positive number: flip just the sign bit. - b[0] = b[0] ^ 0x80 + b[0] ^= 0x80 } } @@ -445,7 +447,7 @@ impl Decode for i64 { return Err(Error::InvalidData); } - (&mut data[(8 - n)..8]).copy_from_slice(&buf[1..(n + 1)]); + (&mut data[(8 - n)..8]).copy_from_slice(&buf[1..=n]); let val = byteorder::BE::read_u64(&data); let max = i64::max_value() as u64; if val <= max { @@ -460,7 +462,7 @@ impl Decode for i64 { return Err(Error::InvalidData); } - (&mut data[(8 - n)..8]).copy_from_slice(&buf[1..(n + 1)]); + (&mut data[(8 - n)..8]).copy_from_slice(&buf[1..=n]); let shift = SIZE_LIMITS[n]; let val = byteorder::BE::read_u64(&data); @@ -625,7 +627,7 @@ mod tests { test_round_trip(10000i64, &[22, 39, 16]); test_round_trip(-100i64, &[19, 155]); test_round_trip(-10000i64, &[18, 216, 239]); - test_round_trip(-1000000i64, &[17, 240, 189, 191]); + test_round_trip(-1_000_000i64, &[17, 240, 189, 191]); // boundary condition test_round_trip(255i64, &[21, 255]); @@ -668,7 +670,7 @@ mod tests { #[test] fn test_large_neg() { test_round_trip( - -8617230260136600747, + -8_617_230_260_136_600_747, &[0x0c, 0x88, 0x69, 0x72, 0xbc, 0x04, 0xcf, 0x9b, 0x54], ); } diff --git a/foundationdb/src/tuple/mod.rs b/foundationdb/src/tuple/mod.rs index 9b6c09f..186cf65 100644 --- a/foundationdb/src/tuple/mod.rs +++ b/foundationdb/src/tuple/mod.rs @@ -57,12 +57,12 @@ impl TupleDepth { } /// Increment the depth by one, this be called when calling into `Tuple::{encode, decode}` of tuple-like datastructures - pub fn increment(&self) -> Self { + pub fn increment(self) -> Self { TupleDepth(self.0 + 1) } /// Returns the current depth in any recursive tuple processing, 0 representing there having been no recursion - pub fn depth(&self) -> usize { + pub fn depth(self) -> usize { self.0 } } diff --git a/foundationdb/tests/atomic.rs b/foundationdb/tests/atomic.rs index f818bbe..f4595e6 100644 --- a/foundationdb/tests/atomic.rs +++ b/foundationdb/tests/atomic.rs @@ -50,7 +50,6 @@ async fn test_atomic() -> error::Result<()> { // Run `n` add(1) operations in parallel let db0 = db.clone(); let fut_add_list = (0..n) - .into_iter() .map(move |_| atomic_add(db0.clone(), KEY, 1)) .collect::>(); let fut_add = join_all(fut_add_list); @@ -58,7 +57,6 @@ async fn test_atomic() -> error::Result<()> { // Run `n` add(-1) operations in parallel let db0 = db.clone(); let fut_sub_list = (0..n) - .into_iter() .map(move |_| atomic_add(db0.clone(), KEY, -1)) .collect::>(); let fut_sub = join_all(fut_sub_list); diff --git a/foundationdb/tests/hca.rs b/foundationdb/tests/hca.rs index 051870f..ff76e74 100644 --- a/foundationdb/tests/hca.rs +++ b/foundationdb/tests/hca.rs @@ -91,8 +91,8 @@ async fn test_hca_concurrent_allocations() -> error::Result<()> { Ok(()) } -fn check_hca_result_uniqueness(results: &Vec) { - let result_set: HashSet = HashSet::from_iter(results.clone()); +fn check_hca_result_uniqueness(results: &[i64]) { + let result_set: HashSet = HashSet::from_iter(results.to_owned()); if results.len() != result_set.len() { panic!( diff --git a/foundationdb/tests/watch.rs b/foundationdb/tests/watch.rs index cb81d7d..7e0084a 100644 --- a/foundationdb/tests/watch.rs +++ b/foundationdb/tests/watch.rs @@ -19,7 +19,7 @@ mod common; #[tokio::test] async fn test_watch() -> error::Result<()> { common::setup_static(); - const KEY: &'static [u8] = b"test-watch"; + const KEY: &[u8] = b"test-watch"; let db = common::create_db().await?; @@ -47,7 +47,7 @@ async fn test_watch() -> error::Result<()> { #[tokio::test] async fn test_watch_without_commit() -> error::Result<()> { common::setup_static(); - const KEY: &'static [u8] = b"test-watch-2"; + const KEY: &[u8] = b"test-watch-2"; let db = common::create_db().await?; let trx = db.create_trx()?; From 93e2a1da42bb3181aaf8aec3e8b2525ce7f0664c Mon Sep 17 00:00:00 2001 From: Matt Schallert Date: Sun, 8 Sep 2019 12:18:03 -0400 Subject: [PATCH 7/8] remove test file --- foundationdb-bench/src/bin/fdb-bench.rs | 1 - foundationdb-bench/src/bin/fdb-test.rs | 93 ------------------------- 2 files changed, 94 deletions(-) delete mode 100644 foundationdb-bench/src/bin/fdb-test.rs diff --git a/foundationdb-bench/src/bin/fdb-bench.rs b/foundationdb-bench/src/bin/fdb-bench.rs index 92adf8a..eb94ee4 100644 --- a/foundationdb-bench/src/bin/fdb-bench.rs +++ b/foundationdb-bench/src/bin/fdb-bench.rs @@ -118,7 +118,6 @@ impl Bench { } async fn run_range(&self, r: std::ops::Range, counter: Counter) -> Result<()> { - println!("range: {:?}", r); let runners = r .map(|n| { // With deterministic Rng, benchmark with same parameters will overwrite same set diff --git a/foundationdb-bench/src/bin/fdb-test.rs b/foundationdb-bench/src/bin/fdb-test.rs deleted file mode 100644 index a01dd7a..0000000 --- a/foundationdb-bench/src/bin/fdb-test.rs +++ /dev/null @@ -1,93 +0,0 @@ -#![feature(async_await)] - -extern crate foundationdb as fdb; - -#[macro_use] -extern crate log; -use env_logger; -use structopt; - -use std::sync::Arc; - -use futures::future::*; -use structopt::StructOpt; - -use tokio::runtime::Builder; - -use crate::fdb::*; - -#[derive(StructOpt, Debug, Clone)] -#[structopt(name = "fdb-bench")] -struct Opt { - #[structopt(short = "t", long = "threads", default_value = "1")] - threads: usize, - - #[structopt(short = "q", long = "queue-depth", default_value = "1000")] - queue_depth: usize, - - #[structopt(short = "c", long = "count", default_value = "300000")] - count: usize, - - #[structopt(long = "trx-batch-size", default_value = "10")] - trx_batch_size: usize, - - #[structopt(long = "key-len", default_value = "10")] - key_len: usize, - #[structopt(long = "val-len", default_value = "100")] - val_len: usize, -} - -fn main() { - env_logger::init(); - let opt = Opt::from_args(); - - info!("opt: {:?}", opt); - - let rt = Builder::new() - .build() - .expect("failed to build tokio runtime"); - - let network = fdb::init().expect("failed to init network"); - - let handle = std::thread::spawn(move || { - let error = network.run(); - - if let Err(error) = error { - panic!("fdb_run_network: {}", error); - } - }); - - network.wait(); - - rt.block_on(async { - let cluster_path = fdb::default_config_path(); - let cluster = Cluster::new(cluster_path) - .await - .expect("failed to create cluster"); - - let db = Arc::new( - cluster - .create_database() - .await - .expect("failed to get database"), - ); - - let mut handles = Vec::new(); - for i in 0..opt.count { - let s = format!("foo-{}", i); - let db = db.clone(); - handles.push(async move { - let s = s.as_bytes(); - let trx = db.create_trx().unwrap(); - trx.set(s, s); - trx.commit().await.unwrap(); - }); - } - - join_all(handles).await; - println!("put foo at bar"); - }); - - network.stop().expect("failed to stop network"); - handle.join().expect("failed to join fdb thread"); -} From 55cae3c05b0389af827b43caed64682f84a4793f Mon Sep 17 00:00:00 2001 From: Matt Schallert Date: Sun, 8 Sep 2019 13:17:00 -0400 Subject: [PATCH 8/8] poll cleanup --- foundationdb-bench/Cargo.toml | 1 - foundationdb/Cargo.toml | 2 - foundationdb/src/bin/bindingtester.rs | 2 +- foundationdb/src/database.rs | 1 - foundationdb/src/future.rs | 7 +- foundationdb/src/transaction.rs | 100 +++++++++++--------------- 6 files changed, 43 insertions(+), 70 deletions(-) diff --git a/foundationdb-bench/Cargo.toml b/foundationdb-bench/Cargo.toml index a5f58cb..a7666ea 100644 --- a/foundationdb-bench/Cargo.toml +++ b/foundationdb-bench/Cargo.toml @@ -29,6 +29,5 @@ log = "0.4" env_logger = "0.6" structopt = "0.2" tokio = "=0.2.0-alpha.2" -# tokio-sync = "=0.2.0-alpha.1" [build-dependencies] diff --git a/foundationdb/Cargo.toml b/foundationdb/Cargo.toml index 2f224e0..0e14669 100644 --- a/foundationdb/Cargo.toml +++ b/foundationdb/Cargo.toml @@ -35,14 +35,12 @@ failure = "0.1" failure_derive = "0.1" foundationdb-sys = { version = "0.2.0", path = "../foundationdb-sys", default-features = false } futures-preview = { version = "0.3.0-alpha.17", features = ["compat", "nightly", "async-await"] } -futures01 = { package = "futures", version = "0.1", optional = true } lazy_static = "1.0" byteorder = "1.2" log = "0.4" uuid = { version = "0.7", optional = true } rand = "0.7.0" tokio = "=0.2.0-alpha.2" -tokio-sync = "=0.2.0-alpha.2" [dev-dependencies] rand = "0.7" diff --git a/foundationdb/src/bin/bindingtester.rs b/foundationdb/src/bin/bindingtester.rs index 039b14b..e86abe3 100644 --- a/foundationdb/src/bin/bindingtester.rs +++ b/foundationdb/src/bin/bindingtester.rs @@ -506,7 +506,7 @@ impl StackMachine { let code: i64 = self.pop_item().await; let trx0 = trx.clone(); let f = async move { - trx0.on_error(Error::from(code as i32)).await?; + trx0.on_error(Error::from(code as i32)).await; Ok((trx0, b"RESULT_NOT_PRESENT".to_vec())) }; self.push_fut(number, f); diff --git a/foundationdb/src/database.rs b/foundationdb/src/database.rs index 771e72c..c529941 100644 --- a/foundationdb/src/database.rs +++ b/foundationdb/src/database.rs @@ -69,7 +69,6 @@ impl Database { where F: FnMut(Transaction) -> Fut, Fut: Future>, - // Item: 'static, Error: From, { let db = self.clone(); diff --git a/foundationdb/src/future.rs b/foundationdb/src/future.rs index 745b2ea..b3f5bea 100644 --- a/foundationdb/src/future.rs +++ b/foundationdb/src/future.rs @@ -16,6 +16,7 @@ use foundationdb_sys as fdb; +use futures::task::AtomicWaker; use std::{ self, future::Future, @@ -24,7 +25,6 @@ use std::{ sync::Arc, task::{self, Poll}, }; -use tokio_sync::AtomicWaker; use crate::error::{self, Result}; @@ -54,7 +54,6 @@ impl Drop for FdbFuture { // `fdb_future_cancel` explicitly. unsafe { fdb::fdb_future_destroy(f) } } - // self.waker.do_drop(); } } @@ -65,7 +64,7 @@ impl Future for FdbFuture { let f = self.f.expect("cannot poll after resolve"); let fut = self.get_mut(); - fut.waker.register_by_ref(cx.waker()); + fut.waker.register(cx.waker()); let ready = unsafe { fdb::fdb_future_is_ready(f) }; if ready != 0 { @@ -73,12 +72,10 @@ impl Future for FdbFuture { // The result is taking ownership of fdb::FDBFuture let g = FdbFutureResult::new(fut.f.take().unwrap()); - // fut.waker.do_drop(); return Poll::Ready(Ok(g)); } if !fut.registered { - // let waker_ptr = &fut.waker as *const _; let w = fut.waker.clone(); let wp = Arc::into_raw(w); diff --git a/foundationdb/src/transaction.rs b/foundationdb/src/transaction.rs index 456f97b..c17cf62 100644 --- a/foundationdb/src/transaction.rs +++ b/foundationdb/src/transaction.rs @@ -11,7 +11,7 @@ //! https://apple.github.io/foundationdb/api-c.html#transaction use foundationdb_sys as fdb; -use futures::{ready, Stream}; +use futures::{ready, FutureExt, Stream}; use std::{ self, fmt, future::Future, @@ -661,15 +661,10 @@ pub struct TrxGet { impl Future for TrxGet { type Output = error::Result; - fn poll(self: Pin<&mut Self>, cx: &mut task::Context) -> Poll { - match Pin::new(&mut self.get_mut().inner).poll(cx) { - Poll::Pending => Poll::Pending, - Poll::Ready(Err(e)) => Poll::Ready(Err(e)), - Poll::Ready(Ok((trx, inner))) => { - inner.get_value()?; - Poll::Ready(Ok(GetResult { trx, inner })) - } - } + fn poll(mut self: Pin<&mut Self>, cx: &mut task::Context) -> Poll { + let (trx, inner) = ready!(self.inner.poll_unpin(cx))?; + inner.get_value()?; + Poll::Ready(Ok(GetResult { trx, inner })) } } @@ -697,14 +692,10 @@ pub struct TrxGetKey { impl Future for TrxGetKey { type Output = Result; - fn poll(self: Pin<&mut Self>, cx: &mut task::Context) -> Poll { - // TODO(schallert): use the new futures "ready!" macro once this - // compiles. - match Pin::new(&mut self.get_mut().inner).poll(cx) { - Poll::Pending => Poll::Pending, - Poll::Ready(Err(e)) => Poll::Ready(Err(e)), - Poll::Ready(Ok((trx, inner))) => Poll::Ready(Ok(GetKeyResult { trx, inner })), - } + fn poll(mut self: Pin<&mut Self>, cx: &mut task::Context) -> Poll { + let (trx, inner) = ready!(self.inner.poll_unpin(cx))?; + inner.get_key()?; + Poll::Ready(Ok(GetKeyResult { trx, inner })) } } @@ -779,20 +770,11 @@ pub struct TrxGetRange { impl Future for TrxGetRange { type Output = Result; - fn poll(self: Pin<&mut Self>, cx: &mut task::Context) -> Poll { - let fut = self.get_mut(); - match Pin::new(&mut fut.inner).poll(cx) { - Poll::Pending => Poll::Pending, - Poll::Ready(res) => match res { - Err(e) => Poll::Ready(Result::Err(e)), - Ok((trx, inner)) => { - // tests if the future resolves to keyvalue array. - inner.get_keyvalue_array()?; - let opt = fut.opt.take().expect("should not poll after ready"); - Poll::Ready(Result::Ok(GetRangeResult { trx, inner, opt })) - } - }, - } + fn poll(mut self: Pin<&mut Self>, cx: &mut task::Context) -> Poll { + let (trx, inner) = ready!(self.inner.poll_unpin(cx))?; + inner.get_keyvalue_array()?; + let opt = self.opt.take().expect("should not poll after ready"); + Poll::Ready(Ok(GetRangeResult { trx, inner, opt })) } } @@ -827,7 +809,6 @@ impl From<(RangeOption, Error)> for error::Error { impl<'a> Stream for RangeStream { type Item = std::result::Result; - // fn poll(&mut self) -> std::result::Result>, Self::Error> { fn poll_next(self: Pin<&mut Self>, cx: &mut task::Context) -> Poll> { if self.inner.is_none() { return Poll::Ready(None); @@ -884,9 +865,8 @@ impl WatchFuture { impl Future for WatchFuture { type Output = Result<()>; - fn poll(self: Pin<&mut Self>, cx: &mut task::Context) -> Poll { - let fut = self.get_mut(); - ready!(Pin::new(&mut fut.inner).poll(cx))?; + fn poll(mut self: Pin<&mut Self>, cx: &mut task::Context) -> Poll { + ready!(self.inner.poll_unpin(cx))?; Poll::Ready(Ok(())) } } @@ -904,9 +884,8 @@ impl VersionstampFuture { impl Future for VersionstampFuture { type Output = Result; - fn poll(self: Pin<&mut Self>, cx: &mut task::Context) -> Poll { - let fut = self.get_mut(); - let res = ready!(Pin::new(&mut fut.inner).poll(cx))?; + fn poll(mut self: Pin<&mut Self>, cx: &mut task::Context) -> Poll { + let res = ready!(self.inner.poll_unpin(cx))?; let key = res.get_key()?; let mut buf: [u8; 10] = Default::default(); buf.copy_from_slice(key); @@ -946,8 +925,8 @@ impl NonTrxFuture { impl Future for NonTrxFuture { type Output = Result; - fn poll(self: Pin<&mut Self>, cx: &mut task::Context) -> Poll { - Pin::new(&mut self.get_mut().inner).poll(cx) + fn poll(mut self: Pin<&mut Self>, cx: &mut task::Context) -> Poll { + self.inner.poll_unpin(cx) } } @@ -972,14 +951,17 @@ impl TrxErrFuture { } impl Future for TrxErrFuture { - type Output = Result; - - fn poll(self: Pin<&mut Self>, cx: &mut task::Context) -> Poll { - let fut = self.get_mut(); - ready!(Pin::new(&mut fut.inner).poll(cx))?; - let mut e = fut.err.take().expect("should not poll after ready"); - e.set_should_retry(true); - Poll::Ready(Ok(e)) + type Output = Error; + + fn poll(mut self: Pin<&mut Self>, cx: &mut task::Context) -> Poll { + match ready!(self.inner.poll_unpin(cx)) { + Ok(_) => { + let mut e = self.err.take().expect("should not poll after ready"); + e.set_should_retry(true); + Poll::Ready(e) + } + Err(e) => Poll::Ready(e), + } } } @@ -1006,14 +988,13 @@ impl TrxFuture { impl Future for TrxFuture { type Output = Result<(Transaction, FdbFutureResult)>; - fn poll(self: Pin<&mut Self>, cx: &mut task::Context) -> Poll { - let mut fut = self.get_mut(); - if fut.f_err.is_none() { - match Pin::new(&mut fut.inner).poll(cx) { + fn poll(mut self: Pin<&mut Self>, cx: &mut task::Context) -> Poll { + if self.f_err.is_none() { + match self.inner.poll_unpin(cx) { Poll::Pending => return Poll::Pending, Poll::Ready(Ok(res)) => { return Poll::Ready(Ok(( - fut.trx.take().expect("should not poll after ready"), + self.trx.take().expect("should not poll after ready"), res, ))); } @@ -1021,17 +1002,16 @@ impl Future for TrxFuture { // The transaction will be dropped on `TrxErrFuture::new`. The `trx` is a last // reference for the transaction, undering transaction will be destroyed at // this point. - let trx = fut.trx.take().expect("should not poll after error"); - fut.f_err = Some(TrxErrFuture::new(trx, e)); - return Pin::new(fut).poll(cx); + let trx = self.trx.take().expect("should not poll after error"); + self.f_err = Some(TrxErrFuture::new(trx, e)); + return self.poll_unpin(cx); } } } - match Pin::new(&mut fut.f_err.as_mut().unwrap()).poll(cx) { + match self.f_err.as_mut().unwrap().poll_unpin(cx) { Poll::Pending => Poll::Pending, - Poll::Ready(Ok(res)) => Poll::Ready(Err(res)), - Poll::Ready(Err(e)) => Poll::Ready(Err(e)), + Poll::Ready(e) => Poll::Ready(Err(e)), } } }