Static store manager

This commit is contained in:
Emily Toop 2018-05-22 11:10:35 +01:00
parent 10eba4fdbf
commit 9d8c666052

View file

@ -20,6 +20,7 @@ use std::path::{
use std::sync::{ use std::sync::{
Arc, Arc,
RwLock,
}; };
use rusqlite; use rusqlite;
@ -37,7 +38,6 @@ use mentat_db::{
TxObserver, TxObserver,
TxReport, TxReport,
}; };
use mentat_db::db;
use mentat_tolstoy::Syncer; use mentat_tolstoy::Syncer;
@ -63,6 +63,14 @@ use query::{
QueryOutput, QueryOutput,
}; };
/// A process is only permitted to have one open handle to each database. This manager
/// exists to enforce that constraint: don't open databases directly.
lazy_static! {
static ref STORES: Arc<RwLock<Stores>> = {
Arc::new(RwLock::new(Stores::new()))
};
}
pub struct Stores { pub struct Stores {
stores: BTreeMap<String, Store>, stores: BTreeMap<String, Store>,
} }
@ -73,39 +81,43 @@ impl Stores {
stores: Default::default(), stores: Default::default(),
} }
} }
pub fn singleton() -> &'static RwLock<Stores> {
&*STORES
}
} }
impl Stores { impl Stores {
fn is_open(&self, path: &str) -> bool { fn is_open(path: &str) -> bool {
self.stores.contains_key(path) Stores::singleton().read().unwrap().stores.contains_key(path)
} }
pub fn open(&mut self, path: &str) -> Result<&mut Store> { pub fn open(path: &str) -> Result<&mut Store> {
let p = path.to_string(); let p = path.to_string();
Ok(self.stores.entry(p).or_insert(Store::open(path)?)) Ok(Stores::singleton().write().unwrap().stores.entry(p).or_insert(Store::open(path)?))
} }
pub fn get(&self, path: &str) -> Result<Option<&Store>> { pub fn get(path: &str) -> Result<Option<&Store>> {
Ok(self.stores.get(path)) Ok(Stores::singleton().read().unwrap().stores.get(path))
} }
pub fn get_mut(&mut self, path: &str) -> Result<Option<&mut Store>> { pub fn get_mut(path: &str) -> Result<Option<&mut Store>> {
Ok(self.stores.get_mut(path)) Ok(Stores::singleton().read().unwrap().stores.get_mut(path))
} }
pub fn connect(&mut self, path: &str) -> Result<Store> { pub fn connect(path: &str) -> Result<Store> {
let store = self.stores.get_mut(path).ok_or(ErrorKind::StoreNotFound(path.to_string()))?; let store = Stores::singleton().read().unwrap().stores.get_mut(path).ok_or(ErrorKind::StoreNotFound(path.to_string()))?;
let connection = ::new_connection(path)?; let connection = ::new_connection(path)?;
store.fork(connection) store.fork(connection)
} }
fn open_connections_for_store(&self, path: &str) -> Result<usize> { fn open_connections_for_store(path: &str) -> Result<usize> {
Ok(Arc::strong_count(self.stores.get(path).ok_or(ErrorKind::StoreNotFound(path.to_string()))?.conn())) Ok(Arc::strong_count(Stores::singleton().read().unwrap().stores.get(path).ok_or(ErrorKind::StoreNotFound(path.to_string()))?.conn()))
} }
pub fn close(&mut self, path: &str) -> Result<()> { pub fn close(path: &str) -> Result<()> {
if self.open_connections_for_store(path)? <= 1 { if Stores::open_connections_for_store(path)? <= 1 {
self.stores.remove(path).ok_or(ErrorKind::StoreNotFound(path.to_string()))?; Stores::singleton().write().unwrap().stores.remove(path).ok_or(ErrorKind::StoreNotFound(path.to_string()))?;
Ok(()) Ok(())
} else { } else {
bail!(ErrorKind::StoreConnectionStillActive(path.to_string())) bail!(ErrorKind::StoreConnectionStillActive(path.to_string()))
@ -714,50 +726,45 @@ mod tests {
#[test] #[test]
fn test_stores_open_new_store() { fn test_stores_open_new_store() {
let mut manager = Stores::new(); let store = Stores::open("test.db").expect("Expected a store to be opened");
let store = manager.open("test.db").expect("Expected a store to be opened");
assert_eq!(1, Arc::strong_count(store.conn())); assert_eq!(1, Arc::strong_count(store.conn()));
} }
#[test] #[test]
fn test_stores_open_new_in_memory_store() { fn test_stores_open_new_in_memory_store() {
let mut manager = Stores::new();
let path = ""; let path = "";
let store = manager.open(path).expect("Expected a store to be opened"); let store = Stores::open(path).expect("Expected a store to be opened");
assert_eq!(1, Arc::strong_count(store.conn())); assert_eq!(1, Arc::strong_count(store.conn()));
} }
#[test] #[test]
fn test_stores_open_existing_store() { fn test_stores_open_existing_store() {
let mut manager = Stores::new();
{ {
let store1 = manager.open("").expect("Expected a store to be opened"); let store1 = Stores::open("").expect("Expected a store to be opened");
assert_eq!(1, Arc::strong_count(store1.conn())); assert_eq!(1, Arc::strong_count(store1.conn()));
} }
{ {
let store2 = manager.open("").expect("Expected a store to be opened"); let store2 = Stores::open("").expect("Expected a store to be opened");
assert_eq!(1, Arc::strong_count(store2.conn())); assert_eq!(1, Arc::strong_count(store2.conn()));
} }
} }
#[test] #[test]
fn test_stores_get_open_store() { fn test_stores_get_open_store() {
let mut manager = Stores::new();
let path = ""; let path = "";
{ {
let store = manager.open(path).expect("Expected a store to be opened"); let store = Stores::open(path).expect("Expected a store to be opened");
assert_eq!(1, Arc::strong_count(store.conn())); assert_eq!(1, Arc::strong_count(store.conn()));
} }
{ {
let store_ref = manager.get(path).expect("Expected a store to be fetched").unwrap(); let store_ref = Stores::get(path).expect("Expected a store to be fetched").unwrap();
assert_eq!(1, Arc::strong_count(store_ref.conn())); assert_eq!(1, Arc::strong_count(store_ref.conn()));
} }
} }
#[test] #[test]
fn test_stores_get_closed_store() { fn test_stores_get_closed_store() {
let manager = Stores::new(); match Stores::get("").expect("Expected a store to be fetched") {
match manager.get("").expect("Expected a store to be fetched") {
None => (), None => (),
Some(_) => panic!("Store is not open and so none should be returned"), Some(_) => panic!("Store is not open and so none should be returned"),
} }
@ -765,22 +772,20 @@ mod tests {
#[test] #[test]
fn test_stores_get_mut_open_store() { fn test_stores_get_mut_open_store() {
let mut manager = Stores::new();
let path = ""; let path = "";
{ {
let store = manager.open(path).expect("Expected a store to be opened"); let store = Stores::open(path).expect("Expected a store to be opened");
assert_eq!(1, Arc::strong_count(store.conn())); assert_eq!(1, Arc::strong_count(store.conn()));
} }
{ {
let store_ref = manager.get_mut(path).expect("Expected a store to be fetched").unwrap(); let store_ref = Stores::get_mut(path).expect("Expected a store to be fetched").unwrap();
assert_eq!(1, Arc::strong_count(store_ref.conn())); assert_eq!(1, Arc::strong_count(store_ref.conn()));
} }
} }
#[test] #[test]
fn test_stores_get_mut_closed_store() { fn test_stores_get_mut_closed_store() {
let mut manager = Stores::new(); match Stores::get_mut("").expect("Expected a store to be fetched") {
match manager.get_mut("").expect("Expected a store to be fetched") {
None => (), None => (),
Some(_) => panic!("Store is not open and so none should be returned"), Some(_) => panic!("Store is not open and so none should be returned"),
} }
@ -788,26 +793,25 @@ mod tests {
#[test] #[test]
fn test_stores_connect_open_store() { fn test_stores_connect_open_store() {
let mut manager = Stores::new();
let path = ""; let path = "";
{ {
let store1 = manager.open(path).expect("Expected a store to be opened"); let store1 = Stores::open(path).expect("Expected a store to be opened");
assert_eq!(1, Arc::strong_count(store1.conn())); assert_eq!(1, Arc::strong_count(store1.conn()));
} }
// forking an open store leads to a ref count of 2 on the shared conn. // forking an open store leads to a ref count of 2 on the shared conn.
let store2 = manager.connect(path).expect("expected a new store"); let store2 = Stores::connect(path).expect("expected a new store");
assert_eq!(2, Arc::strong_count(store2.conn())); assert_eq!(2, Arc::strong_count(store2.conn()));
{ {
// fetching a reference to the original store also has a ref count of 2 on the shared conn // fetching a reference to the original store also has a ref count of 2 on the shared conn
let store3 = manager.get(path).expect("Expected a store to be fetched").unwrap(); let store3 = Stores::get(path).expect("Expected a store to be fetched").unwrap();
assert_eq!(2, Arc::strong_count(store3.conn())); assert_eq!(2, Arc::strong_count(store3.conn()));
} }
{ {
// forking again, in it's own scope increases the refcount. // forking again, in it's own scope increases the refcount.
let store4 = manager.connect(path).expect("expected a new store"); let store4 = Stores::connect(path).expect("expected a new store");
assert_eq!(3, Arc::strong_count(store2.conn())); assert_eq!(3, Arc::strong_count(store2.conn()));
assert_eq!(3, Arc::strong_count(store4.conn())); assert_eq!(3, Arc::strong_count(store4.conn()));
} }
@ -819,9 +823,8 @@ mod tests {
#[test] #[test]
fn test_stores_connect_closed_store() { fn test_stores_connect_closed_store() {
let mut manager = Stores::new();
let path = ""; let path = "";
let err = manager.connect(path).err(); let err = Stores::connect(path).err();
match err.unwrap() { match err.unwrap() {
Error(ErrorKind::StoreNotFound(message), _) => { assert_eq!(path, message); }, Error(ErrorKind::StoreNotFound(message), _) => { assert_eq!(path, message); },
x => panic!("expected Store Not Found error, got {:?}", x), x => panic!("expected Store Not Found error, got {:?}", x),
@ -830,32 +833,30 @@ mod tests {
#[test] #[test]
fn test_stores_close_store_with_one_reference() { fn test_stores_close_store_with_one_reference() {
let mut manager = Stores::new();
let path = ""; let path = "";
{ {
let store = manager.open(path).expect("Expected a store to be opened"); let store = Stores::open(path).expect("Expected a store to be opened");
assert_eq!(1, Arc::strong_count(store.conn())); assert_eq!(1, Arc::strong_count(store.conn()));
} }
assert!(manager.close(path).is_ok()); assert!(Stores::close(path).is_ok());
assert!(manager.get(path).expect("expected an empty result").is_none()) assert!(Stores::get(path).expect("expected an empty result").is_none())
} }
#[test] #[test]
fn test_stores_close_store_with_multiple_references() { fn test_stores_close_store_with_multiple_references() {
let mut manager = Stores::new();
let path = ""; let path = "";
{ {
let store1 = manager.open(path).expect("Expected a store to be opened"); let store1 = Stores::open(path).expect("Expected a store to be opened");
assert_eq!(1, Arc::strong_count(store1.conn())); assert_eq!(1, Arc::strong_count(store1.conn()));
} }
// forking an open store leads to a ref count of 2 on the shared conn. // forking an open store leads to a ref count of 2 on the shared conn.
let store2 = manager.connect(path).expect("expected a new store"); let store2 = Stores::connect(path).expect("expected a new store");
assert_eq!(2, Arc::strong_count(store2.conn())); assert_eq!(2, Arc::strong_count(store2.conn()));
let err = manager.close(path).err(); let err = Stores::close(path).err();
match err.unwrap() { match err.unwrap() {
Error(ErrorKind::StoreConnectionStillActive(message), _) => { assert_eq!(path, message); }, Error(ErrorKind::StoreConnectionStillActive(message), _) => { assert_eq!(path, message); },
x => panic!("expected StoreConnectionStillActive error, got {:?}", x), x => panic!("expected StoreConnectionStillActive error, got {:?}", x),
@ -864,19 +865,18 @@ mod tests {
#[test] #[test]
fn test_stores_close_store_with_scoped_multiple_references() { fn test_stores_close_store_with_scoped_multiple_references() {
let mut manager = Stores::new();
let path = ""; let path = "";
{ {
let store1 = manager.open(path).expect("Expected a store to be opened"); let store1 = Stores::open(path).expect("Expected a store to be opened");
assert_eq!(1, Arc::strong_count(store1.conn())); assert_eq!(1, Arc::strong_count(store1.conn()));
} }
{ {
// forking an open store leads to a ref count of 2 on the shared conn. // forking an open store leads to a ref count of 2 on the shared conn.
let store2 = manager.connect(path).expect("expected a new store"); let store2 = Stores::connect(path).expect("expected a new store");
assert_eq!(2, Arc::strong_count(store2.conn())); assert_eq!(2, Arc::strong_count(store2.conn()));
let err = manager.close(path).err(); let err = Stores::close(path).err();
match err.unwrap() { match err.unwrap() {
Error(ErrorKind::StoreConnectionStillActive(message), _) => { assert_eq!(path, message); }, Error(ErrorKind::StoreConnectionStillActive(message), _) => { assert_eq!(path, message); },
x => panic!("expected StoreConnectionStillActive error, got {:?}", x), x => panic!("expected StoreConnectionStillActive error, got {:?}", x),
@ -884,16 +884,15 @@ mod tests {
} }
// outside of the scope, there should only be one strong reference so we can close the connection // outside of the scope, there should only be one strong reference so we can close the connection
assert!(manager.close(path).is_ok()); assert!(Stores::close(path).is_ok());
assert!(manager.get(path).expect("expected an empty result").is_none()) assert!(Stores::get(path).expect("expected an empty result").is_none())
} }
#[test] #[test]
fn test_stores_close_unopened_store() { fn test_stores_close_unopened_store() {
let mut manager = Stores::new();
let path = ""; let path = "";
let err = manager.close(path).err(); let err = Stores::close(path).err();
match err.unwrap() { match err.unwrap() {
Error(ErrorKind::StoreNotFound(message), _) => { assert_eq!(path, message); }, Error(ErrorKind::StoreNotFound(message), _) => { assert_eq!(path, message); },
x => panic!("expected StoreNotFound error, got {:?}", x), x => panic!("expected StoreNotFound error, got {:?}", x),
@ -902,11 +901,10 @@ mod tests {
#[test] #[test]
fn test_stores_connect_perform_mutable_operations() { fn test_stores_connect_perform_mutable_operations() {
let mut manager = Stores::new();
let path = "test.db"; let path = "test.db";
{ {
let store1 = manager.open(path).expect("Expected a store to be opened"); let store1 = Stores::open(path).expect("Expected a store to be opened");
assert_eq!(1, Arc::strong_count(store1.conn())); assert_eq!(1, Arc::strong_count(store1.conn()));
let mut in_progress = store1.begin_transaction().expect("begun"); let mut in_progress = store1.begin_transaction().expect("begun");
in_progress.transact(r#"[ in_progress.transact(r#"[
@ -928,7 +926,7 @@ mod tests {
{ {
// forking an open store leads to a ref count of 2 on the shared conn. // forking an open store leads to a ref count of 2 on the shared conn.
// we should be able to perform write operations on this connection // we should be able to perform write operations on this connection
let mut store2 = manager.connect(path).expect("expected a new store"); let mut store2 = Stores::connect(path).expect("expected a new store");
assert_eq!(2, Arc::strong_count(store2.conn())); assert_eq!(2, Arc::strong_count(store2.conn()));
let mut in_progress = store2.begin_transaction().expect("begun"); let mut in_progress = store2.begin_transaction().expect("begun");
in_progress.transact(r#"[ in_progress.transact(r#"[