mirror of
https://github.com/Cuprate/cuprate.git
synced 2024-11-16 15:58:17 +00:00
storage: doc fixes (#228)
Some checks are pending
CI / fmt (push) Waiting to run
CI / typo (push) Waiting to run
CI / ci (macos-latest, stable, bash) (push) Waiting to run
CI / ci (ubuntu-latest, stable, bash) (push) Waiting to run
CI / ci (windows-latest, stable-x86_64-pc-windows-gnu, msys2 {0}) (push) Waiting to run
Doc / build (push) Waiting to run
Doc / deploy (push) Blocked by required conditions
Some checks are pending
CI / fmt (push) Waiting to run
CI / typo (push) Waiting to run
CI / ci (macos-latest, stable, bash) (push) Waiting to run
CI / ci (ubuntu-latest, stable, bash) (push) Waiting to run
CI / ci (windows-latest, stable-x86_64-pc-windows-gnu, msys2 {0}) (push) Waiting to run
Doc / build (push) Waiting to run
Doc / deploy (push) Blocked by required conditions
* database: doc fixes * blockchain: doc fixes * database: fix doc test * database: readme fixes * blockchain: ops fix * blockchain: readme fix
This commit is contained in:
parent
fbae3df203
commit
0a390a362a
14 changed files with 100 additions and 65 deletions
|
@ -5,6 +5,10 @@ This documentation is mostly for practical usage of `cuprate_blockchain`.
|
|||
For a high-level overview, see the database section in
|
||||
[Cuprate's architecture book](https://architecture.cuprate.org).
|
||||
|
||||
If you're looking for a database crate, consider using the lower-level
|
||||
[`cuprate-database`](https://doc.cuprate.org/cuprate_database)
|
||||
crate that this crate is built on-top of.
|
||||
|
||||
# Purpose
|
||||
This crate does 3 things:
|
||||
1. Uses [`cuprate_database`] as a base database layer
|
||||
|
@ -47,11 +51,11 @@ there are some things that must be kept in mind when doing so.
|
|||
Failing to uphold these invariants may cause panics.
|
||||
|
||||
1. `LMDB` requires the user to resize the memory map resizing (see [`cuprate_database::RuntimeError::ResizeNeeded`]
|
||||
1. `LMDB` has a maximum reader transaction count, currently it is set to `128`
|
||||
1. `LMDB` has a maximum reader transaction count, currently, [it is set to `126`](https://github.com/LMDB/lmdb/blob/b8e54b4c31378932b69f1298972de54a565185b1/libraries/liblmdb/mdb.c#L794-L799)
|
||||
1. `LMDB` has [maximum key/value byte size](http://www.lmdb.tech/doc/group__internal.html#gac929399f5d93cef85f874b9e9b1d09e0) which must not be exceeded
|
||||
|
||||
# Examples
|
||||
The below is an example of using `cuprate_blockchain`
|
||||
The below is an example of using `cuprate_blockchain`'s
|
||||
lowest API, i.e. using a mix of this crate and `cuprate_database`'s traits directly -
|
||||
**this is NOT recommended.**
|
||||
|
||||
|
|
|
@ -20,12 +20,11 @@ use serde::{Deserialize, Serialize};
|
|||
/// This controls how many reader thread `service`'s
|
||||
/// thread-pool will spawn to receive and send requests/responses.
|
||||
///
|
||||
/// It does nothing outside of `service`.
|
||||
///
|
||||
/// It will always be at least 1, up until the amount of threads on the machine.
|
||||
///
|
||||
/// # Invariant
|
||||
/// The main function used to extract an actual
|
||||
/// usable thread count out of this is [`ReaderThreads::as_threads`].
|
||||
///
|
||||
/// This will always return at least 1, up until the amount of threads on the machine.
|
||||
#[derive(Copy, Clone, Debug, Default, PartialEq, PartialOrd)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub enum ReaderThreads {
|
||||
|
@ -97,30 +96,30 @@ impl ReaderThreads {
|
|||
///
|
||||
/// # Example
|
||||
/// ```rust
|
||||
/// use cuprate_blockchain::config::ReaderThreads as Rt;
|
||||
/// use cuprate_blockchain::config::ReaderThreads as R;
|
||||
///
|
||||
/// let total_threads: std::num::NonZeroUsize =
|
||||
/// cuprate_helper::thread::threads();
|
||||
///
|
||||
/// assert_eq!(Rt::OnePerThread.as_threads(), total_threads);
|
||||
/// assert_eq!(R::OnePerThread.as_threads(), total_threads);
|
||||
///
|
||||
/// assert_eq!(Rt::One.as_threads().get(), 1);
|
||||
/// assert_eq!(R::One.as_threads().get(), 1);
|
||||
///
|
||||
/// assert_eq!(Rt::Number(0).as_threads(), total_threads);
|
||||
/// assert_eq!(Rt::Number(1).as_threads().get(), 1);
|
||||
/// assert_eq!(Rt::Number(usize::MAX).as_threads(), total_threads);
|
||||
/// assert_eq!(R::Number(0).as_threads(), total_threads);
|
||||
/// assert_eq!(R::Number(1).as_threads().get(), 1);
|
||||
/// assert_eq!(R::Number(usize::MAX).as_threads(), total_threads);
|
||||
///
|
||||
/// assert_eq!(Rt::Percent(0.01).as_threads().get(), 1);
|
||||
/// assert_eq!(Rt::Percent(0.0).as_threads(), total_threads);
|
||||
/// assert_eq!(Rt::Percent(1.0).as_threads(), total_threads);
|
||||
/// assert_eq!(Rt::Percent(f32::NAN).as_threads(), total_threads);
|
||||
/// assert_eq!(Rt::Percent(f32::INFINITY).as_threads(), total_threads);
|
||||
/// assert_eq!(Rt::Percent(f32::NEG_INFINITY).as_threads(), total_threads);
|
||||
/// assert_eq!(R::Percent(0.01).as_threads().get(), 1);
|
||||
/// assert_eq!(R::Percent(0.0).as_threads(), total_threads);
|
||||
/// assert_eq!(R::Percent(1.0).as_threads(), total_threads);
|
||||
/// assert_eq!(R::Percent(f32::NAN).as_threads(), total_threads);
|
||||
/// assert_eq!(R::Percent(f32::INFINITY).as_threads(), total_threads);
|
||||
/// assert_eq!(R::Percent(f32::NEG_INFINITY).as_threads(), total_threads);
|
||||
///
|
||||
/// // Percentage only works on more than 1 thread.
|
||||
/// if total_threads.get() > 1 {
|
||||
/// assert_eq!(
|
||||
/// Rt::Percent(0.5).as_threads().get(),
|
||||
/// R::Percent(0.5).as_threads().get(),
|
||||
/// (total_threads.get() as f32 / 2.0) as usize,
|
||||
/// );
|
||||
/// }
|
||||
|
|
|
@ -6,7 +6,7 @@ use cuprate_database::{ConcreteEnv, Env, EnvInner, InitError, RuntimeError, TxRw
|
|||
use crate::{config::Config, tables::OpenTables};
|
||||
|
||||
//---------------------------------------------------------------------------------------------------- Free functions
|
||||
/// Open the blockchain database, using the passed [`Config`].
|
||||
/// Open the blockchain database using the passed [`Config`].
|
||||
///
|
||||
/// This calls [`cuprate_database::Env::open`] and prepares the
|
||||
/// database to be ready for blockchain-related usage, e.g.
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
//! Blocks functions.
|
||||
//! Block functions.
|
||||
|
||||
//---------------------------------------------------------------------------------------------------- Import
|
||||
use bytemuck::TransparentWrapper;
|
||||
|
|
|
@ -5,14 +5,14 @@
|
|||
//! database operations.
|
||||
//!
|
||||
//! # `impl Table`
|
||||
//! `ops/` functions take [`Tables`](crate::tables::Tables) and
|
||||
//! Functions in this module take [`Tables`](crate::tables::Tables) and
|
||||
//! [`TablesMut`](crate::tables::TablesMut) directly - these are
|
||||
//! _already opened_ database tables.
|
||||
//!
|
||||
//! As such, the function puts the responsibility
|
||||
//! of transactions, tables, etc on the caller.
|
||||
//! As such, the responsibility of
|
||||
//! transactions, tables, etc, are on the caller.
|
||||
//!
|
||||
//! This does mean these functions are mostly as lean
|
||||
//! Notably, this means that these functions are as lean
|
||||
//! as possible, so calling them in a loop should be okay.
|
||||
//!
|
||||
//! # Atomicity
|
||||
|
|
|
@ -6,10 +6,10 @@ For a high-level overview, see the database section in
|
|||
[Cuprate's architecture book](https://architecture.cuprate.org).
|
||||
|
||||
If you need blockchain specific capabilities, consider using the higher-level
|
||||
`cuprate-blockchain` crate which builds upon this one.
|
||||
[`cuprate-blockchain`](https://doc.cuprate.org/cuprate_blockchain) crate which builds upon this one.
|
||||
|
||||
# Purpose
|
||||
This crate abstracts various database backends with traits. The databases are:
|
||||
This crate abstracts various database backends with traits.
|
||||
|
||||
All backends have the following attributes:
|
||||
- [Embedded](https://en.wikipedia.org/wiki/Embedded_database)
|
||||
|
@ -19,6 +19,10 @@ All backends have the following attributes:
|
|||
- Are table oriented (`"table_name" -> (key, value)`)
|
||||
- Allows concurrent readers
|
||||
|
||||
The currently implemented backends are:
|
||||
- [`heed`](https://github.com/meilisearch/heed) (LMDB)
|
||||
- [`redb`](https://github.com/cberner/redb)
|
||||
|
||||
# Terminology
|
||||
To be more clear on some terms used in this crate:
|
||||
|
||||
|
@ -26,17 +30,17 @@ To be more clear on some terms used in this crate:
|
|||
|------------------|--------------------------------------|
|
||||
| `Env` | The 1 database environment, the "whole" thing
|
||||
| `DatabaseR{o,w}` | A _actively open_ readable/writable `key/value` store
|
||||
| `Table` | Solely the metadata of a `cuprate_database` (the `key` and `value` types, and the name)
|
||||
| `Table` | Solely the metadata of a `Database` (the `key` and `value` types, and the name)
|
||||
| `TxR{o,w}` | A read/write transaction
|
||||
| `Storable` | A data that type can be stored in the database
|
||||
| `Storable` | A data type that can be stored in the database
|
||||
|
||||
The dataflow is `Env` -> `Tx` -> `cuprate_database`
|
||||
The flow is `Env` -> `Tx` -> `Database`
|
||||
|
||||
Which reads as:
|
||||
1. You have a database `Environment`
|
||||
1. You open up a `Transaction`
|
||||
1. You open a particular `Table` from that `Environment`, getting a `cuprate_database`
|
||||
1. You can now read/write data from/to that `cuprate_database`
|
||||
1. You open a particular `Table` from that `Environment`, getting a `Database`
|
||||
1. You can now read/write data from/to that `Database`
|
||||
|
||||
# Concrete types
|
||||
You should _not_ rely on the concrete type of any abstracted backend.
|
||||
|
|
|
@ -160,7 +160,7 @@ pub struct Config {
|
|||
/// Set the number of slots in the reader table.
|
||||
///
|
||||
/// This is only used in LMDB, see
|
||||
/// <https://github.com/LMDB/lmdb/blob/b8e54b4c31378932b69f1298972de54a565185b1/libraries/liblmdb/mdb.c#L794-L799>.
|
||||
/// [here](https://github.com/LMDB/lmdb/blob/b8e54b4c31378932b69f1298972de54a565185b1/libraries/liblmdb/mdb.c#L794-L799).
|
||||
///
|
||||
/// By default, this value is [`READER_THREADS_DEFAULT`].
|
||||
pub reader_threads: NonZeroUsize,
|
||||
|
|
|
@ -127,8 +127,8 @@ pub enum SyncMode {
|
|||
/// In the case of a system crash, the database
|
||||
/// may become corrupted when using this option.
|
||||
///
|
||||
/// [^1]:
|
||||
/// Semantically, this variant would actually map to
|
||||
///
|
||||
/// [^1]: Semantically, this variant would actually map to
|
||||
/// [`redb::Durability::None`](https://docs.rs/redb/1.5.0/redb/enum.Durability.html#variant.None),
|
||||
/// however due to [`#149`](https://github.com/Cuprate/cuprate/issues/149),
|
||||
/// this is not possible. As such, when using the `redb` backend,
|
||||
|
|
|
@ -24,15 +24,14 @@ use crate::{
|
|||
///
|
||||
/// # Lifetimes
|
||||
/// The lifetimes associated with `Env` have a sequential flow:
|
||||
/// 1. `ConcreteEnv`
|
||||
/// 2. `'env`
|
||||
/// 3. `'tx`
|
||||
/// 4. `'db`
|
||||
/// ```text
|
||||
/// Env -> Tx -> Database
|
||||
/// ```
|
||||
///
|
||||
/// As in:
|
||||
/// - open database tables only live as long as...
|
||||
/// - transactions which only live as long as the...
|
||||
/// - environment ([`EnvInner`])
|
||||
/// - database environment
|
||||
pub trait Env: Sized {
|
||||
//------------------------------------------------ Constants
|
||||
/// Does the database backend need to be manually
|
||||
|
@ -202,18 +201,19 @@ Subsequent table opens will follow the flags/ordering, but only if
|
|||
/// Note that when opening tables with [`EnvInner::open_db_ro`],
|
||||
/// they must be created first or else it will return error.
|
||||
///
|
||||
/// Note that when opening tables with [`EnvInner::open_db_ro`],
|
||||
/// they must be created first or else it will return error.
|
||||
///
|
||||
/// See [`EnvInner::create_db`] for creating tables.
|
||||
///
|
||||
/// # Invariant
|
||||
#[doc = doc_heed_create_db_invariant!()]
|
||||
pub trait EnvInner<'env> {
|
||||
/// The read-only transaction type of the backend.
|
||||
type Ro<'a>: TxRo<'a>;
|
||||
///
|
||||
/// `'tx` is the lifetime of the transaction itself.
|
||||
type Ro<'tx>: TxRo<'tx>;
|
||||
/// The read-write transaction type of the backend.
|
||||
type Rw<'a>: TxRw<'a>;
|
||||
///
|
||||
/// `'tx` is the lifetime of the transaction itself.
|
||||
type Rw<'tx>: TxRw<'tx>;
|
||||
|
||||
/// Create a read-only transaction.
|
||||
///
|
||||
|
@ -235,11 +235,37 @@ pub trait EnvInner<'env> {
|
|||
/// 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)
|
||||
/// ```rust
|
||||
/// # use cuprate_database::{
|
||||
/// # ConcreteEnv,
|
||||
/// # config::ConfigBuilder,
|
||||
/// # Env, EnvInner,
|
||||
/// # DatabaseRo, DatabaseRw, TxRo, TxRw,
|
||||
/// # };
|
||||
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
/// # let tmp_dir = tempfile::tempdir()?;
|
||||
/// # let db_dir = tmp_dir.path().to_owned();
|
||||
/// # let config = ConfigBuilder::new(db_dir.into()).build();
|
||||
/// # let env = ConcreteEnv::open(config)?;
|
||||
/// #
|
||||
/// # struct Table;
|
||||
/// # impl cuprate_database::Table for Table {
|
||||
/// # const NAME: &'static str = "table";
|
||||
/// # type Key = u8;
|
||||
/// # type Value = u64;
|
||||
/// # }
|
||||
/// #
|
||||
/// # let env_inner = env.env_inner();
|
||||
/// # let tx_rw = env_inner.tx_rw()?;
|
||||
/// # env_inner.create_db::<Table>(&tx_rw)?;
|
||||
/// # TxRw::commit(tx_rw);
|
||||
/// #
|
||||
/// # let tx_ro = env_inner.tx_ro()?;
|
||||
/// let db = env_inner.open_db_ro::<Table>(&tx_ro);
|
||||
/// // ^ ^
|
||||
/// // database table table metadata
|
||||
/// // (name, key/value type)
|
||||
/// # Ok(()) }
|
||||
/// ```
|
||||
///
|
||||
/// # Errors
|
||||
|
|
|
@ -59,18 +59,16 @@ pub enum InitError {
|
|||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------------------- RuntimeError
|
||||
/// Errors that occur _after_ successful ([`Env::open`](crate::env::Env::open)).
|
||||
/// Errors that occur _after_ successful [`Env::open`](crate::env::Env::open).
|
||||
///
|
||||
/// There are no errors for:
|
||||
/// 1. Missing tables
|
||||
/// 2. (De)serialization
|
||||
/// 3. Shutdown errors
|
||||
///
|
||||
/// as `cuprate_database` upholds the invariant that:
|
||||
///
|
||||
/// 1. All tables exist
|
||||
/// 2. (De)serialization never fails
|
||||
/// 3. The database (thread-pool) only shuts down when all channels are dropped
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
pub enum RuntimeError {
|
||||
/// The given key already existed in the database.
|
||||
|
|
|
@ -129,7 +129,7 @@ where
|
|||
///
|
||||
/// Slice types are owned both:
|
||||
/// - when returned from the database
|
||||
/// - in `put()`
|
||||
/// - in [`crate::DatabaseRw::put()`]
|
||||
///
|
||||
/// This is needed as `impl Storable for Vec<T>` runs into impl conflicts.
|
||||
///
|
||||
|
|
|
@ -8,6 +8,8 @@ use crate::{key::Key, storable::Storable};
|
|||
/// Database table metadata.
|
||||
///
|
||||
/// Purely compile time information for database tables.
|
||||
///
|
||||
/// See [`crate::define_tables`] for bulk table generation.
|
||||
pub trait Table: 'static {
|
||||
/// Name of the database table.
|
||||
const NAME: &'static str;
|
||||
|
|
|
@ -349,11 +349,18 @@ macro_rules! define_tables {
|
|||
/// Note that this is already implemented on [`cuprate_database::EnvInner`], thus:
|
||||
/// - You don't need to implement this
|
||||
/// - It can be called using `env_inner.open_tables()` notation
|
||||
///
|
||||
/// # Creation before opening
|
||||
/// As [`cuprate_database::EnvInner`] documentation states,
|
||||
/// tables must be created before they are opened.
|
||||
///
|
||||
/// I.e. [`OpenTables::create_tables`] must be called before
|
||||
/// [`OpenTables::open_tables`] or else panics may occur.
|
||||
pub trait OpenTables<'env> {
|
||||
/// The read-only transaction type of the backend.
|
||||
type Ro<'a>;
|
||||
type Ro<'tx>;
|
||||
/// The read-write transaction type of the backend.
|
||||
type Rw<'a>;
|
||||
type Rw<'tx>;
|
||||
|
||||
/// Open all tables in read/iter mode.
|
||||
///
|
||||
|
@ -362,11 +369,6 @@ macro_rules! define_tables {
|
|||
///
|
||||
/// # Errors
|
||||
/// This will only return [`cuprate_database::RuntimeError::Io`] if it errors.
|
||||
///
|
||||
/// # Invariant
|
||||
/// All tables should be created with a crate-specific open function.
|
||||
///
|
||||
/// TODO: explain why
|
||||
fn open_tables(&self, tx_ro: &Self::Ro<'_>) -> Result<impl TablesIter, $crate::RuntimeError>;
|
||||
|
||||
/// Open all tables in read-write mode.
|
||||
|
@ -391,8 +393,8 @@ macro_rules! define_tables {
|
|||
where
|
||||
Ei: $crate::EnvInner<'env>,
|
||||
{
|
||||
type Ro<'a> = <Ei as $crate::EnvInner<'env>>::Ro<'a>;
|
||||
type Rw<'a> = <Ei as $crate::EnvInner<'env>>::Rw<'a>;
|
||||
type Ro<'tx> = <Ei as $crate::EnvInner<'env>>::Ro<'tx>;
|
||||
type Rw<'tx> = <Ei as $crate::EnvInner<'env>>::Rw<'tx>;
|
||||
|
||||
fn open_tables(&self, tx_ro: &Self::Ro<'_>) -> Result<impl TablesIter, $crate::RuntimeError> {
|
||||
Ok(($(
|
||||
|
|
|
@ -11,7 +11,7 @@ use crate::error::RuntimeError;
|
|||
/// # Commit
|
||||
/// It's recommended but may not be necessary to call [`TxRo::commit`] in certain cases:
|
||||
/// - <https://docs.rs/heed/0.20.0-alpha.9/heed/struct.RoTxn.html#method.commit>
|
||||
pub trait TxRo<'env> {
|
||||
pub trait TxRo<'tx> {
|
||||
/// Commit the read-only transaction.
|
||||
///
|
||||
/// # Errors
|
||||
|
@ -23,7 +23,7 @@ pub trait TxRo<'env> {
|
|||
/// Read/write database transaction.
|
||||
///
|
||||
/// Returned from [`EnvInner::tx_rw`](crate::EnvInner::tx_rw).
|
||||
pub trait TxRw<'env> {
|
||||
pub trait TxRw<'tx> {
|
||||
/// Commit the read/write transaction.
|
||||
///
|
||||
/// Note that this doesn't necessarily sync the database caches to disk.
|
||||
|
|
Loading…
Reference in a new issue