test work

This commit is contained in:
John Smith
2023-06-18 18:47:39 -04:00
parent b8e5039251
commit b6e055e47d
21 changed files with 664 additions and 171 deletions

View File

@@ -45,6 +45,7 @@ impl Drop for TableDBUnlockedInner {
#[derive(Debug, Clone)]
pub struct TableDB {
opened_column_count: u32,
unlocked_inner: Arc<TableDBUnlockedInner>,
}
@@ -56,11 +57,13 @@ impl TableDB {
database: Database,
encryption_key: Option<TypedSharedSecret>,
decryption_key: Option<TypedSharedSecret>,
opened_column_count: u32,
) -> Self {
let encrypt_info = encryption_key.map(|ek| CryptInfo::new(crypto.clone(), ek));
let decrypt_info = decryption_key.map(|dk| CryptInfo::new(crypto.clone(), dk));
Self {
opened_column_count,
unlocked_inner: Arc::new(TableDBUnlockedInner {
table,
table_store,
@@ -71,8 +74,12 @@ impl TableDB {
}
}
pub(super) fn try_new_from_weak_inner(weak_inner: Weak<TableDBUnlockedInner>) -> Option<Self> {
pub(super) fn try_new_from_weak_inner(
weak_inner: Weak<TableDBUnlockedInner>,
opened_column_count: u32,
) -> Option<Self> {
weak_inner.upgrade().map(|table_db_unlocked_inner| Self {
opened_column_count,
unlocked_inner: table_db_unlocked_inner,
})
}
@@ -82,6 +89,7 @@ impl TableDB {
}
/// Get the total number of columns in the TableDB
/// Not the number of columns that were opened, rather the total number that could be opened
pub fn get_column_count(&self) -> VeilidAPIResult<u32> {
let db = &self.unlocked_inner.database;
db.num_columns().map_err(VeilidAPIError::from)
@@ -144,8 +152,14 @@ impl TableDB {
}
}
/// Get the list of keys in a column of the TableDB
/// Get the list of keys in a column of the TableDAB
pub async fn get_keys(&self, col: u32) -> VeilidAPIResult<Vec<Vec<u8>>> {
if col >= self.opened_column_count {
apibail_generic!(format!(
"Column exceeds opened column count {} >= {}",
col, self.opened_column_count
));
}
let db = self.unlocked_inner.database.clone();
let mut out = Vec::new();
db.iter_keys(col, None, |k| {
@@ -165,6 +179,12 @@ impl TableDB {
/// Store a key with a value in a column in the TableDB. Performs a single transaction immediately.
pub async fn store(&self, col: u32, key: &[u8], value: &[u8]) -> VeilidAPIResult<()> {
if col >= self.opened_column_count {
apibail_generic!(format!(
"Column exceeds opened column count {} >= {}",
col, self.opened_column_count
));
}
let db = self.unlocked_inner.database.clone();
let mut dbt = db.transaction();
dbt.put(
@@ -195,6 +215,12 @@ impl TableDB {
/// Read a key from a column in the TableDB immediately.
pub async fn load(&self, col: u32, key: &[u8]) -> VeilidAPIResult<Option<Vec<u8>>> {
if col >= self.opened_column_count {
apibail_generic!(format!(
"Column exceeds opened column count {} >= {}",
col, self.opened_column_count
));
}
let db = self.unlocked_inner.database.clone();
let key = self.maybe_encrypt(key, true);
Ok(db
@@ -233,6 +259,12 @@ impl TableDB {
/// Delete key with from a column in the TableDB
pub async fn delete(&self, col: u32, key: &[u8]) -> VeilidAPIResult<Option<Vec<u8>>> {
if col >= self.opened_column_count {
apibail_generic!(format!(
"Column exceeds opened column count {} >= {}",
col, self.opened_column_count
));
}
let key = self.maybe_encrypt(key, true);
let db = self.unlocked_inner.database.clone();
@@ -330,11 +362,19 @@ impl TableDBTransaction {
}
/// Store a key with a value in a column in the TableDB
pub fn store(&self, col: u32, key: &[u8], value: &[u8]) {
pub fn store(&self, col: u32, key: &[u8], value: &[u8]) -> VeilidAPIResult<()> {
if col >= self.db.opened_column_count {
apibail_generic!(format!(
"Column exceeds opened column count {} >= {}",
col, self.db.opened_column_count
));
}
let key = self.db.maybe_encrypt(key, true);
let value = self.db.maybe_encrypt(value, false);
let mut inner = self.inner.lock();
inner.dbt.as_mut().unwrap().put_owned(col, key, value);
Ok(())
}
/// Store a key in rkyv format with a value in a column in the TableDB
@@ -343,12 +383,7 @@ impl TableDBTransaction {
T: RkyvSerialize<DefaultVeilidRkyvSerializer>,
{
let value = to_rkyv(value)?;
let key = self.db.maybe_encrypt(key, true);
let value = self.db.maybe_encrypt(&value, false);
let mut inner = self.inner.lock();
inner.dbt.as_mut().unwrap().put_owned(col, key, value);
Ok(())
self.store(col, key, &value)
}
/// Store a key in rkyv format with a value in a column in the TableDB
@@ -357,19 +392,22 @@ impl TableDBTransaction {
T: serde::Serialize,
{
let value = serde_json::to_vec(value).map_err(VeilidAPIError::internal)?;
let key = self.db.maybe_encrypt(key, true);
let value = self.db.maybe_encrypt(&value, false);
let mut inner = self.inner.lock();
inner.dbt.as_mut().unwrap().put_owned(col, key, value);
Ok(())
self.store(col, key, &value)
}
/// Delete key with from a column in the TableDB
pub fn delete(&self, col: u32, key: &[u8]) {
pub fn delete(&self, col: u32, key: &[u8]) -> VeilidAPIResult<()> {
if col >= self.db.opened_column_count {
apibail_generic!(format!(
"Column exceeds opened column count {} >= {}",
col, self.db.opened_column_count
));
}
let key = self.db.maybe_encrypt(key, true);
let mut inner = self.inner.lock();
inner.dbt.as_mut().unwrap().delete_owned(col, key);
Ok(())
}
}

View File

@@ -428,6 +428,7 @@ impl TableStore {
}
pub(crate) fn on_table_db_drop(&self, table: String) {
log_rtab!(debug "dropping table db: {}", table);
let mut inner = self.inner.lock();
if inner.opened.remove(&table).is_none() {
unreachable!("should have removed an item");
@@ -449,12 +450,21 @@ impl TableStore {
let table_name = self.name_get_or_create(name).await?;
// See if this table is already opened
// See if this table is already opened, if so the column count must be the same
{
let mut inner = self.inner.lock();
if let Some(table_db_weak_inner) = inner.opened.get(&table_name) {
match TableDB::try_new_from_weak_inner(table_db_weak_inner.clone()) {
match TableDB::try_new_from_weak_inner(table_db_weak_inner.clone(), column_count) {
Some(tdb) => {
// Ensure column count isnt bigger
let existing_col_count = tdb.get_column_count()?;
if column_count > existing_col_count {
return Err(VeilidAPIError::generic(format!(
"database must be closed before increasing column count {} -> {}",
existing_col_count, column_count,
)));
}
return Ok(tdb);
}
None => {
@@ -465,7 +475,7 @@ impl TableStore {
}
// Open table db using platform-specific driver
let db = match self
let mut db = match self
.table_store_driver
.open(&table_name, column_count)
.await
@@ -481,6 +491,24 @@ impl TableStore {
// Flush table names to disk
self.flush().await;
// If more columns are available, open the low level db with the max column count but restrict the tabledb object to the number requested
let existing_col_count = db.num_columns().map_err(VeilidAPIError::from)?;
if existing_col_count > column_count {
drop(db);
db = match self
.table_store_driver
.open(&table_name, existing_col_count)
.await
{
Ok(db) => db,
Err(e) => {
self.name_delete(name).await.expect("cleanup failed");
self.flush().await;
return Err(e);
}
};
}
// Wrap low-level Database in TableDB object
let mut inner = self.inner.lock();
let table_db = TableDB::new(
@@ -490,6 +518,7 @@ impl TableStore {
db,
inner.encryption_key.clone(),
inner.encryption_key.clone(),
column_count,
);
// Keep track of opened DBs

View File

@@ -132,6 +132,41 @@ pub async fn test_store_delete_load(ts: TableStore) {
assert_eq!(db.load(2, b"baz").await.unwrap(), Some(b"QWERTY".to_vec()));
}
pub async fn test_transaction(ts: TableStore) {
trace!("test_transaction");
let _ = ts.delete("test");
let db = ts.open("test", 3).await.expect("should have opened");
assert!(
ts.delete("test").await.is_err(),
"should fail because file is opened"
);
let tx = db.transact();
assert!(tx.store(0, b"aaa", b"a-value").is_ok());
assert!(tx.store_json(1, b"bbb", &"b-value".to_owned()).is_ok());
assert!(tx.store_rkyv(2, b"ccc", &"c-value".to_owned()).is_ok());
assert!(tx.store(3, b"ddd", b"d-value").is_err());
assert!(tx.store(0, b"ddd", b"d-value").is_ok());
assert!(tx.delete(0, b"ddd").is_ok());
assert!(tx.commit().await.is_ok());
let tx = db.transact();
assert!(tx.delete(2, b"ccc").is_ok());
tx.rollback();
assert_eq!(db.load(0, b"aaa").await, Ok(Some(b"a-value".to_vec())));
assert_eq!(
db.load_json::<String>(1, b"bbb").await,
Ok(Some("b-value".to_owned()))
);
assert_eq!(
db.load_rkyv::<String>(2, b"ccc").await,
Ok(Some("c-value".to_owned()))
);
assert_eq!(db.load(0, b"ddd").await, Ok(None));
}
pub async fn test_rkyv(vcrypto: CryptoSystemVersion, ts: TableStore) {
trace!("test_rkyv");
@@ -268,6 +303,7 @@ pub async fn test_all() {
test_protect_unprotect(vcrypto.clone(), ts.clone()).await;
test_delete_open_delete(ts.clone()).await;
test_store_delete_load(ts.clone()).await;
test_transaction(ts.clone()).await;
test_rkyv(vcrypto.clone(), ts.clone()).await;
test_json(vcrypto, ts.clone()).await;
let _ = ts.delete("test").await;