diff --git a/bookmarks_old/dbbookmarks/src/lib.rs b/bookmarks_old/dbbookmarks/src/lib.rs deleted file mode 100644 index 2b51ab9d2f..0000000000 --- a/bookmarks_old/dbbookmarks/src/lib.rs +++ /dev/null @@ -1,241 +0,0 @@ -// Copyright (c) 2004-present, Facebook, Inc. -// All Rights Reserved. -// -// This software may be used and distributed according to the terms of the -// GNU General Public License version 2 or any later version. - -#![deny(warnings)] -#![feature(try_from)] - -extern crate bookmarks; - -extern crate ascii; -extern crate failure_ext as failure; -extern crate futures; -extern crate mysql; -#[macro_use] -extern crate mysql_async; -extern crate tokio_core; - -extern crate db; -extern crate futures_ext; -extern crate mercurial_types; -extern crate sendwrapper; -extern crate storage_types; - -use std::convert::TryFrom; -use std::rc::Rc; - -use ascii::AsciiStr; -use failure::{Error, SyncFailure}; -use futures::{future, stream, Future, Stream}; -use mysql_async::{Opts, Pool, Row, TransactionOptions}; -use mysql_async::prelude::*; -use tokio_core::reactor::Remote; - -use bookmarks::{Bookmarks, BookmarksMut}; -use db::ConnectionParams; -use futures_ext::{BoxFuture, BoxFutureNonSend, BoxStream, FutureExt, StreamExt}; -use mercurial_types::nodehash::HgChangesetId; -use sendwrapper::SendWrapper; -use storage_types::Version; - -pub struct DbBookmarks { - wrapper: SendWrapper, -} - -impl DbBookmarks { - pub fn new_async(params: ConnectionParams, remote: &Remote) -> BoxFuture { - SendWrapper::new(remote, |handle| { - Opts::try_from(params) - .and_then(|opts| Ok(Pool::new(opts, handle))) - .map_err(Into::into) - }).and_then(|wrapper| Ok(DbBookmarks { wrapper })) - .boxify() - } -} - -impl Bookmarks for DbBookmarks { - fn get(&self, key: &AsRef<[u8]>) -> BoxFuture, Error> { - let key = key.as_ref().to_vec(); - self.wrapper - .with_inner(move |pool| get_bookmark(pool, key)) - .map_err(|e| e.context("DbBookmarks get failed").into()) - .boxify() - } - - fn keys(&self) -> BoxStream, Error> { - self.wrapper - .with_inner(move |pool| list_keys(pool)) - .flatten_stream() - .map_err(|e| e.context("DbBookmarks keys failed").into()) - .boxify() - } -} - -impl BookmarksMut for DbBookmarks { - fn set( - &self, - key: &AsRef<[u8]>, - value: &HgChangesetId, - version: &Version, - ) -> BoxFuture, Error> { - let key = key.as_ref().to_vec(); - let value = value.clone(); - let version = version.clone(); - self.wrapper - .with_inner(move |pool| set_bookmark(pool, key, value, version)) - .map_err(|e| e.context("DbBookmarks set failed").into()) - .boxify() - } - - fn delete(&self, key: &AsRef<[u8]>, version: &Version) -> BoxFuture, Error> { - let key = key.as_ref().to_vec(); - let version = version.clone(); - self.wrapper - .with_inner(move |pool| delete_bookmark(pool, key, version)) - .map_err(|e| e.context("DbBookmarks delete failed").into()) - .boxify() - } -} - -fn list_keys(pool: Rc) -> BoxFutureNonSend, Error>, Error> { - pool.get_conn() - .and_then(|conn| conn.query("SELECT name FROM bookmarks")) - .and_then(|res| res.collect::<(Vec,)>()) - .map(|(_, rows)| stream::iter_ok(rows.into_iter().map(|row| row.0)).boxify()) - .map_err(|e| SyncFailure::new(e).into()) - .boxify_nonsend() -} - -fn get_bookmark( - pool: Rc, - key: Vec, -) -> BoxFutureNonSend, Error> { - pool.get_conn() - .and_then(|conn| { - conn.prep_exec( - "SELECT value, version FROM bookmarks WHERE name = ?", - (key,), - ) - }) - .and_then(|res| res.collect::<(String, u64)>()) - .map_err(|e| SyncFailure::new(e).into()) - .and_then(|(_, mut rows)| { - if let Some((value, version)) = rows.pop() { - let value = AsciiStr::from_ascii(&value)?; - let value = HgChangesetId::from_ascii_str(&value)?; - Ok(Some((value, Version::from(version)))) - } else { - Ok(None) - } - }) - .boxify_nonsend() -} - -fn set_bookmark( - pool: Rc, - key: Vec, - value: HgChangesetId, - version: Version, -) -> BoxFutureNonSend, Error> { - pool.get_conn() - // Need to use a transaction since we need to perform both a read (to get the - // current version, if any) and a write (to set the bookmark). - .and_then(|conn| conn.start_transaction(TransactionOptions::new())) - .and_then({ - let key = key.clone(); - // Get current version for this bookmark (if the key is present). - move |txn| txn.prep_exec("SELECT version FROM bookmarks WHERE name = ?", (key,)) - }) - .and_then(|res| res.collect_and_drop::<(u64,)>()) - // At this point, change the `Error` type of this combinator chain to this crate's - // `Error` type so we can return custom errors. This means all subsequent `Future`s - // from `mysql_async` will need `.from_err()` to convert to our `Error` type. - .map_err(|e| SyncFailure::new(e).into()) - .and_then(move |(txn, mut rows)| { - // Get the current and new versions for this bookmark. If the bookmark is not present, - // default to a current version of Version::absent() and a new version of 0. - let raw_version = rows.pop().map(|row| row.0); - let old_version = raw_version.map(|v| Version::from(v)).unwrap_or_default(); - let new_version = raw_version.map(|v| Version::from(v+1)).unwrap_or(Version::from(0)); - - // If version matches the one passed by the caller, write the new value and version. - if version == old_version { - let value: String = value.to_hex().into(); - txn.prep_exec( - "INSERT INTO bookmarks (name, value, version) \ - VALUES (:key, :value, 0) \ - ON DUPLICATE KEY UPDATE \ - value = :value, version = version + 1", - params!(key, value), - ).and_then(|res| res.drop_result()) - // Commit the transaction, and return the new version. - .and_then(|txn| txn.commit()) - .map_err(|e| SyncFailure::new(e).into()) - .map(move |_| Some(new_version)) - .boxify_nonsend() - } else { - future::ok(None).boxify_nonsend() - } - }) - .boxify_nonsend() -} - -fn delete_bookmark( - pool: Rc, - key: Vec, - version: Version, -) -> BoxFutureNonSend, Error> { - pool.get_conn() - .and_then(move |conn| { - // Do we expect this bookmark to exist at all? (i.e., did the caller pass the - // the absent version?) If so, then attempt the deletion and see if it succeeds. - // Otherwise, just check to make sure the bookmark is actually absent. - if let Version(Some(v)) = version { - conn.prep_exec( - "DELETE FROM bookmarks WHERE name = :key AND version = :v", - params!(key, v), - ).map(|res| if res.affected_rows() > 0 { - Some(Version::absent()) - } else { - None - }) - .boxify_nonsend() - } else { - // Caller passed the absent version, so this is a no-op. That said, we - // still need to verify that the bookmark is actually not present, and if - // it is, signal an error by returning None. - conn.prep_exec("SELECT 1 FROM bookmarks WHERE name = ?", (key,)) - .and_then(|res| res.collect::()) - .map(|(_, rows)| if rows.is_empty() { - Some(Version::absent()) - } else { - None - }) - .boxify_nonsend() - } - }) - .map_err(|e| SyncFailure::new(e).into()) - .boxify_nonsend() -} - -pub fn init_test_db() -> ConnectionParams { - let params = db::create_test_db("mononoke_dbbookmarks").unwrap(); - let pool = mysql::Pool::new(params.clone()).unwrap(); - - let _ = pool.prep_exec( - "CREATE TABLE bookmarks ( - id INTEGER PRIMARY KEY AUTO_INCREMENT, - name VARBINARY(256) NOT NULL, - value VARCHAR(40) NOT NULL, - version INTEGER UNSIGNED NOT NULL, - created TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - modified TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - UNIQUE KEY (name) - );", - (), - ).unwrap(); - - params -} diff --git a/bookmarks_old/filebookmarks/src/lib.rs b/bookmarks_old/filebookmarks/src/lib.rs deleted file mode 100644 index 77eb721514..0000000000 --- a/bookmarks_old/filebookmarks/src/lib.rs +++ /dev/null @@ -1,121 +0,0 @@ -// Copyright (c) 2004-present, Facebook, Inc. -// All Rights Reserved. -// -// This software may be used and distributed according to the terms of the -// GNU General Public License version 2 or any later version. - -#![deny(warnings)] - -extern crate bookmarks; - -extern crate failure_ext as failure; -extern crate futures; -extern crate futures_cpupool; -extern crate percent_encoding; - -extern crate filekv; -extern crate futures_ext; -extern crate mercurial_types; -extern crate storage_types; - -use std::path::PathBuf; -use std::str; -use std::sync::Arc; - -use failure::{Error, Result}; -use futures::{Future, Stream}; -use futures_cpupool::CpuPool; -use percent_encoding::{percent_decode, percent_encode, DEFAULT_ENCODE_SET}; - -use bookmarks::{Bookmarks, BookmarksMut}; -use filekv::FileKV; -use futures_ext::{BoxFuture, BoxStream, FutureExt, StreamExt}; -use mercurial_types::nodehash::HgChangesetId; -use storage_types::Version; - -static PREFIX: &'static str = "bookmark:"; - -/// A basic file-based persistent bookmark store. -/// -/// Bookmarks are stored as files in the specified base directory. File operations are dispatched -/// to a thread pool to avoid blocking the main thread. File accesses between these threads -/// are synchronized by a global map of per-path locks. -pub struct FileBookmarks { - kv: FileKV, -} - -impl FileBookmarks { - #[inline] - pub fn open>(path: P) -> Result { - Ok(FileBookmarks { - kv: FileKV::open(path, PREFIX)?, - }) - } - - #[inline] - pub fn open_with_pool>(path: P, pool: Arc) -> Result { - Ok(FileBookmarks { - kv: FileKV::open_with_pool(path, PREFIX, pool)?, - }) - } - - #[inline] - pub fn create>(path: P) -> Result { - Ok(FileBookmarks { - kv: FileKV::create(path, PREFIX)?, - }) - } - - #[inline] - pub fn create_with_pool>(path: P, pool: Arc) -> Result { - Ok(FileBookmarks { - kv: FileKV::create_with_pool(path, PREFIX, pool)?, - }) - } -} - -#[inline] -fn encode_key(key: &AsRef<[u8]>) -> String { - percent_encode(key.as_ref(), DEFAULT_ENCODE_SET).to_string() -} - -impl Bookmarks for FileBookmarks { - #[inline] - fn get(&self, name: &AsRef<[u8]>) -> BoxFuture, Error> { - self.kv - .get(encode_key(name)) - .map_err(|e| e.context("FileBookmarks get failed").into()) - .boxify() - } - - fn keys(&self) -> BoxStream, Error> { - self.kv - .keys() - .and_then(|name| Ok(percent_decode(&name[..].as_bytes()).collect())) - .map_err(|e| e.context("FileBookmarks keys failed").into()) - .boxify() - } -} - -impl BookmarksMut for FileBookmarks { - #[inline] - fn set( - &self, - key: &AsRef<[u8]>, - value: &HgChangesetId, - version: &Version, - ) -> BoxFuture, Error> { - self.kv - .set(encode_key(key), value, version, None) - .map_err(|e| e.context("FileBookmarks set failed").into()) - .boxify() - } - - #[inline] - fn delete(&self, key: &AsRef<[u8]>, version: &Version) -> BoxFuture, Error> { - self.kv - .delete(encode_key(key), version) - .map_err(|e| e.context("FileBookmarks delete failed").into()) - .boxify() - } -} diff --git a/bookmarks_old/membookmarks/src/lib.rs b/bookmarks_old/membookmarks/src/lib.rs deleted file mode 100644 index 7f2703d3f2..0000000000 --- a/bookmarks_old/membookmarks/src/lib.rs +++ /dev/null @@ -1,110 +0,0 @@ -// Copyright (c) 2004-present, Facebook, Inc. -// All Rights Reserved. -// -// This software may be used and distributed according to the terms of the -// GNU General Public License version 2 or any later version. - -#![feature(never_type)] - -extern crate bookmarks; -extern crate failure; -extern crate futures; -extern crate futures_ext; -extern crate mercurial_types; -extern crate storage_types; - -use std::collections::HashMap; -use std::collections::hash_map::Entry; -use std::sync::Mutex; -use std::sync::atomic::{AtomicUsize, Ordering, ATOMIC_USIZE_INIT}; - -use failure::Error; -use futures::future::ok; -use futures::stream::iter_ok; - -use bookmarks::{Bookmarks, BookmarksMut}; -use futures_ext::{BoxFuture, BoxStream, FutureExt, StreamExt}; -use mercurial_types::nodehash::HgChangesetId; -use storage_types::Version; - -static VERSION_COUNTER: AtomicUsize = ATOMIC_USIZE_INIT; - -fn version_next() -> Version { - Version::from(VERSION_COUNTER.fetch_add(1, Ordering::Relaxed) as u64) -} - -/// In-memory bookmark store backed by a HashMap, intended to be used in tests. -pub struct MemBookmarks { - bookmarks: Mutex, (HgChangesetId, Version)>>, -} - -impl MemBookmarks { - pub fn new() -> Self { - MemBookmarks { - bookmarks: Mutex::new(HashMap::new()), - } - } -} - -impl Bookmarks for MemBookmarks { - fn get(&self, key: &AsRef<[u8]>) -> BoxFuture, Error> { - ok(self.bookmarks - .lock() - .unwrap() - .get(key.as_ref()) - .map(Clone::clone)) - .boxify() - } - - fn keys(&self) -> BoxStream, Error> { - let guard = self.bookmarks.lock().unwrap(); - let keys = guard.keys().map(|k| k.clone()).collect::>(); - iter_ok(keys.into_iter()).boxify() - } -} - -impl BookmarksMut for MemBookmarks { - fn set( - &self, - key: &AsRef<[u8]>, - value: &HgChangesetId, - version: &Version, - ) -> BoxFuture, Error> { - let mut bookmarks = self.bookmarks.lock().unwrap(); - - match bookmarks.entry(key.as_ref().to_vec()) { - Entry::Occupied(mut entry) => if *version == entry.get().1 { - let new = version_next(); - entry.insert((value.clone(), new)); - ok(Some(new)) - } else { - ok(None) - }, - Entry::Vacant(entry) => if *version == Version::absent() { - let new = version_next(); - entry.insert((value.clone(), new)); - ok(Some(new)) - } else { - ok(None) - }, - }.boxify() - } - - fn delete(&self, key: &AsRef<[u8]>, version: &Version) -> BoxFuture, Error> { - let mut bookmarks = self.bookmarks.lock().unwrap(); - - match bookmarks.entry(key.as_ref().to_vec()) { - Entry::Occupied(entry) => if *version == entry.get().1 { - entry.remove(); - ok(Some(Version::absent())) - } else { - ok(None) - }, - Entry::Vacant(_) => if *version == Version::absent() { - ok(Some(Version::absent())) - } else { - ok(None) - }, - }.boxify() - } -} diff --git a/bookmarks_old/test/main.rs b/bookmarks_old/test/main.rs deleted file mode 100644 index e14045da04..0000000000 --- a/bookmarks_old/test/main.rs +++ /dev/null @@ -1,198 +0,0 @@ -// Copyright (c) 2017-present, Facebook, Inc. -// All Rights Reserved. -// -// This software may be used and distributed according to the terms of the -// GNU General Public License version 2 or any later version. - -//! Tests run against all bookmarks implementations. - -#![deny(warnings)] - -extern crate futures; -extern crate tempdir; -extern crate tokio_core; - -extern crate bookmarks; -extern crate db; -extern crate dbbookmarks; -extern crate filebookmarks; -extern crate membookmarks; -extern crate mercurial_types; -extern crate mercurial_types_mocks; -extern crate storage_types; - -use std::cell::RefCell; -use std::rc::Rc; - -use futures::Stream; -use tempdir::TempDir; -use tokio_core::reactor::Core; - -use bookmarks::BookmarksMut; -use dbbookmarks::DbBookmarks; -use filebookmarks::FileBookmarks; -use membookmarks::MemBookmarks; -use mercurial_types::nodehash::HgChangesetId; -use mercurial_types_mocks::nodehash; -use storage_types::Version; - -fn basic(bookmarks: B, core: &mut Core) -where - B: BookmarksMut, -{ - let foo = b"foo"; - let one = HgChangesetId::new(nodehash::ONES_HASH); - let two = HgChangesetId::new(nodehash::TWOS_HASH); - let three = HgChangesetId::new(nodehash::THREES_HASH); - - assert_eq!(core.run(bookmarks.get(&foo)).unwrap(), None); - - let absent = Version::absent(); - let foo_v1 = core.run(bookmarks.set(&foo, &one, &absent)) - .unwrap() - .unwrap(); - assert_eq!( - core.run(bookmarks.get(&foo)).unwrap(), - Some((one.clone(), foo_v1)) - ); - - let foo_v2 = core.run(bookmarks.set(&foo, &two, &foo_v1)) - .unwrap() - .unwrap(); - - // Should fail due to version mismatch. - assert_eq!( - core.run(bookmarks.set(&foo, &three, &foo_v1)).unwrap(), - None - ); - - assert_eq!( - core.run(bookmarks.delete(&foo, &foo_v2)).unwrap().unwrap(), - absent - ); - assert_eq!(core.run(bookmarks.get(&foo)).unwrap(), None); - - // Even though bookmark doesn't exist, this should fail with a version mismatch. - assert_eq!(core.run(bookmarks.delete(&foo, &foo_v2)).unwrap(), None); - - // Deleting it with the absent version should work. - assert_eq!( - core.run(bookmarks.delete(&foo, &absent)).unwrap().unwrap(), - absent - ); -} - -fn list(bookmarks: B, core: &mut Core) -where - B: BookmarksMut, -{ - let one = b"1"; - let two = b"2"; - let three = b"3"; - let hash = HgChangesetId::new(nodehash::ONES_HASH); - - let _ = core.run(bookmarks.create(&one, &hash)).unwrap().unwrap(); - let _ = core.run(bookmarks.create(&two, &hash)).unwrap().unwrap(); - let _ = core.run(bookmarks.create(&three, &hash)).unwrap().unwrap(); - - let mut result = core.run(bookmarks.keys().collect()).unwrap(); - result.sort(); - - let expected = vec![one, two, three]; - assert_eq!(result, expected); -} - -fn persistence(mut new_bookmarks: F, core: Rc>) -where - F: FnMut() -> B, - B: BookmarksMut, -{ - let foo = b"foo"; - let bar = HgChangesetId::new(nodehash::ONES_HASH); - - let version = { - let bookmarks = new_bookmarks(); - core.borrow_mut() - .run(bookmarks.create(&foo, &bar)) - .unwrap() - .unwrap() - }; - - let bookmarks = new_bookmarks(); - assert_eq!( - core.borrow_mut().run(bookmarks.get(&foo)).unwrap(), - Some((bar, version)) - ); -} - -macro_rules! bookmarks_test_impl { - ($mod_name: ident => { - state: $state: expr, - new: $new_cb: expr, - persistent: $persistent: expr, - }) => { - mod $mod_name { - use super::*; - - #[test] - fn test_basic() { - let mut core = Core::new().unwrap(); - let state = $state; - let bookmarks = $new_cb(&state, &mut core); - basic(bookmarks, &mut core); - } - - #[test] - fn test_list() { - let mut core = Core::new().unwrap(); - let state = $state; - let bookmarks = $new_cb(&state, &mut core); - list(bookmarks, &mut core); - } - - #[test] - fn test_persistence() { - // Not all bookmark implementations support persistence. - if $persistent { - let core = Rc::new(RefCell::new(Core::new().unwrap())); - let state = $state; - let new_bookmarks = { - let core = Rc::clone(&core); - move || { - $new_cb(&state, &mut *core.borrow_mut()) - } - }; - persistence(new_bookmarks, core); - } - } - } - } -} - -bookmarks_test_impl! { - membookmarks_test => { - state: (), - new: |_, _| MemBookmarks::new(), - persistent: false, - } -} - -bookmarks_test_impl! { - filebookmarks_test => { - state: TempDir::new("filebookmarks_test").unwrap(), - new: |dir: &TempDir, _| FileBookmarks::open(dir.as_ref()).unwrap(), - persistent: true, - } -} - -bookmarks_test_impl! { - dbbookmarks_test => { - state: dbbookmarks::init_test_db(), - new: |params: &db::ConnectionParams, core: &mut Core| { - let params = params.clone(); - let remote = core.remote(); - core.run(DbBookmarks::new_async(params, &remote)).unwrap() - }, - persistent: true, - } -}