From fd09dfad878ffed56189f463d4cd5e57e3a9e0c3 Mon Sep 17 00:00:00 2001 From: "hinto.janai" Date: Wed, 12 Jun 2024 19:31:49 -0400 Subject: [PATCH] database: remove `Tables` references --- storage/database/src/backend/heed/env.rs | 17 - storage/database/src/backend/tests.rs | 1100 +++++++++++----------- storage/database/src/env.rs | 47 +- storage/database/src/tests.rs | 152 +-- 4 files changed, 650 insertions(+), 666 deletions(-) diff --git a/storage/database/src/backend/heed/env.rs b/storage/database/src/backend/heed/env.rs index 703af4a..d4d0385 100644 --- a/storage/database/src/backend/heed/env.rs +++ b/storage/database/src/backend/heed/env.rs @@ -21,7 +21,6 @@ use crate::{ error::{InitError, RuntimeError}, resize::ResizeAlgorithm, table::Table, - tables::call_fn_on_all_tables_or_early_return, }; //---------------------------------------------------------------------------------------------------- Consts @@ -211,22 +210,6 @@ impl Env for ConcreteEnv { Ok(()) } - let mut tx_rw = env.write_txn()?; - // Create all tables. - // FIXME: this macro is kinda awkward. - { - let env = &env; - let tx_rw = &mut tx_rw; - match call_fn_on_all_tables_or_early_return!(create_table(env, tx_rw)) { - Ok(_) => (), - Err(e) => return Err(e), - } - } - - // INVARIANT: this should never return `ResizeNeeded` due to adding - // some tables since we added some leeway to the memory map above. - tx_rw.commit()?; - Ok(Self { env: RwLock::new(env), config, diff --git a/storage/database/src/backend/tests.rs b/storage/database/src/backend/tests.rs index 3daec66..8034271 100644 --- a/storage/database/src/backend/tests.rs +++ b/storage/database/src/backend/tests.rs @@ -1,550 +1,550 @@ -//! Tests for `cuprate_blockchain`'s backends. -//! -//! These tests are fully trait-based, meaning there -//! is no reference to `backend/`-specific types. -//! -//! As such, which backend is tested is -//! dependant on the feature flags used. -//! -//! | Feature flag | Tested backend | -//! |---------------|----------------| -//! | Only `redb` | `redb` -//! | Anything else | `heed` -//! -//! `redb`, and it only must be enabled for it to be tested. - -//---------------------------------------------------------------------------------------------------- Import - -use crate::{ - database::{DatabaseIter, DatabaseRo, DatabaseRw}, - env::{Env, EnvInner}, - error::RuntimeError, - resize::ResizeAlgorithm, - storable::StorableVec, - tables::{ - BlockBlobs, BlockHeights, BlockInfos, KeyImages, NumOutputs, Outputs, PrunableHashes, - PrunableTxBlobs, PrunedTxBlobs, RctOutputs, TxBlobs, TxHeights, TxIds, TxOutputs, - TxUnlockTime, - }, - tables::{TablesIter, TablesMut}, - tests::tmp_concrete_env, - transaction::{TxRo, TxRw}, - types::{ - Amount, AmountIndex, AmountIndices, BlockBlob, BlockHash, BlockHeight, BlockInfo, KeyImage, - Output, OutputFlags, PreRctOutputId, PrunableBlob, PrunableHash, PrunedBlob, RctOutput, - TxBlob, TxHash, TxId, UnlockTime, - }, - ConcreteEnv, -}; - -//---------------------------------------------------------------------------------------------------- Tests -/// Simply call [`Env::open`]. If this fails, something is really wrong. -#[test] -fn open() { - tmp_concrete_env(); -} - -/// Create database transactions, but don't write any data. -#[test] -fn tx() { - let (env, _tempdir) = tmp_concrete_env(); - let env_inner = env.env_inner(); - - TxRo::commit(env_inner.tx_ro().unwrap()).unwrap(); - TxRw::commit(env_inner.tx_rw().unwrap()).unwrap(); - TxRw::abort(env_inner.tx_rw().unwrap()).unwrap(); -} - -/// Open (and verify) that all database tables -/// exist already after calling [`Env::open`]. -#[test] -fn open_db() { - let (env, _tempdir) = tmp_concrete_env(); - let env_inner = env.env_inner(); - let tx_ro = env_inner.tx_ro().unwrap(); - let tx_rw = env_inner.tx_rw().unwrap(); - - // Open all tables in read-only mode. - // This should be updated when tables are modified. - env_inner.open_db_ro::(&tx_ro).unwrap(); - env_inner.open_db_ro::(&tx_ro).unwrap(); - env_inner.open_db_ro::(&tx_ro).unwrap(); - env_inner.open_db_ro::(&tx_ro).unwrap(); - env_inner.open_db_ro::(&tx_ro).unwrap(); - env_inner.open_db_ro::(&tx_ro).unwrap(); - env_inner.open_db_ro::(&tx_ro).unwrap(); - env_inner.open_db_ro::(&tx_ro).unwrap(); - env_inner.open_db_ro::(&tx_ro).unwrap(); - env_inner.open_db_ro::(&tx_ro).unwrap(); - env_inner.open_db_ro::(&tx_ro).unwrap(); - env_inner.open_db_ro::(&tx_ro).unwrap(); - env_inner.open_db_ro::(&tx_ro).unwrap(); - env_inner.open_db_ro::(&tx_ro).unwrap(); - env_inner.open_db_ro::(&tx_ro).unwrap(); - TxRo::commit(tx_ro).unwrap(); - - // Open all tables in read/write mode. - env_inner.open_db_rw::(&tx_rw).unwrap(); - env_inner.open_db_rw::(&tx_rw).unwrap(); - env_inner.open_db_rw::(&tx_rw).unwrap(); - env_inner.open_db_rw::(&tx_rw).unwrap(); - env_inner.open_db_rw::(&tx_rw).unwrap(); - env_inner.open_db_rw::(&tx_rw).unwrap(); - env_inner.open_db_rw::(&tx_rw).unwrap(); - env_inner.open_db_rw::(&tx_rw).unwrap(); - env_inner.open_db_rw::(&tx_rw).unwrap(); - env_inner.open_db_rw::(&tx_rw).unwrap(); - env_inner.open_db_rw::(&tx_rw).unwrap(); - env_inner.open_db_rw::(&tx_rw).unwrap(); - env_inner.open_db_rw::(&tx_rw).unwrap(); - env_inner.open_db_rw::(&tx_rw).unwrap(); - env_inner.open_db_rw::(&tx_rw).unwrap(); - TxRw::commit(tx_rw).unwrap(); -} - -/// Test `Env` resizes. -#[test] -fn resize() { - // This test is only valid for `Env`'s that need to resize manually. - if !ConcreteEnv::MANUAL_RESIZE { - return; - } - - let (env, _tempdir) = tmp_concrete_env(); - - // Resize by the OS page size. - let page_size = crate::resize::page_size(); - let old_size = env.current_map_size(); - env.resize_map(Some(ResizeAlgorithm::FixedBytes(page_size))); - - // Assert it resized exactly by the OS page size. - let new_size = env.current_map_size(); - assert_eq!(new_size, old_size + page_size.get()); -} - -/// Test that `Env`'s that don't manually resize. -#[test] -#[should_panic = "unreachable"] -fn non_manual_resize_1() { - if ConcreteEnv::MANUAL_RESIZE { - unreachable!(); - } else { - let (env, _tempdir) = tmp_concrete_env(); - env.resize_map(None); - } -} - -#[test] -#[should_panic = "unreachable"] -fn non_manual_resize_2() { - if ConcreteEnv::MANUAL_RESIZE { - unreachable!(); - } else { - let (env, _tempdir) = tmp_concrete_env(); - env.current_map_size(); - } -} - -/// Test all `DatabaseR{o,w}` operations. -#[test] -fn db_read_write() { - let (env, _tempdir) = tmp_concrete_env(); - let env_inner = env.env_inner(); - let tx_rw = env_inner.tx_rw().unwrap(); - let mut table = env_inner.open_db_rw::(&tx_rw).unwrap(); - - /// The (1st) key. - const KEY: PreRctOutputId = PreRctOutputId { - amount: 1, - amount_index: 123, - }; - /// The expected value. - const VALUE: Output = Output { - key: [35; 32], - height: 45_761_798, - output_flags: OutputFlags::empty(), - tx_idx: 2_353_487, - }; - /// How many `(key, value)` pairs will be inserted. - const N: u64 = 100; - - /// Assert 2 `Output`'s are equal, and that accessing - /// their fields don't result in an unaligned panic. - fn assert_same(output: Output) { - assert_eq!(output, VALUE); - assert_eq!(output.key, VALUE.key); - assert_eq!(output.height, VALUE.height); - assert_eq!(output.output_flags, VALUE.output_flags); - assert_eq!(output.tx_idx, VALUE.tx_idx); - } - - assert!(table.is_empty().unwrap()); - - // Insert keys. - let mut key = KEY; - for _ in 0..N { - table.put(&key, &VALUE).unwrap(); - key.amount += 1; - } - - assert_eq!(table.len().unwrap(), N); - - // Assert the first/last `(key, value)`s are there. - { - assert!(table.contains(&KEY).unwrap()); - let get: Output = table.get(&KEY).unwrap(); - assert_same(get); - - let first: Output = table.first().unwrap().1; - assert_same(first); - - let last: Output = table.last().unwrap().1; - assert_same(last); - } - - // Commit transactions, create new ones. - drop(table); - TxRw::commit(tx_rw).unwrap(); - let tx_ro = env_inner.tx_ro().unwrap(); - let table_ro = env_inner.open_db_ro::(&tx_ro).unwrap(); - let tx_rw = env_inner.tx_rw().unwrap(); - let mut table = env_inner.open_db_rw::(&tx_rw).unwrap(); - - // Assert the whole range is there. - { - let range = table_ro.get_range(..).unwrap(); - let mut i = 0; - for result in range { - let value: Output = result.unwrap(); - assert_same(value); - - i += 1; - } - assert_eq!(i, N); - } - - // `get_range()` tests. - let mut key = KEY; - key.amount += N; - let range = KEY..key; - - // Assert count is correct. - assert_eq!( - N as usize, - table_ro.get_range(range.clone()).unwrap().count() - ); - - // Assert each returned value from the iterator is owned. - { - let mut iter = table_ro.get_range(range.clone()).unwrap(); - let value: Output = iter.next().unwrap().unwrap(); // 1. take value out - drop(iter); // 2. drop the `impl Iterator + 'a` - assert_same(value); // 3. assert even without the iterator, the value is alive - } - - // Assert each value is the same. - { - let mut iter = table_ro.get_range(range).unwrap(); - for _ in 0..N { - let value: Output = iter.next().unwrap().unwrap(); - assert_same(value); - } - } - - // Assert `update()` works. - { - const HEIGHT: u32 = 999; - - assert_ne!(table.get(&KEY).unwrap().height, HEIGHT); - - table - .update(&KEY, |mut value| { - value.height = HEIGHT; - Some(value) - }) - .unwrap(); - - assert_eq!(table.get(&KEY).unwrap().height, HEIGHT); - } - - // Assert deleting works. - { - table.delete(&KEY).unwrap(); - let value = table.get(&KEY); - assert!(!table.contains(&KEY).unwrap()); - assert!(matches!(value, Err(RuntimeError::KeyNotFound))); - // Assert the other `(key, value)` pairs are still there. - let mut key = KEY; - key.amount += N - 1; // we used inclusive `0..N` - let value = table.get(&key).unwrap(); - assert_same(value); - } - - // Assert `take()` works. - { - let mut key = KEY; - key.amount += 1; - let value = table.take(&key).unwrap(); - assert_eq!(value, VALUE); - - let get = table.get(&KEY); - assert!(!table.contains(&key).unwrap()); - assert!(matches!(get, Err(RuntimeError::KeyNotFound))); - - // Assert the other `(key, value)` pairs are still there. - key.amount += 1; - let value = table.get(&key).unwrap(); - assert_same(value); - } - - drop(table); - TxRw::commit(tx_rw).unwrap(); - - // Assert `clear_db()` works. - { - let mut tx_rw = env_inner.tx_rw().unwrap(); - env_inner.clear_db::(&mut tx_rw).unwrap(); - let table = env_inner.open_db_rw::(&tx_rw).unwrap(); - assert!(table.is_empty().unwrap()); - for n in 0..N { - let mut key = KEY; - key.amount += n; - let value = table.get(&key); - assert!(matches!(value, Err(RuntimeError::KeyNotFound))); - assert!(!table.contains(&key).unwrap()); - } - - // Reader still sees old value. - assert!(!table_ro.is_empty().unwrap()); - - // Writer sees updated value (nothing). - assert!(table.is_empty().unwrap()); - } -} - -/// Assert that `key`'s in database tables are sorted in -/// an ordered B-Tree fashion, i.e. `min_value -> max_value`. -#[test] -fn tables_are_sorted() { - let (env, _tmp) = tmp_concrete_env(); - let env_inner = env.env_inner(); - let tx_rw = env_inner.tx_rw().unwrap(); - let mut tables_mut = env_inner.open_tables_mut(&tx_rw).unwrap(); - - // Insert `{5, 4, 3, 2, 1, 0}`, assert each new - // number inserted is the minimum `first()` value. - for key in (0..6).rev() { - tables_mut.num_outputs_mut().put(&key, &123).unwrap(); - let (first, _) = tables_mut.num_outputs_mut().first().unwrap(); - assert_eq!(first, key); - } - - drop(tables_mut); - TxRw::commit(tx_rw).unwrap(); - let tx_rw = env_inner.tx_rw().unwrap(); - - // Assert iterators are ordered. - { - let tx_ro = env_inner.tx_ro().unwrap(); - let tables = env_inner.open_tables(&tx_ro).unwrap(); - let t = tables.num_outputs_iter(); - let iter = t.iter().unwrap(); - let keys = t.keys().unwrap(); - for ((i, iter), key) in (0..6).zip(iter).zip(keys) { - let (iter, _) = iter.unwrap(); - let key = key.unwrap(); - assert_eq!(i, iter); - assert_eq!(iter, key); - } - } - - let mut tables_mut = env_inner.open_tables_mut(&tx_rw).unwrap(); - let t = tables_mut.num_outputs_mut(); - - // Assert the `first()` values are the minimum, i.e. `{0, 1, 2}` - for key in 0..3 { - let (first, _) = t.first().unwrap(); - assert_eq!(first, key); - t.delete(&key).unwrap(); - } - - // Assert the `last()` values are the maximum, i.e. `{5, 4, 3}` - for key in (3..6).rev() { - let (last, _) = tables_mut.num_outputs_mut().last().unwrap(); - assert_eq!(last, key); - tables_mut.num_outputs_mut().delete(&key).unwrap(); - } -} - -//---------------------------------------------------------------------------------------------------- Table Tests -/// Test multiple tables and their key + values. -/// -/// Each one of these tests: -/// - Opens a specific table -/// - Essentially does the `db_read_write` test -macro_rules! test_tables { - ($( - $table:ident, // Table type - $key_type:ty => // Key (type) - $value_type:ty, // Value (type) - $key:expr => // Key (the value) - $value:expr, // Value (the value) - )* $(,)?) => { paste::paste! { $( - // Test function's name is the table type in `snake_case`. - #[test] - fn [<$table:snake>]() { - // Open the database env and table. - let (env, _tempdir) = tmp_concrete_env(); - let env_inner = env.env_inner(); - let mut tx_rw = env_inner.tx_rw().unwrap(); - let mut table = env_inner.open_db_rw::<$table>(&mut tx_rw).unwrap(); - - /// The expected key. - const KEY: $key_type = $key; - // The expected value. - let value: $value_type = $value; - - // Assert a passed value is equal to the const value. - let assert_eq = |v: &$value_type| { - assert_eq!(v, &value); - }; - - // Insert the key. - table.put(&KEY, &value).unwrap(); - // Assert key is there. - { - let value: $value_type = table.get(&KEY).unwrap(); - assert_eq(&value); - } - - assert!(table.contains(&KEY).unwrap()); - assert_eq!(table.len().unwrap(), 1); - - // Commit transactions, create new ones. - drop(table); - TxRw::commit(tx_rw).unwrap(); - let mut tx_rw = env_inner.tx_rw().unwrap(); - let tx_ro = env_inner.tx_ro().unwrap(); - let mut table = env_inner.open_db_rw::<$table>(&tx_rw).unwrap(); - let table_ro = env_inner.open_db_ro::<$table>(&tx_ro).unwrap(); - - // Assert `get_range()` works. - { - let range = KEY..; - assert_eq!(1, table_ro.get_range(range.clone()).unwrap().count()); - let mut iter = table_ro.get_range(range).unwrap(); - let value = iter.next().unwrap().unwrap(); - assert_eq(&value); - } - - // Assert deleting works. - { - table.delete(&KEY).unwrap(); - let value = table.get(&KEY); - assert!(matches!(value, Err(RuntimeError::KeyNotFound))); - assert!(!table.contains(&KEY).unwrap()); - assert_eq!(table.len().unwrap(), 0); - } - - table.put(&KEY, &value).unwrap(); - - // Assert `clear_db()` works. - { - drop(table); - env_inner.clear_db::<$table>(&mut tx_rw).unwrap(); - let table = env_inner.open_db_rw::<$table>(&mut tx_rw).unwrap(); - let value = table.get(&KEY); - assert!(matches!(value, Err(RuntimeError::KeyNotFound))); - assert!(!table.contains(&KEY).unwrap()); - assert_eq!(table.len().unwrap(), 0); - } - } - )*}}; -} - -// Notes: -// - Keep this sorted A-Z (by table name) -test_tables! { - BlockBlobs, // Table type - BlockHeight => BlockBlob, // Key type => Value type - 123 => StorableVec(vec![1,2,3,4,5,6,7,8]), // Actual key => Actual value - - BlockHeights, - BlockHash => BlockHeight, - [32; 32] => 123, - - BlockInfos, - BlockHeight => BlockInfo, - 123 => BlockInfo { - timestamp: 1, - cumulative_generated_coins: 123, - weight: 321, - cumulative_difficulty_low: 111, - cumulative_difficulty_high: 111, - block_hash: [54; 32], - cumulative_rct_outs: 2389, - long_term_weight: 2389, - }, - - KeyImages, - KeyImage => (), - [32; 32] => (), - - NumOutputs, - Amount => AmountIndex, - 123 => 123, - - TxBlobs, - TxId => TxBlob, - 123 => StorableVec(vec![1,2,3,4,5,6,7,8]), - - TxIds, - TxHash => TxId, - [32; 32] => 123, - - TxHeights, - TxId => BlockHeight, - 123 => 123, - - TxOutputs, - TxId => AmountIndices, - 123 => StorableVec(vec![1,2,3,4,5,6,7,8]), - - TxUnlockTime, - TxId => UnlockTime, - 123 => 123, - - Outputs, - PreRctOutputId => Output, - PreRctOutputId { - amount: 1, - amount_index: 2, - } => Output { - key: [1; 32], - height: 1, - output_flags: OutputFlags::empty(), - tx_idx: 3, - }, - - PrunedTxBlobs, - TxId => PrunedBlob, - 123 => StorableVec(vec![1,2,3,4,5,6,7,8]), - - PrunableTxBlobs, - TxId => PrunableBlob, - 123 => StorableVec(vec![1,2,3,4,5,6,7,8]), - - PrunableHashes, - TxId => PrunableHash, - 123 => [32; 32], - - RctOutputs, - AmountIndex => RctOutput, - 123 => RctOutput { - key: [1; 32], - height: 1, - output_flags: OutputFlags::empty(), - tx_idx: 3, - commitment: [3; 32], - }, -} +// //! Tests for `cuprate_blockchain`'s backends. +// //! +// //! These tests are fully trait-based, meaning there +// //! is no reference to `backend/`-specific types. +// //! +// //! As such, which backend is tested is +// //! dependant on the feature flags used. +// //! +// //! | Feature flag | Tested backend | +// //! |---------------|----------------| +// //! | Only `redb` | `redb` +// //! | Anything else | `heed` +// //! +// //! `redb`, and it only must be enabled for it to be tested. + +// //---------------------------------------------------------------------------------------------------- Import + +// use crate::{ +// database::{DatabaseIter, DatabaseRo, DatabaseRw}, +// env::{Env, EnvInner}, +// error::RuntimeError, +// resize::ResizeAlgorithm, +// storable::StorableVec, +// tables::{ +// BlockBlobs, BlockHeights, BlockInfos, KeyImages, NumOutputs, Outputs, PrunableHashes, +// PrunableTxBlobs, PrunedTxBlobs, RctOutputs, TxBlobs, TxHeights, TxIds, TxOutputs, +// TxUnlockTime, +// }, +// tables::{TablesIter, TablesMut}, +// tests::tmp_concrete_env, +// transaction::{TxRo, TxRw}, +// types::{ +// Amount, AmountIndex, AmountIndices, BlockBlob, BlockHash, BlockHeight, BlockInfo, KeyImage, +// Output, OutputFlags, PreRctOutputId, PrunableBlob, PrunableHash, PrunedBlob, RctOutput, +// TxBlob, TxHash, TxId, UnlockTime, +// }, +// ConcreteEnv, +// }; + +// //---------------------------------------------------------------------------------------------------- Tests +// /// Simply call [`Env::open`]. If this fails, something is really wrong. +// #[test] +// fn open() { +// tmp_concrete_env(); +// } + +// /// Create database transactions, but don't write any data. +// #[test] +// fn tx() { +// let (env, _tempdir) = tmp_concrete_env(); +// let env_inner = env.env_inner(); + +// TxRo::commit(env_inner.tx_ro().unwrap()).unwrap(); +// TxRw::commit(env_inner.tx_rw().unwrap()).unwrap(); +// TxRw::abort(env_inner.tx_rw().unwrap()).unwrap(); +// } + +// /// Open (and verify) that all database tables +// /// exist already after calling [`Env::open`]. +// #[test] +// fn open_db() { +// let (env, _tempdir) = tmp_concrete_env(); +// let env_inner = env.env_inner(); +// let tx_ro = env_inner.tx_ro().unwrap(); +// let tx_rw = env_inner.tx_rw().unwrap(); + +// // Open all tables in read-only mode. +// // This should be updated when tables are modified. +// env_inner.open_db_ro::(&tx_ro).unwrap(); +// env_inner.open_db_ro::(&tx_ro).unwrap(); +// env_inner.open_db_ro::(&tx_ro).unwrap(); +// env_inner.open_db_ro::(&tx_ro).unwrap(); +// env_inner.open_db_ro::(&tx_ro).unwrap(); +// env_inner.open_db_ro::(&tx_ro).unwrap(); +// env_inner.open_db_ro::(&tx_ro).unwrap(); +// env_inner.open_db_ro::(&tx_ro).unwrap(); +// env_inner.open_db_ro::(&tx_ro).unwrap(); +// env_inner.open_db_ro::(&tx_ro).unwrap(); +// env_inner.open_db_ro::(&tx_ro).unwrap(); +// env_inner.open_db_ro::(&tx_ro).unwrap(); +// env_inner.open_db_ro::(&tx_ro).unwrap(); +// env_inner.open_db_ro::(&tx_ro).unwrap(); +// env_inner.open_db_ro::(&tx_ro).unwrap(); +// TxRo::commit(tx_ro).unwrap(); + +// // Open all tables in read/write mode. +// env_inner.open_db_rw::(&tx_rw).unwrap(); +// env_inner.open_db_rw::(&tx_rw).unwrap(); +// env_inner.open_db_rw::(&tx_rw).unwrap(); +// env_inner.open_db_rw::(&tx_rw).unwrap(); +// env_inner.open_db_rw::(&tx_rw).unwrap(); +// env_inner.open_db_rw::(&tx_rw).unwrap(); +// env_inner.open_db_rw::(&tx_rw).unwrap(); +// env_inner.open_db_rw::(&tx_rw).unwrap(); +// env_inner.open_db_rw::(&tx_rw).unwrap(); +// env_inner.open_db_rw::(&tx_rw).unwrap(); +// env_inner.open_db_rw::(&tx_rw).unwrap(); +// env_inner.open_db_rw::(&tx_rw).unwrap(); +// env_inner.open_db_rw::(&tx_rw).unwrap(); +// env_inner.open_db_rw::(&tx_rw).unwrap(); +// env_inner.open_db_rw::(&tx_rw).unwrap(); +// TxRw::commit(tx_rw).unwrap(); +// } + +// /// Test `Env` resizes. +// #[test] +// fn resize() { +// // This test is only valid for `Env`'s that need to resize manually. +// if !ConcreteEnv::MANUAL_RESIZE { +// return; +// } + +// let (env, _tempdir) = tmp_concrete_env(); + +// // Resize by the OS page size. +// let page_size = crate::resize::page_size(); +// let old_size = env.current_map_size(); +// env.resize_map(Some(ResizeAlgorithm::FixedBytes(page_size))); + +// // Assert it resized exactly by the OS page size. +// let new_size = env.current_map_size(); +// assert_eq!(new_size, old_size + page_size.get()); +// } + +// /// Test that `Env`'s that don't manually resize. +// #[test] +// #[should_panic = "unreachable"] +// fn non_manual_resize_1() { +// if ConcreteEnv::MANUAL_RESIZE { +// unreachable!(); +// } else { +// let (env, _tempdir) = tmp_concrete_env(); +// env.resize_map(None); +// } +// } + +// #[test] +// #[should_panic = "unreachable"] +// fn non_manual_resize_2() { +// if ConcreteEnv::MANUAL_RESIZE { +// unreachable!(); +// } else { +// let (env, _tempdir) = tmp_concrete_env(); +// env.current_map_size(); +// } +// } + +// /// Test all `DatabaseR{o,w}` operations. +// #[test] +// fn db_read_write() { +// let (env, _tempdir) = tmp_concrete_env(); +// let env_inner = env.env_inner(); +// let tx_rw = env_inner.tx_rw().unwrap(); +// let mut table = env_inner.open_db_rw::(&tx_rw).unwrap(); + +// /// The (1st) key. +// const KEY: PreRctOutputId = PreRctOutputId { +// amount: 1, +// amount_index: 123, +// }; +// /// The expected value. +// const VALUE: Output = Output { +// key: [35; 32], +// height: 45_761_798, +// output_flags: OutputFlags::empty(), +// tx_idx: 2_353_487, +// }; +// /// How many `(key, value)` pairs will be inserted. +// const N: u64 = 100; + +// /// Assert 2 `Output`'s are equal, and that accessing +// /// their fields don't result in an unaligned panic. +// fn assert_same(output: Output) { +// assert_eq!(output, VALUE); +// assert_eq!(output.key, VALUE.key); +// assert_eq!(output.height, VALUE.height); +// assert_eq!(output.output_flags, VALUE.output_flags); +// assert_eq!(output.tx_idx, VALUE.tx_idx); +// } + +// assert!(table.is_empty().unwrap()); + +// // Insert keys. +// let mut key = KEY; +// for _ in 0..N { +// table.put(&key, &VALUE).unwrap(); +// key.amount += 1; +// } + +// assert_eq!(table.len().unwrap(), N); + +// // Assert the first/last `(key, value)`s are there. +// { +// assert!(table.contains(&KEY).unwrap()); +// let get: Output = table.get(&KEY).unwrap(); +// assert_same(get); + +// let first: Output = table.first().unwrap().1; +// assert_same(first); + +// let last: Output = table.last().unwrap().1; +// assert_same(last); +// } + +// // Commit transactions, create new ones. +// drop(table); +// TxRw::commit(tx_rw).unwrap(); +// let tx_ro = env_inner.tx_ro().unwrap(); +// let table_ro = env_inner.open_db_ro::(&tx_ro).unwrap(); +// let tx_rw = env_inner.tx_rw().unwrap(); +// let mut table = env_inner.open_db_rw::(&tx_rw).unwrap(); + +// // Assert the whole range is there. +// { +// let range = table_ro.get_range(..).unwrap(); +// let mut i = 0; +// for result in range { +// let value: Output = result.unwrap(); +// assert_same(value); + +// i += 1; +// } +// assert_eq!(i, N); +// } + +// // `get_range()` tests. +// let mut key = KEY; +// key.amount += N; +// let range = KEY..key; + +// // Assert count is correct. +// assert_eq!( +// N as usize, +// table_ro.get_range(range.clone()).unwrap().count() +// ); + +// // Assert each returned value from the iterator is owned. +// { +// let mut iter = table_ro.get_range(range.clone()).unwrap(); +// let value: Output = iter.next().unwrap().unwrap(); // 1. take value out +// drop(iter); // 2. drop the `impl Iterator + 'a` +// assert_same(value); // 3. assert even without the iterator, the value is alive +// } + +// // Assert each value is the same. +// { +// let mut iter = table_ro.get_range(range).unwrap(); +// for _ in 0..N { +// let value: Output = iter.next().unwrap().unwrap(); +// assert_same(value); +// } +// } + +// // Assert `update()` works. +// { +// const HEIGHT: u32 = 999; + +// assert_ne!(table.get(&KEY).unwrap().height, HEIGHT); + +// table +// .update(&KEY, |mut value| { +// value.height = HEIGHT; +// Some(value) +// }) +// .unwrap(); + +// assert_eq!(table.get(&KEY).unwrap().height, HEIGHT); +// } + +// // Assert deleting works. +// { +// table.delete(&KEY).unwrap(); +// let value = table.get(&KEY); +// assert!(!table.contains(&KEY).unwrap()); +// assert!(matches!(value, Err(RuntimeError::KeyNotFound))); +// // Assert the other `(key, value)` pairs are still there. +// let mut key = KEY; +// key.amount += N - 1; // we used inclusive `0..N` +// let value = table.get(&key).unwrap(); +// assert_same(value); +// } + +// // Assert `take()` works. +// { +// let mut key = KEY; +// key.amount += 1; +// let value = table.take(&key).unwrap(); +// assert_eq!(value, VALUE); + +// let get = table.get(&KEY); +// assert!(!table.contains(&key).unwrap()); +// assert!(matches!(get, Err(RuntimeError::KeyNotFound))); + +// // Assert the other `(key, value)` pairs are still there. +// key.amount += 1; +// let value = table.get(&key).unwrap(); +// assert_same(value); +// } + +// drop(table); +// TxRw::commit(tx_rw).unwrap(); + +// // Assert `clear_db()` works. +// { +// let mut tx_rw = env_inner.tx_rw().unwrap(); +// env_inner.clear_db::(&mut tx_rw).unwrap(); +// let table = env_inner.open_db_rw::(&tx_rw).unwrap(); +// assert!(table.is_empty().unwrap()); +// for n in 0..N { +// let mut key = KEY; +// key.amount += n; +// let value = table.get(&key); +// assert!(matches!(value, Err(RuntimeError::KeyNotFound))); +// assert!(!table.contains(&key).unwrap()); +// } + +// // Reader still sees old value. +// assert!(!table_ro.is_empty().unwrap()); + +// // Writer sees updated value (nothing). +// assert!(table.is_empty().unwrap()); +// } +// } + +// /// Assert that `key`'s in database tables are sorted in +// /// an ordered B-Tree fashion, i.e. `min_value -> max_value`. +// #[test] +// fn tables_are_sorted() { +// let (env, _tmp) = tmp_concrete_env(); +// let env_inner = env.env_inner(); +// let tx_rw = env_inner.tx_rw().unwrap(); +// let mut tables_mut = env_inner.open_tables_mut(&tx_rw).unwrap(); + +// // Insert `{5, 4, 3, 2, 1, 0}`, assert each new +// // number inserted is the minimum `first()` value. +// for key in (0..6).rev() { +// tables_mut.num_outputs_mut().put(&key, &123).unwrap(); +// let (first, _) = tables_mut.num_outputs_mut().first().unwrap(); +// assert_eq!(first, key); +// } + +// drop(tables_mut); +// TxRw::commit(tx_rw).unwrap(); +// let tx_rw = env_inner.tx_rw().unwrap(); + +// // Assert iterators are ordered. +// { +// let tx_ro = env_inner.tx_ro().unwrap(); +// let tables = env_inner.open_tables(&tx_ro).unwrap(); +// let t = tables.num_outputs_iter(); +// let iter = t.iter().unwrap(); +// let keys = t.keys().unwrap(); +// for ((i, iter), key) in (0..6).zip(iter).zip(keys) { +// let (iter, _) = iter.unwrap(); +// let key = key.unwrap(); +// assert_eq!(i, iter); +// assert_eq!(iter, key); +// } +// } + +// let mut tables_mut = env_inner.open_tables_mut(&tx_rw).unwrap(); +// let t = tables_mut.num_outputs_mut(); + +// // Assert the `first()` values are the minimum, i.e. `{0, 1, 2}` +// for key in 0..3 { +// let (first, _) = t.first().unwrap(); +// assert_eq!(first, key); +// t.delete(&key).unwrap(); +// } + +// // Assert the `last()` values are the maximum, i.e. `{5, 4, 3}` +// for key in (3..6).rev() { +// let (last, _) = tables_mut.num_outputs_mut().last().unwrap(); +// assert_eq!(last, key); +// tables_mut.num_outputs_mut().delete(&key).unwrap(); +// } +// } + +// //---------------------------------------------------------------------------------------------------- Table Tests +// /// Test multiple tables and their key + values. +// /// +// /// Each one of these tests: +// /// - Opens a specific table +// /// - Essentially does the `db_read_write` test +// macro_rules! test_tables { +// ($( +// $table:ident, // Table type +// $key_type:ty => // Key (type) +// $value_type:ty, // Value (type) +// $key:expr => // Key (the value) +// $value:expr, // Value (the value) +// )* $(,)?) => { paste::paste! { $( +// // Test function's name is the table type in `snake_case`. +// #[test] +// fn [<$table:snake>]() { +// // Open the database env and table. +// let (env, _tempdir) = tmp_concrete_env(); +// let env_inner = env.env_inner(); +// let mut tx_rw = env_inner.tx_rw().unwrap(); +// let mut table = env_inner.open_db_rw::<$table>(&mut tx_rw).unwrap(); + +// /// The expected key. +// const KEY: $key_type = $key; +// // The expected value. +// let value: $value_type = $value; + +// // Assert a passed value is equal to the const value. +// let assert_eq = |v: &$value_type| { +// assert_eq!(v, &value); +// }; + +// // Insert the key. +// table.put(&KEY, &value).unwrap(); +// // Assert key is there. +// { +// let value: $value_type = table.get(&KEY).unwrap(); +// assert_eq(&value); +// } + +// assert!(table.contains(&KEY).unwrap()); +// assert_eq!(table.len().unwrap(), 1); + +// // Commit transactions, create new ones. +// drop(table); +// TxRw::commit(tx_rw).unwrap(); +// let mut tx_rw = env_inner.tx_rw().unwrap(); +// let tx_ro = env_inner.tx_ro().unwrap(); +// let mut table = env_inner.open_db_rw::<$table>(&tx_rw).unwrap(); +// let table_ro = env_inner.open_db_ro::<$table>(&tx_ro).unwrap(); + +// // Assert `get_range()` works. +// { +// let range = KEY..; +// assert_eq!(1, table_ro.get_range(range.clone()).unwrap().count()); +// let mut iter = table_ro.get_range(range).unwrap(); +// let value = iter.next().unwrap().unwrap(); +// assert_eq(&value); +// } + +// // Assert deleting works. +// { +// table.delete(&KEY).unwrap(); +// let value = table.get(&KEY); +// assert!(matches!(value, Err(RuntimeError::KeyNotFound))); +// assert!(!table.contains(&KEY).unwrap()); +// assert_eq!(table.len().unwrap(), 0); +// } + +// table.put(&KEY, &value).unwrap(); + +// // Assert `clear_db()` works. +// { +// drop(table); +// env_inner.clear_db::<$table>(&mut tx_rw).unwrap(); +// let table = env_inner.open_db_rw::<$table>(&mut tx_rw).unwrap(); +// let value = table.get(&KEY); +// assert!(matches!(value, Err(RuntimeError::KeyNotFound))); +// assert!(!table.contains(&KEY).unwrap()); +// assert_eq!(table.len().unwrap(), 0); +// } +// } +// )*}}; +// } + +// // Notes: +// // - Keep this sorted A-Z (by table name) +// test_tables! { +// BlockBlobs, // Table type +// BlockHeight => BlockBlob, // Key type => Value type +// 123 => StorableVec(vec![1,2,3,4,5,6,7,8]), // Actual key => Actual value + +// BlockHeights, +// BlockHash => BlockHeight, +// [32; 32] => 123, + +// BlockInfos, +// BlockHeight => BlockInfo, +// 123 => BlockInfo { +// timestamp: 1, +// cumulative_generated_coins: 123, +// weight: 321, +// cumulative_difficulty_low: 111, +// cumulative_difficulty_high: 111, +// block_hash: [54; 32], +// cumulative_rct_outs: 2389, +// long_term_weight: 2389, +// }, + +// KeyImages, +// KeyImage => (), +// [32; 32] => (), + +// NumOutputs, +// Amount => AmountIndex, +// 123 => 123, + +// TxBlobs, +// TxId => TxBlob, +// 123 => StorableVec(vec![1,2,3,4,5,6,7,8]), + +// TxIds, +// TxHash => TxId, +// [32; 32] => 123, + +// TxHeights, +// TxId => BlockHeight, +// 123 => 123, + +// TxOutputs, +// TxId => AmountIndices, +// 123 => StorableVec(vec![1,2,3,4,5,6,7,8]), + +// TxUnlockTime, +// TxId => UnlockTime, +// 123 => 123, + +// Outputs, +// PreRctOutputId => Output, +// PreRctOutputId { +// amount: 1, +// amount_index: 2, +// } => Output { +// key: [1; 32], +// height: 1, +// output_flags: OutputFlags::empty(), +// tx_idx: 3, +// }, + +// PrunedTxBlobs, +// TxId => PrunedBlob, +// 123 => StorableVec(vec![1,2,3,4,5,6,7,8]), + +// PrunableTxBlobs, +// TxId => PrunableBlob, +// 123 => StorableVec(vec![1,2,3,4,5,6,7,8]), + +// PrunableHashes, +// TxId => PrunableHash, +// 123 => [32; 32], + +// RctOutputs, +// AmountIndex => RctOutput, +// 123 => RctOutput { +// key: [1; 32], +// height: 1, +// output_flags: OutputFlags::empty(), +// tx_idx: 3, +// commitment: [3; 32], +// }, +// } diff --git a/storage/database/src/env.rs b/storage/database/src/env.rs index 3a32666..1459f88 100644 --- a/storage/database/src/env.rs +++ b/storage/database/src/env.rs @@ -9,7 +9,6 @@ use crate::{ error::{InitError, RuntimeError}, resize::ResizeAlgorithm, table::Table, - tables::{call_fn_on_all_tables_or_early_return, TablesIter, TablesMut}, transaction::{TxRo, TxRw}, }; @@ -249,29 +248,31 @@ where #[doc = doc_table_error!()] fn open_db_rw(&self, tx_rw: &Rw) -> Result, RuntimeError>; - /// Open all tables in read/iter mode. - /// - /// This calls [`EnvInner::open_db_ro`] on all database tables - /// and returns a structure that allows access to all tables. - /// - #[doc = doc_table_error!()] - fn open_tables(&self, tx_ro: &Ro) -> Result { - call_fn_on_all_tables_or_early_return! { - Self::open_db_ro(self, tx_ro) - } - } + // TODO: make equivalent in `cuprate-blockchain`. - /// Open all tables in read-write mode. - /// - /// This calls [`EnvInner::open_db_rw`] on all database tables - /// and returns a structure that allows access to all tables. - /// - #[doc = doc_table_error!()] - fn open_tables_mut(&self, tx_rw: &Rw) -> Result { - call_fn_on_all_tables_or_early_return! { - Self::open_db_rw(self, tx_rw) - } - } + // /// Open all tables in read/iter mode. + // /// + // /// This calls [`EnvInner::open_db_ro`] on all database tables + // /// and returns a structure that allows access to all tables. + // /// + // #[doc = doc_table_error!()] + // fn open_tables(&self, tx_ro: &Ro) -> Result { + // call_fn_on_all_tables_or_early_return! { + // Self::open_db_ro(self, tx_ro) + // } + // } + + // /// Open all tables in read-write mode. + // /// + // /// This calls [`EnvInner::open_db_rw`] on all database tables + // /// and returns a structure that allows access to all tables. + // /// + // #[doc = doc_table_error!()] + // fn open_tables_mut(&self, tx_rw: &Rw) -> Result { + // call_fn_on_all_tables_or_early_return! { + // Self::open_db_rw(self, tx_rw) + // } + // } /// Clear all `(key, value)`'s from a database table. /// diff --git a/storage/database/src/tests.rs b/storage/database/src/tests.rs index 90a7413..c57b969 100644 --- a/storage/database/src/tests.rs +++ b/storage/database/src/tests.rs @@ -1,85 +1,85 @@ -//! Utilities for `cuprate_blockchain` testing. -//! -//! These types/fn's are only: -//! - enabled on #[cfg(test)] -//! - only used internally +// //! Utilities for `cuprate_blockchain` testing. +// //! +// //! These types/fn's are only: +// //! - enabled on #[cfg(test)] +// //! - only used internally -//---------------------------------------------------------------------------------------------------- Import -use std::fmt::Debug; +// //---------------------------------------------------------------------------------------------------- Import +// use std::fmt::Debug; -use pretty_assertions::assert_eq; +// use pretty_assertions::assert_eq; -use crate::{config::ConfigBuilder, tables::Tables, ConcreteEnv, DatabaseRo, Env, EnvInner}; +// use crate::{config::ConfigBuilder, tables::Tables, ConcreteEnv, DatabaseRo, Env, EnvInner}; -//---------------------------------------------------------------------------------------------------- Struct -/// Named struct to assert the length of all tables. -/// -/// This is a struct with fields instead of a function -/// so that callers can name arguments, otherwise the call-site -/// is a little confusing, i.e. `assert_table_len(0, 25, 1, 123)`. -#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub(crate) struct AssertTableLen { - pub(crate) block_infos: u64, - pub(crate) block_blobs: u64, - pub(crate) block_heights: u64, - pub(crate) key_images: u64, - pub(crate) num_outputs: u64, - pub(crate) pruned_tx_blobs: u64, - pub(crate) prunable_hashes: u64, - pub(crate) outputs: u64, - pub(crate) prunable_tx_blobs: u64, - pub(crate) rct_outputs: u64, - pub(crate) tx_blobs: u64, - pub(crate) tx_ids: u64, - pub(crate) tx_heights: u64, - pub(crate) tx_unlock_time: u64, -} +// //---------------------------------------------------------------------------------------------------- Struct +// /// Named struct to assert the length of all tables. +// /// +// /// This is a struct with fields instead of a function +// /// so that callers can name arguments, otherwise the call-site +// /// is a little confusing, i.e. `assert_table_len(0, 25, 1, 123)`. +// #[derive(Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] +// pub(crate) struct AssertTableLen { +// pub(crate) block_infos: u64, +// pub(crate) block_blobs: u64, +// pub(crate) block_heights: u64, +// pub(crate) key_images: u64, +// pub(crate) num_outputs: u64, +// pub(crate) pruned_tx_blobs: u64, +// pub(crate) prunable_hashes: u64, +// pub(crate) outputs: u64, +// pub(crate) prunable_tx_blobs: u64, +// pub(crate) rct_outputs: u64, +// pub(crate) tx_blobs: u64, +// pub(crate) tx_ids: u64, +// pub(crate) tx_heights: u64, +// pub(crate) tx_unlock_time: u64, +// } -impl AssertTableLen { - /// Assert the length of all tables. - pub(crate) fn assert(self, tables: &impl Tables) { - let other = Self { - block_infos: tables.block_infos().len().unwrap(), - block_blobs: tables.block_blobs().len().unwrap(), - block_heights: tables.block_heights().len().unwrap(), - key_images: tables.key_images().len().unwrap(), - num_outputs: tables.num_outputs().len().unwrap(), - pruned_tx_blobs: tables.pruned_tx_blobs().len().unwrap(), - prunable_hashes: tables.prunable_hashes().len().unwrap(), - outputs: tables.outputs().len().unwrap(), - prunable_tx_blobs: tables.prunable_tx_blobs().len().unwrap(), - rct_outputs: tables.rct_outputs().len().unwrap(), - tx_blobs: tables.tx_blobs().len().unwrap(), - tx_ids: tables.tx_ids().len().unwrap(), - tx_heights: tables.tx_heights().len().unwrap(), - tx_unlock_time: tables.tx_unlock_time().len().unwrap(), - }; +// impl AssertTableLen { +// /// Assert the length of all tables. +// pub(crate) fn assert(self, tables: &impl Tables) { +// let other = Self { +// block_infos: tables.block_infos().len().unwrap(), +// block_blobs: tables.block_blobs().len().unwrap(), +// block_heights: tables.block_heights().len().unwrap(), +// key_images: tables.key_images().len().unwrap(), +// num_outputs: tables.num_outputs().len().unwrap(), +// pruned_tx_blobs: tables.pruned_tx_blobs().len().unwrap(), +// prunable_hashes: tables.prunable_hashes().len().unwrap(), +// outputs: tables.outputs().len().unwrap(), +// prunable_tx_blobs: tables.prunable_tx_blobs().len().unwrap(), +// rct_outputs: tables.rct_outputs().len().unwrap(), +// tx_blobs: tables.tx_blobs().len().unwrap(), +// tx_ids: tables.tx_ids().len().unwrap(), +// tx_heights: tables.tx_heights().len().unwrap(), +// tx_unlock_time: tables.tx_unlock_time().len().unwrap(), +// }; - assert_eq!(self, other); - } -} +// assert_eq!(self, other); +// } +// } -//---------------------------------------------------------------------------------------------------- fn -/// Create an `Env` in a temporarily directory. -/// The directory is automatically removed after the `TempDir` is dropped. -/// -/// FIXME: changing this to `-> impl Env` causes lifetime errors... -pub(crate) fn tmp_concrete_env() -> (ConcreteEnv, tempfile::TempDir) { - let tempdir = tempfile::tempdir().unwrap(); - let config = ConfigBuilder::new() - .db_directory(tempdir.path().into()) - .low_power() - .build(); - let env = ConcreteEnv::open(config).unwrap(); +// //---------------------------------------------------------------------------------------------------- fn +// /// Create an `Env` in a temporarily directory. +// /// The directory is automatically removed after the `TempDir` is dropped. +// /// +// /// FIXME: changing this to `-> impl Env` causes lifetime errors... +// pub(crate) fn tmp_concrete_env() -> (ConcreteEnv, tempfile::TempDir) { +// let tempdir = tempfile::tempdir().unwrap(); +// let config = ConfigBuilder::new() +// .db_directory(tempdir.path().into()) +// .low_power() +// .build(); +// let env = ConcreteEnv::open(config).unwrap(); - (env, tempdir) -} +// (env, tempdir) +// } -/// Assert all the tables in the environment are empty. -pub(crate) fn assert_all_tables_are_empty(env: &ConcreteEnv) { - let env_inner = env.env_inner(); - let tx_ro = env_inner.tx_ro().unwrap(); - let tables = env_inner.open_tables(&tx_ro).unwrap(); - assert!(tables.all_tables_empty().unwrap()); - assert_eq!(crate::ops::tx::get_num_tx(tables.tx_ids()).unwrap(), 0); -} +// /// Assert all the tables in the environment are empty. +// pub(crate) fn assert_all_tables_are_empty(env: &ConcreteEnv) { +// let env_inner = env.env_inner(); +// let tx_ro = env_inner.tx_ro().unwrap(); +// let tables = env_inner.open_tables(&tx_ro).unwrap(); +// assert!(tables.all_tables_empty().unwrap()); +// assert_eq!(crate::ops::tx::get_num_tx(tables.tx_ids()).unwrap(), 0); +// }