mirror of
https://github.com/Cuprate/cuprate.git
synced 2024-12-28 22:49:30 +00:00
9c27ba5791
* write: impl write_block()
* ops: add `get_block_info()`
* read: impl block fn's
* read: fix signatures
* service: wrap `ConcreteEnv` in `RwLock` and doc why
* heed: use `read-txn-no-tls` for `Send` read transactions
* service: remove RwLock, impl some read functions
* read: impl `outputs()`
* read: flatten indentation, add `thread_local()`
* read: impl `number_outputs_with_amount()`
* tests: add `AssertTableLen`
* ops: replace all table len asserts with `AssertTableLen`
* service: initial tests
* service: finish most tests
* service: fix bad block data in test
* tables: fix incorrect doc
* service: add `ReadRequest::Outputs` test
* read: use macros for set/getting `ThreadLocal`'s based on backend
* small fixes
* fix review
* small fixes
* read: fix ThreadLocal macros for `redb`
* read: move `Output` mapping to `crate::free`
it's needed in tests too
* service: check output value correctness in tests
* helper: add timelock <-> u64 mapping functions
* free: use `u64_to_timelock()`
* read: rct outputs
* read: fix variable name
* read: use ThreadLocal for both backends
* heed: use Mutex for `HeedTableRo`'s read tx
* block: add miner_tx
* heed: remove Table bound
oops
* Revert "heed: use Mutex for `HeedTableRo`'s read tx"
This reverts commit 7e8aae016c
.
* add `UnsafeSendable`
* read: use `UnsafeSendable` for `heed`, branch on backend
* read: safety docs
* cargo.toml: re-add `read-txn-no-tls` for heed
* ops: fix tests, remove miner_tx
* fix tx_idx calculation, account for RCT outputs in tests
* read: docs, fix `get_tables!()` for both backends
* fix clippy
* database: `unsafe trait DatabaseRo`
* tx: use correct tx_id
* free: remove miner_tx comment
* free: remove `amount` input for rct outputs
* ops: split `add_tx` inputs
* read: use `UnsafeSendable` for all backends
* heed: update safety comment
* move output functions `free` -> `ops`
* read: fix `chain_height()` handling
* remove serde on `UnsafeSendable`
* de-dup docs on `trait DatabaseRo`, `get_tables!()`
* Update database/src/unsafe_sendable.rs
Co-authored-by: Boog900 <boog900@tutanota.com>
---------
Co-authored-by: Boog900 <boog900@tutanota.com>
267 lines
9.3 KiB
Rust
267 lines
9.3 KiB
Rust
//! Abstracted database environment; `trait Env`.
|
|
|
|
//---------------------------------------------------------------------------------------------------- Import
|
|
use std::{fmt::Debug, num::NonZeroUsize, ops::Deref};
|
|
|
|
use crate::{
|
|
config::Config,
|
|
database::{DatabaseIter, DatabaseRo, DatabaseRw},
|
|
error::{InitError, RuntimeError},
|
|
resize::ResizeAlgorithm,
|
|
table::Table,
|
|
tables::{
|
|
call_fn_on_all_tables_or_early_return, BlockBlobs, BlockHeights, BlockInfos, KeyImages,
|
|
NumOutputs, Outputs, PrunableHashes, PrunableTxBlobs, PrunedTxBlobs, RctOutputs, Tables,
|
|
TablesIter, TablesMut, TxHeights, TxIds, TxUnlockTime,
|
|
},
|
|
transaction::{TxRo, TxRw},
|
|
};
|
|
|
|
//---------------------------------------------------------------------------------------------------- Env
|
|
/// Database environment abstraction.
|
|
///
|
|
/// Essentially, the functions that can be called on [`ConcreteEnv`](crate::ConcreteEnv).
|
|
///
|
|
/// # `Drop`
|
|
/// Objects that implement [`Env`] _should_ probably
|
|
/// [`Env::sync`] in their drop implementations,
|
|
/// although, no invariant relies on this (yet).
|
|
///
|
|
/// # Lifetimes
|
|
/// TODO: Explain the very sequential lifetime pipeline:
|
|
/// - `ConcreteEnv` -> `'env` -> `'tx` -> `impl DatabaseR{o,w}`
|
|
pub trait Env: Sized {
|
|
//------------------------------------------------ Constants
|
|
/// Does the database backend need to be manually
|
|
/// resized when the memory-map is full?
|
|
///
|
|
/// # Invariant
|
|
/// If this is `false`, that means this [`Env`]
|
|
/// can _never_ return a [`RuntimeError::ResizeNeeded`].
|
|
///
|
|
/// If this is `true`, [`Env::resize_map`] & [`Env::current_map_size`]
|
|
/// _must_ be re-implemented, as it just panics by default.
|
|
const MANUAL_RESIZE: bool;
|
|
|
|
/// Does the database backend forcefully sync/flush
|
|
/// to disk on every transaction commit?
|
|
///
|
|
/// This is used as an optimization.
|
|
const SYNCS_PER_TX: bool;
|
|
|
|
//------------------------------------------------ Types
|
|
/// The struct representing the actual backend's database environment.
|
|
///
|
|
/// This is used as the `self` in [`EnvInner`] functions, so whatever
|
|
/// this type is, is what will be accessible from those functions.
|
|
///
|
|
/// # Explanation (not needed for practical use)
|
|
/// For `heed`, this is just `heed::Env`, for `redb` this is
|
|
/// `(redb::Database, redb::Durability)` as each transaction
|
|
/// needs the sync mode set during creation.
|
|
type EnvInner<'env>: EnvInner<'env, Self::TxRo<'env>, Self::TxRw<'env>>
|
|
where
|
|
Self: 'env;
|
|
|
|
/// The read-only transaction type of the backend.
|
|
type TxRo<'env>: TxRo<'env> + 'env
|
|
where
|
|
Self: 'env;
|
|
|
|
/// The read/write transaction type of the backend.
|
|
type TxRw<'env>: TxRw<'env> + 'env
|
|
where
|
|
Self: 'env;
|
|
|
|
//------------------------------------------------ Required
|
|
/// Open the database environment, using the passed [`Config`].
|
|
///
|
|
/// # Invariants
|
|
/// This function **must** create all tables listed in [`crate::tables`].
|
|
///
|
|
/// The rest of the functions depend on the fact
|
|
/// they already exist, or else they will panic.
|
|
///
|
|
/// # Errors
|
|
/// This will error if the database could not be opened.
|
|
///
|
|
/// This is the only [`Env`] function that will return
|
|
/// an [`InitError`] instead of a [`RuntimeError`].
|
|
fn open(config: Config) -> Result<Self, InitError>;
|
|
|
|
/// Return the [`Config`] that this database was [`Env::open`]ed with.
|
|
fn config(&self) -> &Config;
|
|
|
|
/// Fully sync the database caches to disk.
|
|
///
|
|
/// # Invariant
|
|
/// This must **fully** and **synchronously** flush the database data to disk.
|
|
///
|
|
/// I.e., after this function returns, there must be no doubts
|
|
/// that the data isn't synced yet, it _must_ be synced.
|
|
///
|
|
/// TODO: either this invariant or `sync()` itself will most
|
|
/// likely be removed/changed after `SyncMode` is finalized.
|
|
///
|
|
/// # Errors
|
|
/// TODO
|
|
fn sync(&self) -> Result<(), RuntimeError>;
|
|
|
|
/// Resize the database's memory map to a
|
|
/// new (bigger) size using a [`ResizeAlgorithm`].
|
|
///
|
|
/// By default, this function will use the `ResizeAlgorithm` in [`Env::config`].
|
|
///
|
|
/// If `resize_algorithm` is `Some`, that will be used instead.
|
|
///
|
|
/// This function returns the _new_ memory map size in bytes.
|
|
///
|
|
/// # Invariant
|
|
/// This function _must_ be re-implemented if [`Env::MANUAL_RESIZE`] is `true`.
|
|
///
|
|
/// Otherwise, this function will panic with `unreachable!()`.
|
|
fn resize_map(&self, resize_algorithm: Option<ResizeAlgorithm>) -> NonZeroUsize {
|
|
unreachable!()
|
|
}
|
|
|
|
/// What is the _current_ size of the database's memory map in bytes?
|
|
///
|
|
/// # Invariant
|
|
/// 1. This function _must_ be re-implemented if [`Env::MANUAL_RESIZE`] is `true`.
|
|
/// 2. This function must be accurate, as [`Env::resize_map()`] may depend on it.
|
|
fn current_map_size(&self) -> usize {
|
|
unreachable!()
|
|
}
|
|
|
|
/// Return the [`Env::EnvInner`].
|
|
///
|
|
/// # Locking behavior
|
|
/// When using the `heed` backend, [`Env::EnvInner`] is a
|
|
/// `RwLockReadGuard`, i.e., calling this function takes a
|
|
/// read lock on the `heed::Env`.
|
|
///
|
|
/// Be aware of this, as other functions (currently only
|
|
/// [`Env::resize_map`]) will take a _write_ lock.
|
|
fn env_inner(&self) -> Self::EnvInner<'_>;
|
|
|
|
//------------------------------------------------ Provided
|
|
/// Return the amount of actual of bytes the database is taking up on disk.
|
|
///
|
|
/// This is the current _disk_ value in bytes, not the memory map.
|
|
///
|
|
/// # Errors
|
|
/// This will error if either:
|
|
///
|
|
/// - [`std::fs::File::open`]
|
|
/// - [`std::fs::File::metadata`]
|
|
///
|
|
/// failed on the database file on disk.
|
|
fn disk_size_bytes(&self) -> std::io::Result<u64> {
|
|
// We have the direct PATH to the file,
|
|
// no need to use backend-specific functions.
|
|
//
|
|
// SAFETY: as we are only accessing the metadata of
|
|
// the file and not reading the bytes, it should be
|
|
// fine even with a memory mapped file being actively
|
|
// written to.
|
|
Ok(std::fs::File::open(&self.config().db_file)?
|
|
.metadata()?
|
|
.len())
|
|
}
|
|
}
|
|
|
|
//---------------------------------------------------------------------------------------------------- DatabaseRo
|
|
/// TODO
|
|
pub trait EnvInner<'env, Ro, Rw>
|
|
where
|
|
Self: 'env,
|
|
Ro: TxRo<'env>,
|
|
Rw: TxRw<'env>,
|
|
{
|
|
/// Create a read-only transaction.
|
|
///
|
|
/// # Errors
|
|
/// This will only return [`RuntimeError::Io`] if it errors.
|
|
fn tx_ro(&'env self) -> Result<Ro, RuntimeError>;
|
|
|
|
/// Create a read/write transaction.
|
|
///
|
|
/// # Errors
|
|
/// This will only return [`RuntimeError::Io`] if it errors.
|
|
fn tx_rw(&'env self) -> Result<Rw, RuntimeError>;
|
|
|
|
/// Open a database in read-only mode.
|
|
///
|
|
/// This will open the database [`Table`]
|
|
/// passed as a generic to this function.
|
|
///
|
|
/// ```rust,ignore
|
|
/// let db = env.open_db_ro::<Table>(&tx_ro);
|
|
/// // ^ ^
|
|
/// // database table table metadata
|
|
/// // (name, key/value type)
|
|
/// ```
|
|
///
|
|
/// # Errors
|
|
/// This function errors upon internal database/IO errors.
|
|
///
|
|
/// As [`Table`] is `Sealed`, and all tables are created
|
|
/// upon [`Env::open`], this function will never error because
|
|
/// a table doesn't exist.
|
|
fn open_db_ro<T: Table>(
|
|
&self,
|
|
tx_ro: &Ro,
|
|
) -> Result<impl DatabaseRo<T> + DatabaseIter<T>, RuntimeError>;
|
|
|
|
/// Open a database in read/write mode.
|
|
///
|
|
/// All [`DatabaseRo`] functions are also callable
|
|
/// with the returned [`DatabaseRw`] structure.
|
|
///
|
|
/// This will open the database [`Table`]
|
|
/// passed as a generic to this function.
|
|
///
|
|
/// # Errors
|
|
/// This function errors upon internal database/IO errors.
|
|
///
|
|
/// As [`Table`] is `Sealed`, and all tables are created
|
|
/// upon [`Env::open`], this function will never error because
|
|
/// a table doesn't exist.
|
|
fn open_db_rw<T: Table>(&self, tx_rw: &Rw) -> Result<impl DatabaseRw<T>, RuntimeError>;
|
|
|
|
/// TODO
|
|
///
|
|
/// # Errors
|
|
/// TODO
|
|
fn open_tables(&self, tx_ro: &Ro) -> Result<impl TablesIter, RuntimeError> {
|
|
call_fn_on_all_tables_or_early_return! {
|
|
Self::open_db_ro(self, tx_ro)
|
|
}
|
|
}
|
|
|
|
/// TODO
|
|
///
|
|
/// # Errors
|
|
/// TODO
|
|
fn open_tables_mut(&self, tx_rw: &Rw) -> Result<impl TablesMut, RuntimeError> {
|
|
call_fn_on_all_tables_or_early_return! {
|
|
Self::open_db_rw(self, tx_rw)
|
|
}
|
|
}
|
|
|
|
/// Clear all `(key, value)`'s from a database table.
|
|
///
|
|
/// This will delete all key and values in the passed
|
|
/// `T: Table`, but the table itself will continue to exist.
|
|
///
|
|
/// Note that this operation is tied to `tx_rw`, as such this
|
|
/// function's effects can be aborted using [`TxRw::abort`].
|
|
///
|
|
/// # Errors
|
|
/// This function errors upon internal database/IO errors.
|
|
///
|
|
/// As [`Table`] is `Sealed`, and all tables are created
|
|
/// upon [`Env::open`], this function will never error because
|
|
/// a table doesn't exist.
|
|
fn clear_db<T: Table>(&self, tx_rw: &mut Rw) -> Result<(), RuntimeError>;
|
|
}
|