mononoke: remove most of bookmarks_old

Summary: We only need stockbookmarks now

Reviewed By: farnz

Differential Revision: D7382258

fbshipit-source-id: d8f5f5fa1424d509c9e0364529f8aeb9b4627585
This commit is contained in:
Jeremy Fitzhardinge 2018-03-23 13:18:31 -07:00 committed by Facebook Github Bot
parent e108e9b258
commit 2c8cd78c6e
4 changed files with 0 additions and 670 deletions

View File

@ -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<Pool>,
}
impl DbBookmarks {
pub fn new_async(params: ConnectionParams, remote: &Remote) -> BoxFuture<Self, Error> {
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<Option<(HgChangesetId, Version)>, 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<Vec<u8>, 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<Option<Version>, 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<Option<Version>, 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<Pool>) -> BoxFutureNonSend<BoxStream<Vec<u8>, Error>, Error> {
pool.get_conn()
.and_then(|conn| conn.query("SELECT name FROM bookmarks"))
.and_then(|res| res.collect::<(Vec<u8>,)>())
.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<Pool>,
key: Vec<u8>,
) -> BoxFutureNonSend<Option<(HgChangesetId, Version)>, 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<Pool>,
key: Vec<u8>,
value: HgChangesetId,
version: Version,
) -> BoxFutureNonSend<Option<Version>, 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<Pool>,
key: Vec<u8>,
version: Version,
) -> BoxFutureNonSend<Option<Version>, 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::<Row>())
.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
}

View File

@ -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<HgChangesetId>,
}
impl FileBookmarks {
#[inline]
pub fn open<P: Into<PathBuf>>(path: P) -> Result<Self> {
Ok(FileBookmarks {
kv: FileKV::open(path, PREFIX)?,
})
}
#[inline]
pub fn open_with_pool<P: Into<PathBuf>>(path: P, pool: Arc<CpuPool>) -> Result<Self> {
Ok(FileBookmarks {
kv: FileKV::open_with_pool(path, PREFIX, pool)?,
})
}
#[inline]
pub fn create<P: Into<PathBuf>>(path: P) -> Result<Self> {
Ok(FileBookmarks {
kv: FileKV::create(path, PREFIX)?,
})
}
#[inline]
pub fn create_with_pool<P: Into<PathBuf>>(path: P, pool: Arc<CpuPool>) -> Result<Self> {
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<Option<(HgChangesetId, Version)>, Error> {
self.kv
.get(encode_key(name))
.map_err(|e| e.context("FileBookmarks get failed").into())
.boxify()
}
fn keys(&self) -> BoxStream<Vec<u8>, 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<Option<Version>, 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<Option<Version>, Error> {
self.kv
.delete(encode_key(key), version)
.map_err(|e| e.context("FileBookmarks delete failed").into())
.boxify()
}
}

View File

@ -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<HashMap<Vec<u8>, (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<Option<(HgChangesetId, Version)>, Error> {
ok(self.bookmarks
.lock()
.unwrap()
.get(key.as_ref())
.map(Clone::clone))
.boxify()
}
fn keys(&self) -> BoxStream<Vec<u8>, Error> {
let guard = self.bookmarks.lock().unwrap();
let keys = guard.keys().map(|k| k.clone()).collect::<Vec<_>>();
iter_ok(keys.into_iter()).boxify()
}
}
impl BookmarksMut for MemBookmarks {
fn set(
&self,
key: &AsRef<[u8]>,
value: &HgChangesetId,
version: &Version,
) -> BoxFuture<Option<Version>, 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<Option<Version>, 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()
}
}

View File

@ -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<B>(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<B>(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<F, B>(mut new_bookmarks: F, core: Rc<RefCell<Core>>)
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,
}
}