mirror of
https://github.com/Cuprate/cuprate.git
synced 2024-11-16 15:58:17 +00:00
database: Errors (#62)
* error: add variants to `RuntimeError`
* error: remove `<BackendError>` generic
we can just map each backend error variant <-> our error as needed
* backend: impl `From<heed::Error>` for `RuntimeError`
* add `Never` type to allow foreign trait implementations
This is a newtype to workaround `sanakirja::Storable` not being
able to be implemented on `std::convert::Infallible` due to
"foreign trait on foreign type" rules.
* revert 0342848
, fix `sanakirja` trait bounds
K/V will always be `[u8]`, not the concrete type
so it does not need to be bounded.
* backend: fix `sanakijra` K/V generics
* sanakirja: add `error.rs`
* error: remove serde traits
* heed: add `todo!()` for error mappings
* error: add `Corrupt` variant
* sanakirja: finish error mappings
* heed: finish error mappings
* error: add to error types
* env: use `InitError` in `Env::open()`
* error: docs
* heed: remove `serde.rs`
Not needed if all K/V's stored are `[u8]`.
* heed: use `heed::types::Bytes` as K/V
* database: docs
* heed: impl `From<heed::Error>` for `InitError`
* sanakirja: impl `From<sanakirja::Error>` for `InitError`
* error: fix doc warnings
* heed: fix `clippy::match_same_arms` in error match
* readme: add TODO
* error: add `BoxError`, and fatal/unknown variants
* heed: use `Fatal/Unknown` variants in errors
* sanakirja: use `Fatal/Unknown` variants in errors
* clippy
* sanakijra: remove `fallible_impl_from`
* error: remove `RuntimeError::InvalidVersion`
* error: remove `RuntimeError` variants that should panic
* error: remove `InitError::Fatal`
We will exit on all errors regardless.
Any non-enumrated variants will use `InitError::Unknown`.
* heed: fix error mappings
* constants: add `CUPRATE_DATABASE_CORRUPT_MSG`
* sanakijra: fix error mappings
* heed: fix import
* comments/docs
* key: fix docs
This commit is contained in:
parent
475c8c5ac0
commit
f7bd1304e2
19 changed files with 360 additions and 166 deletions
|
@ -30,4 +30,4 @@ tower = { workspace = true, features = ["full"], optional = true }
|
|||
borsh = { workspace = true, optional = true }
|
||||
heed = { git = "https://github.com/Cuprate/heed", rev = "5aa75b7", optional = true }
|
||||
sanakirja = { version = "1.4.0", optional = true }
|
||||
serde = { workspace = true, optional = true }
|
||||
serde = { workspace = true, optional = true }
|
||||
|
|
|
@ -1,10 +1,13 @@
|
|||
# Database
|
||||
Cuprate's database implementation.
|
||||
|
||||
TODO: document `Pod` and how databases use (de)serialize objects when storing/fetching, essentially using `<[u8], [u8]>`.
|
||||
|
||||
<!-- Did you know markdown automatically increments number lists, even if they are all 1...? -->
|
||||
1. [Documentation](#documentation)
|
||||
1. [File Structure](#file-structure)
|
||||
- [`src/`](#src)
|
||||
- [`src/ops`](#src-ops)
|
||||
- [`src/service/`](#src-service)
|
||||
- [`src/backend/`](#src-backend)
|
||||
1. [Backends](#backends)
|
||||
|
@ -60,7 +63,7 @@ Note that `lib.rs/mod.rs` files are purely for re-exporting/visibility/lints, an
|
|||
## `src/`
|
||||
The top-level `src/` files.
|
||||
|
||||
| File/Folder | Purpose |
|
||||
| File | Purpose |
|
||||
|------------------|---------|
|
||||
| `constants.rs` | General constants used throughout `cuprate-database`
|
||||
| `database.rs` | Abstracted database; `trait Database`
|
||||
|
@ -73,15 +76,31 @@ The top-level `src/` files.
|
|||
| `tables.rs` | All the table definitions used by `cuprate-database`
|
||||
| `transaction.rs` | Database transaction abstraction; `trait RoTx`, `trait RwTx`
|
||||
|
||||
## `src/ops/`
|
||||
This folder contains the `cupate_database::ops` module.
|
||||
|
||||
TODO: more detailed descriptions.
|
||||
|
||||
| File | Purpose |
|
||||
|-----------------|---------|
|
||||
| `alt_block.rs` | Alternative blocks
|
||||
| `block.rs` | Blocks
|
||||
| `blockchain.rs` | Blockchain-related
|
||||
| `output.rs` | Outputs
|
||||
| `property.rs` | Properties
|
||||
| `spent_key.rs` | Spent keys
|
||||
| `tx.rs` | Transactions
|
||||
|
||||
## `src/service/`
|
||||
This folder contains the `cupate_database::service` module.
|
||||
|
||||
| File/Folder | Purpose |
|
||||
| File | Purpose |
|
||||
|----------------|---------|
|
||||
| `free.rs` | General free functions used (related to `cuprate_database::service`)
|
||||
| `read.rs` | Read thread-pool definitions and logic
|
||||
| `request.rs` | Read/write `Request`s to the database
|
||||
| `response.rs` | Read/write `Response`'s from the database
|
||||
| `tests.rs` | Thread-pool tests and test helper functions
|
||||
| `write.rs` | Write thread-pool definitions and logic
|
||||
|
||||
## `src/backend/`
|
||||
|
@ -89,25 +108,20 @@ This folder contains the actual database crates used as the backend for `cuprate
|
|||
|
||||
Each backend has its own folder.
|
||||
|
||||
| File/Folder | Purpose |
|
||||
| Folder | Purpose |
|
||||
|--------------|---------|
|
||||
| `heed/` | Backend using using forked [`heed`](https://github.com/Cuprate/heed)
|
||||
| `sanakirja/` | Backend using [`sanakirja`](https://docs.rs/sanakirja)
|
||||
|
||||
### `src/backend/heed/`
|
||||
| File/Folder | Purpose |
|
||||
|------------------|---------|
|
||||
| `database.rs` | Implementation of `trait Database`
|
||||
| `env.rs` | Implementation of `trait Env`
|
||||
| `serde.rs` | Data (de)serialization implementations
|
||||
| `transaction.rs` | Implementation of `trait RoTx/RwTx`
|
||||
All backends follow the same file structure:
|
||||
|
||||
### `src/backend/sanakirja/`
|
||||
| File/Folder | Purpose |
|
||||
| File | Purpose |
|
||||
|------------------|---------|
|
||||
| `database.rs` | Implementation of `trait Database`
|
||||
| `env.rs` | Implementation of `trait Env`
|
||||
| `error.rs` | Implementation of backend's errors to `cuprate_database`'s error types
|
||||
| `transaction.rs` | Implementation of `trait RoTx/RwTx`
|
||||
| `types.rs` | Type aliases for long backend-specific types
|
||||
|
||||
# Backends
|
||||
`cuprate-database`'s `trait`s abstract over various actual databases.
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
//! Implementation of `trait Database` for `heed`.
|
||||
|
||||
//---------------------------------------------------------------------------------------------------- Import
|
||||
use crate::{database::Database, error::RuntimeError, table::Table};
|
||||
use crate::{backend::heed::types::HeedDb, database::Database, error::RuntimeError, table::Table};
|
||||
|
||||
//---------------------------------------------------------------------------------------------------- Database Impls
|
||||
impl<T: Table> Database<T> for heed::Database<T::Key, T::Value> {
|
||||
impl<T: Table> Database<T> for HeedDb {
|
||||
type RoTx<'db> = heed::RoTxn<'db>;
|
||||
type RwTx<'db> = heed::RwTxn<'db>;
|
||||
|
||||
|
|
|
@ -3,7 +3,13 @@
|
|||
//---------------------------------------------------------------------------------------------------- Import
|
||||
use std::path::Path;
|
||||
|
||||
use crate::{database::Database, env::Env, error::RuntimeError, table::Table};
|
||||
use crate::{
|
||||
backend::heed::types::HeedDb,
|
||||
database::Database,
|
||||
env::Env,
|
||||
error::{InitError, RuntimeError},
|
||||
table::Table,
|
||||
};
|
||||
|
||||
//---------------------------------------------------------------------------------------------------- Env
|
||||
/// A strongly typed, concrete database environment, backed by `heed`.
|
||||
|
@ -23,7 +29,7 @@ impl Env for ConcreteEnv {
|
|||
/// TODO
|
||||
/// # Errors
|
||||
/// TODO
|
||||
fn open<P: AsRef<Path>>(path: P) -> Result<Self, RuntimeError> {
|
||||
fn open<P: AsRef<Path>>(path: P) -> Result<Self, InitError> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
|
@ -70,7 +76,7 @@ impl Env for ConcreteEnv {
|
|||
&self,
|
||||
to_rw: &Self::RoTx<'_>,
|
||||
) -> Result<impl Database<T>, RuntimeError> {
|
||||
let tx: heed::Database<T::Key, T::Value> = todo!();
|
||||
let tx: HeedDb = todo!();
|
||||
Ok(tx)
|
||||
}
|
||||
}
|
||||
|
|
131
database/src/backend/heed/error.rs
Normal file
131
database/src/backend/heed/error.rs
Normal file
|
@ -0,0 +1,131 @@
|
|||
//! Conversion from `heed::Error` -> `cuprate_database::RuntimeError`.
|
||||
|
||||
//---------------------------------------------------------------------------------------------------- Use
|
||||
use crate::constants::CUPRATE_DATABASE_CORRUPT_MSG;
|
||||
|
||||
//---------------------------------------------------------------------------------------------------- InitError
|
||||
impl From<heed::Error> for crate::InitError {
|
||||
fn from(error: heed::Error) -> Self {
|
||||
use heed::Error as E1;
|
||||
use heed::MdbError as E2;
|
||||
|
||||
// Reference of all possible errors `heed` will return
|
||||
// upon using [`heed::EnvOpenOptions::open`]:
|
||||
// <https://docs.rs/heed/latest/src/heed/env.rs.html#149-219>
|
||||
match error {
|
||||
E1::Io(io_error) => Self::Io(io_error),
|
||||
E1::DatabaseClosing => Self::ShuttingDown,
|
||||
|
||||
// LMDB errors.
|
||||
E1::Mdb(mdb_error) => match mdb_error {
|
||||
E2::Invalid => Self::Invalid,
|
||||
E2::VersionMismatch => Self::InvalidVersion,
|
||||
E2::Other(c_int) => Self::Unknown(Box::new(mdb_error)),
|
||||
|
||||
// "Located page was wrong type".
|
||||
// <https://docs.rs/heed/latest/heed/enum.MdbError.html#variant.Corrupted>
|
||||
//
|
||||
// "Requested page not found - this usually indicates corruption."
|
||||
// <https://docs.rs/heed/latest/heed/enum.MdbError.html#variant.PageNotFound>
|
||||
E2::Corrupted | E2::PageNotFound => Self::Corrupt,
|
||||
|
||||
// These errors shouldn't be returned on database init.
|
||||
E2::Incompatible
|
||||
| E2::BadTxn
|
||||
| E2::Problem
|
||||
| E2::KeyExist
|
||||
| E2::NotFound
|
||||
| E2::MapFull
|
||||
| E2::ReadersFull
|
||||
| E2::PageFull
|
||||
| E2::DbsFull
|
||||
| E2::TlsFull
|
||||
| E2::TxnFull
|
||||
| E2::CursorFull
|
||||
| E2::MapResized
|
||||
| E2::BadRslot
|
||||
| E2::BadValSize
|
||||
| E2::BadDbi
|
||||
| E2::Panic => Self::Unknown(Box::new(mdb_error)),
|
||||
},
|
||||
|
||||
E1::InvalidDatabaseTyping
|
||||
| E1::BadOpenOptions { .. }
|
||||
| E1::Encoding(_)
|
||||
| E1::Decoding(_) => Self::Unknown(Box::new(error)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------------------- RuntimeError
|
||||
#[allow(clippy::fallible_impl_from)] // We need to panic sometimes.
|
||||
impl From<heed::Error> for crate::RuntimeError {
|
||||
/// # Panics
|
||||
/// This will panic on unrecoverable errors for safety.
|
||||
fn from(error: heed::Error) -> Self {
|
||||
use heed::Error as E1;
|
||||
use heed::MdbError as E2;
|
||||
|
||||
match error {
|
||||
// I/O errors.
|
||||
E1::Io(io_error) => Self::Io(io_error),
|
||||
|
||||
// LMDB errors.
|
||||
E1::Mdb(mdb_error) => match mdb_error {
|
||||
E2::KeyExist => Self::KeyExists,
|
||||
E2::NotFound => Self::KeyNotFound,
|
||||
|
||||
// Corruption errors, these have special panic messages.
|
||||
//
|
||||
// "Located page was wrong type".
|
||||
// <https://docs.rs/heed/latest/heed/enum.MdbError.html#variant.Corrupted>
|
||||
//
|
||||
// "Requested page not found - this usually indicates corruption."
|
||||
// <https://docs.rs/heed/latest/heed/enum.MdbError.html#variant.PageNotFound>
|
||||
E2::Corrupted | E2::PageNotFound => panic!("{mdb_error:?}\n{CUPRATE_DATABASE_CORRUPT_MSG}"),
|
||||
|
||||
// These errors should not occur, and if they do,
|
||||
// the best thing `cuprate_database` can do for
|
||||
// safety is to panic right here.
|
||||
E2::Panic
|
||||
| E2::PageFull
|
||||
| E2::Other(_)
|
||||
| E2::BadTxn
|
||||
| E2::Problem
|
||||
| E2::Invalid
|
||||
| E2::TlsFull
|
||||
| E2::TxnFull
|
||||
| E2::BadRslot
|
||||
| E2::VersionMismatch
|
||||
| E2::BadDbi => panic!("{mdb_error:?}"),
|
||||
|
||||
// These errors are the same as above, but instead
|
||||
// of being errors we can't control, these are errors
|
||||
// that only happen if we write incorrect code.
|
||||
E2::MapFull // Resize the map when needed.
|
||||
| E2::ReadersFull // Don't spawn too many reader threads.
|
||||
| E2::DbsFull // Don't create too many database tables.
|
||||
| E2::CursorFull // Don't do crazy multi-nested LMDB cursor stuff.
|
||||
| E2::MapResized // Resize the map when needed.
|
||||
| E2::Incompatible // <https://docs.rs/heed/0.20.0-alpha.9/heed/enum.MdbError.html#variant.Incompatible>
|
||||
| E2::BadValSize // Unsupported size of key/DB name/data, or wrong DUP_FIXED size.
|
||||
=> panic!("fix the database code! {mdb_error:?}"),
|
||||
},
|
||||
|
||||
// Database is shutting down.
|
||||
E1::DatabaseClosing => Self::ShuttingDown,
|
||||
|
||||
// Only if we write incorrect code.
|
||||
E1::InvalidDatabaseTyping
|
||||
| E1::BadOpenOptions { .. }
|
||||
| E1::Encoding(_)
|
||||
| E1::Decoding(_) => panic!("fix the database code! {error:?}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------------------- Tests
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
// use super::*;
|
||||
}
|
|
@ -4,6 +4,6 @@ mod env;
|
|||
pub use env::ConcreteEnv;
|
||||
|
||||
mod database;
|
||||
|
||||
mod serde;
|
||||
mod error;
|
||||
mod transaction;
|
||||
mod types;
|
||||
|
|
|
@ -1,53 +0,0 @@
|
|||
//! (De)serialization trait implementations for `heed`.
|
||||
|
||||
//---------------------------------------------------------------------------------------------------- Import
|
||||
use std::borrow::Cow;
|
||||
|
||||
use crate::pod::Pod;
|
||||
|
||||
//---------------------------------------------------------------------------------------------------- Serde
|
||||
/// Implement `heed` (de)serialization traits
|
||||
/// for anything that implements [`crate::pod::Pod`].
|
||||
///
|
||||
/// Blanket implementation breaks orphan impl rules, so this is used instead.
|
||||
macro_rules! impl_heed {
|
||||
($(
|
||||
$name:ident => // The name that implements [`crate::pod::Pod`]
|
||||
$t:ident // The type to (de)serialize into/from
|
||||
),* $(,)?) => {
|
||||
$(
|
||||
// `heed` Encode.
|
||||
impl<'a> heed::BytesEncode<'a> for $name {
|
||||
type EItem = $t;
|
||||
|
||||
#[inline]
|
||||
fn bytes_encode(item: &'a Self::EItem) -> Result<Cow<'a, [u8]>, heed::BoxedError> {
|
||||
Ok(item.into_bytes())
|
||||
}
|
||||
}
|
||||
|
||||
// `heed` Decode.
|
||||
impl<'a> heed::BytesDecode<'a> for $name {
|
||||
type DItem = $t;
|
||||
|
||||
#[inline]
|
||||
fn bytes_decode(bytes: &'a [u8]) -> Result<Self::DItem, heed::BoxedError> {
|
||||
Ok(Pod::from_bytes(bytes))
|
||||
}
|
||||
}
|
||||
)*
|
||||
};
|
||||
}
|
||||
|
||||
/// TODO
|
||||
struct Test;
|
||||
|
||||
impl_heed! {
|
||||
Test => u8,
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------------------- Tests
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
// use super::*;
|
||||
}
|
8
database/src/backend/heed/types.rs
Normal file
8
database/src/backend/heed/types.rs
Normal file
|
@ -0,0 +1,8 @@
|
|||
//! `heed` type aliases.
|
||||
|
||||
//---------------------------------------------------------------------------------------------------- Use
|
||||
use heed::{types::Bytes, Database};
|
||||
|
||||
//---------------------------------------------------------------------------------------------------- Types
|
||||
/// The concrete database type for `heed`.
|
||||
pub(super) type HeedDb = Database<Bytes, Bytes>;
|
|
@ -1,12 +1,14 @@
|
|||
//! Implementation of `trait Database` for `sanakirja`.
|
||||
|
||||
//---------------------------------------------------------------------------------------------------- Import
|
||||
use crate::{database::Database, error::RuntimeError, table::Table};
|
||||
use crate::{
|
||||
backend::sanakirja::types::SanakirjaDb, database::Database, error::RuntimeError, table::Table,
|
||||
};
|
||||
|
||||
//---------------------------------------------------------------------------------------------------- Database Impls
|
||||
impl<T: Table> Database<T> for sanakirja::btree::Db<T::Key, T::Value> {
|
||||
type RoTx<'db> = sanakirja::Txn<&'_ sanakirja::Env>;
|
||||
type RwTx<'db> = sanakirja::MutTxn<&'_ sanakirja::Env, ()>;
|
||||
impl<T: Table> Database<T> for SanakirjaDb {
|
||||
type RoTx<'db> = sanakirja::Txn<&'db sanakirja::Env>;
|
||||
type RwTx<'db> = sanakirja::MutTxn<&'db sanakirja::Env, ()>;
|
||||
|
||||
fn get(&self, ro_tx: &Self::RoTx<'_>, key: &T::Key) -> Result<Option<T::Value>, RuntimeError> {
|
||||
todo!()
|
||||
|
|
|
@ -3,7 +3,13 @@
|
|||
//---------------------------------------------------------------------------------------------------- Import
|
||||
use std::path::Path;
|
||||
|
||||
use crate::{database::Database, env::Env, error::RuntimeError, table::Table};
|
||||
use crate::{
|
||||
backend::sanakirja::types::SanakirjaDb,
|
||||
database::Database,
|
||||
env::Env,
|
||||
error::{InitError, RuntimeError},
|
||||
table::Table,
|
||||
};
|
||||
|
||||
//---------------------------------------------------------------------------------------------------- ConcreteEnv
|
||||
/// A strongly typed, concrete database environment, backed by `sanakirja`.
|
||||
|
@ -26,7 +32,7 @@ impl Env for ConcreteEnv {
|
|||
/// TODO
|
||||
/// # Errors
|
||||
/// TODO
|
||||
fn open<P: AsRef<Path>>(path: P) -> Result<Self, RuntimeError> {
|
||||
fn open<P: AsRef<Path>>(path: P) -> Result<Self, InitError> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
|
@ -72,9 +78,9 @@ impl Env for ConcreteEnv {
|
|||
fn open_database<T: Table>(
|
||||
&self,
|
||||
to_rw: &Self::RoTx<'_>,
|
||||
) -> Result<Option<impl Database<T>>, RuntimeError> {
|
||||
let tx: sanakirja::btree::Db<T::Key, T::Value> = todo!();
|
||||
Ok(Some(tx))
|
||||
) -> Result<impl Database<T>, RuntimeError> {
|
||||
let tx: SanakirjaDb = todo!();
|
||||
Ok(tx)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
51
database/src/backend/sanakirja/error.rs
Normal file
51
database/src/backend/sanakirja/error.rs
Normal file
|
@ -0,0 +1,51 @@
|
|||
//! Conversion from `sanakirja::Error` -> `cuprate_database::RuntimeError`.
|
||||
|
||||
//---------------------------------------------------------------------------------------------------- Import
|
||||
use crate::constants::CUPRATE_DATABASE_CORRUPT_MSG;
|
||||
|
||||
//---------------------------------------------------------------------------------------------------- InitError
|
||||
impl From<sanakirja::Error> for crate::InitError {
|
||||
fn from(error: sanakirja::Error) -> Self {
|
||||
use sanakirja::Error as E;
|
||||
|
||||
match error {
|
||||
E::IO(io_error) => Self::Io(io_error),
|
||||
E::VersionMismatch => Self::InvalidVersion,
|
||||
|
||||
// A CRC failure essentially means a `sanakirja` page was corrupt.
|
||||
// <https://docs.rs/sanakirja/latest/sanakirja/enum.Error.html#variant.CRC>
|
||||
E::Corrupt(_) | E::CRC(_) => Self::Corrupt,
|
||||
|
||||
// A database lock was poisoned.
|
||||
// <https://docs.rs/sanakirja/latest/sanakirja/enum.Error.html#variant.Poison>
|
||||
E::Poison => Self::Unknown(Box::new(error)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------------------- RuntimeError
|
||||
#[allow(clippy::fallible_impl_from)] // We need to panic sometimes.
|
||||
impl From<sanakirja::Error> for crate::RuntimeError {
|
||||
fn from(error: sanakirja::Error) -> Self {
|
||||
use sanakirja::Error as E;
|
||||
|
||||
match error {
|
||||
E::IO(io_error) => Self::Io(io_error),
|
||||
|
||||
// A CRC failure essentially means a `sanakirja` page was corrupt.
|
||||
// <https://docs.rs/sanakirja/latest/sanakirja/enum.Error.html#variant.CRC>
|
||||
E::Corrupt(_) | E::CRC(_) => panic!("{error:?}\n{CUPRATE_DATABASE_CORRUPT_MSG}"),
|
||||
|
||||
// These errors should not occur, and if they do,
|
||||
// the best thing `cuprate_database` can do for
|
||||
// safety is to panic right here.
|
||||
E::Poison | E::VersionMismatch => panic!("{error:?}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------------------- Tests
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
// use super::*;
|
||||
}
|
|
@ -3,6 +3,10 @@
|
|||
mod env;
|
||||
pub use env::ConcreteEnv;
|
||||
|
||||
mod error;
|
||||
|
||||
mod database;
|
||||
|
||||
mod transaction;
|
||||
|
||||
mod types;
|
||||
|
|
6
database/src/backend/sanakirja/types.rs
Normal file
6
database/src/backend/sanakirja/types.rs
Normal file
|
@ -0,0 +1,6 @@
|
|||
//! `sanakirja` type aliases.
|
||||
|
||||
//---------------------------------------------------------------------------------------------------- Types
|
||||
/// The concrete database type for `sanakirja`.
|
||||
pub(super) type SanakirjaDb =
|
||||
sanakirja::btree::Db_<[u8], [u8], sanakirja::btree::page_unsized::Page<[u8], [u8]>>;
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
//---------------------------------------------------------------------------------------------------- Import
|
||||
|
||||
//---------------------------------------------------------------------------------------------------- Constants
|
||||
//---------------------------------------------------------------------------------------------------- Directory/Files
|
||||
/// The directory that contains database-related files.
|
||||
///
|
||||
/// This is a sub-directory within the Cuprate folder, e.g:
|
||||
|
@ -25,6 +25,22 @@ pub const CUPRATE_DATABASE_DIR: &str = "database";
|
|||
/// ```
|
||||
pub const CUPRATE_DATABASE_FILE: &str = "data";
|
||||
|
||||
//---------------------------------------------------------------------------------------------------- Error Messages
|
||||
/// Corrupt database error message.
|
||||
///
|
||||
/// The error message shown to end-users in panic
|
||||
/// messages if we think the database is corrupted.
|
||||
///
|
||||
/// This is meant to be user-friendly.
|
||||
pub const CUPRATE_DATABASE_CORRUPT_MSG: &str = r"Cuprate has encountered a fatal error. The database may be corrupted.
|
||||
|
||||
TODO: instructions on:
|
||||
1. What to do
|
||||
2. How to fix (re-sync, recover, etc)
|
||||
3. General advice for preventing corruption
|
||||
4. etc";
|
||||
|
||||
//---------------------------------------------------------------------------------------------------- Misc
|
||||
cfg_if::cfg_if! {
|
||||
// If both backends are enabled, fallback to `heed`.
|
||||
// This is useful when using `--all-features`.
|
||||
|
|
|
@ -5,7 +5,7 @@ use std::path::Path;
|
|||
|
||||
use crate::{
|
||||
database::Database,
|
||||
error::RuntimeError,
|
||||
error::{InitError, RuntimeError},
|
||||
table::Table,
|
||||
transaction::{RoTx, RwTx},
|
||||
};
|
||||
|
@ -26,7 +26,7 @@ pub trait Env: Sized {
|
|||
/// TODO
|
||||
/// # Errors
|
||||
/// TODO
|
||||
fn open<P: AsRef<Path>>(path: P) -> Result<Self, RuntimeError>;
|
||||
fn open<P: AsRef<Path>>(path: P) -> Result<Self, InitError>;
|
||||
|
||||
/// TODO
|
||||
/// # Errors
|
||||
|
|
|
@ -2,61 +2,89 @@
|
|||
//! TODO: `InitError/RuntimeError` are maybe bad names.
|
||||
|
||||
//---------------------------------------------------------------------------------------------------- Import
|
||||
use std::{borrow::Cow, fmt::Debug};
|
||||
use std::fmt::Debug;
|
||||
|
||||
use crate::constants::DATABASE_BACKEND;
|
||||
#[allow(unused_imports)] // docs
|
||||
use crate::env::Env;
|
||||
|
||||
//---------------------------------------------------------------------------------------------------- Types
|
||||
/// Alias for a thread-safe boxed error.
|
||||
type BoxError = Box<dyn std::error::Error + Send + Sync + 'static>;
|
||||
|
||||
//---------------------------------------------------------------------------------------------------- InitError
|
||||
/// Database errors that occur during initialization.
|
||||
/// Errors that occur during ([`Env::open`]).
|
||||
///
|
||||
/// `BackendError` is an error specifically from the
|
||||
/// database backend being used. TODO: this may not
|
||||
/// be needed if we can convert all error types into
|
||||
/// "generic" database errors.
|
||||
/// # Handling
|
||||
/// As this is a database initialization error, the correct
|
||||
/// way to handle any of these occurring is probably just to
|
||||
/// exit the program.
|
||||
///
|
||||
/// There is not much we as Cuprate can do
|
||||
/// to recover from any of these errors.
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
pub enum InitError<BackendError: Debug> {
|
||||
/// TODO
|
||||
#[error("database PATH is inaccessible: {0}")]
|
||||
Path(std::io::Error),
|
||||
pub enum InitError {
|
||||
/// The given `Path/File` existed and was accessible,
|
||||
/// but was not a valid database file.
|
||||
#[error("database file exists but is not valid")]
|
||||
Invalid,
|
||||
|
||||
/// TODO
|
||||
#[error("{DATABASE_BACKEND} error: {0}")]
|
||||
Backend(BackendError),
|
||||
/// The given `Path/File` existed, was a valid
|
||||
/// database, but the version is incorrect.
|
||||
#[error("database file is valid, but version is incorrect")]
|
||||
InvalidVersion,
|
||||
|
||||
/// TODO
|
||||
/// I/O error.
|
||||
#[error("database I/O error: {0}")]
|
||||
Io(#[from] std::io::Error),
|
||||
|
||||
/// The given `Path/File` existed,
|
||||
/// was a valid database, but it is corrupt.
|
||||
#[error("database file is corrupt")]
|
||||
Corrupt,
|
||||
|
||||
/// The database is currently in the process
|
||||
/// of shutting down and cannot respond.
|
||||
///
|
||||
/// TODO: This might happen if we try to open
|
||||
/// while we are shutting down, `unreachable!()`?
|
||||
#[error("database is shutting down")]
|
||||
ShuttingDown,
|
||||
|
||||
/// An unknown error occurred.
|
||||
///
|
||||
/// This is for errors that cannot be recovered from,
|
||||
/// but we'd still like to panic gracefully.
|
||||
#[error("unknown error: {0}")]
|
||||
Unknown(Cow<'static, str>),
|
||||
Unknown(BoxError),
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------------------- RuntimeError
|
||||
/// Database errors that occur _after_ successful initialization.
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(
|
||||
feature = "borsh",
|
||||
derive(borsh::BorshSerialize, borsh::BorshDeserialize)
|
||||
)]
|
||||
#[derive(thiserror::Error, Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
|
||||
/// Errors that occur _after_ successful ([`Env::open`]).
|
||||
///
|
||||
/// There are no errors for:
|
||||
/// 1. Missing tables
|
||||
/// 2. (De)serialization
|
||||
///
|
||||
/// as `cuprate_database` upholds the invariant that:
|
||||
///
|
||||
/// 1. All tables exist
|
||||
/// 2. (De)serialization never fails
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
pub enum RuntimeError {
|
||||
// TODO: replace string with actual error type.
|
||||
///
|
||||
/// An error occurred when attempting to
|
||||
/// serialize the key data into bytes.
|
||||
#[error("serialize error: {0}")]
|
||||
Serialize(String),
|
||||
/// The given key already existed in the database.
|
||||
#[error("key already existed")]
|
||||
KeyExists,
|
||||
|
||||
// TODO: replace string with actual error type.
|
||||
///
|
||||
/// An error occurred when attempting to
|
||||
/// deserialize the response value from
|
||||
/// the database.
|
||||
#[error("deserialize error: {0}")]
|
||||
Deserialize(String),
|
||||
/// The given key did not exist in the database.
|
||||
#[error("key/value pair was not found")]
|
||||
KeyNotFound,
|
||||
|
||||
/// TODO
|
||||
///
|
||||
/// An unknown error occurred.
|
||||
#[error("unknown error: {0}")]
|
||||
Unknown(Cow<'static, str>),
|
||||
/// A [`std::io::Error`].
|
||||
#[error("I/O error: {0}")]
|
||||
Io(#[from] std::io::Error),
|
||||
|
||||
/// The database is currently in the process
|
||||
/// of shutting down and cannot respond.
|
||||
#[error("database is shutting down")]
|
||||
ShuttingDown,
|
||||
}
|
||||
|
|
|
@ -16,32 +16,15 @@ pub trait Key {
|
|||
/// If [`Key::DUPLICATE`] is `true`, [`Key::Secondary`] will contain
|
||||
/// the "subkey", or secondary key needed to access the actual value.
|
||||
///
|
||||
/// If [`Key::DUPLICATE`] is `false`, [`Key::Secondary`]
|
||||
/// will just be the same type as [`Key::Primary`].
|
||||
/// If [`Key::DUPLICATE`] is `false`, [`Key::Secondary`] is ignored.
|
||||
/// Consider using [`std::convert::Infallible`] as the type.
|
||||
const DUPLICATE: bool;
|
||||
|
||||
// TODO: fix this sanakirja bound.
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(all(feature = "sanakirja", not(feature = "heed")))] {
|
||||
/// The primary key type.
|
||||
type Primary: Pod + sanakirja::Storable;
|
||||
/// The primary key type.
|
||||
type Primary: Pod;
|
||||
|
||||
/// The secondary key type.
|
||||
///
|
||||
/// Only needs to be different than [`Key::Primary`]
|
||||
/// if [`Key::DUPLICATE`] is `true`.
|
||||
type Secondary: Pod + sanakirja::Storable;
|
||||
} else {
|
||||
/// The primary key type.
|
||||
type Primary: Pod;
|
||||
|
||||
/// The secondary key type.
|
||||
///
|
||||
/// Only needs to be different than [`Key::Primary`]
|
||||
/// if [`Key::DUPLICATE`] is `true`.
|
||||
type Secondary: Pod;
|
||||
}
|
||||
}
|
||||
/// The secondary key type.
|
||||
type Secondary: Pod;
|
||||
|
||||
/// Acquire [`Key::Primary`].
|
||||
fn primary(self) -> Self::Primary;
|
||||
|
@ -50,7 +33,7 @@ pub trait Key {
|
|||
///
|
||||
/// This only needs to be implemented on types that are [`Self::DUPLICATE`].
|
||||
///
|
||||
/// It is `unreachable!()` on non-duplicate key tables.
|
||||
/// Consider using [`unreachable!()`] on non-duplicate key tables.
|
||||
fn primary_secondary(self) -> (Self::Primary, Self::Secondary);
|
||||
}
|
||||
|
||||
|
@ -84,7 +67,9 @@ macro_rules! impl_key {
|
|||
$(
|
||||
impl Key for $t {
|
||||
const DUPLICATE: bool = false;
|
||||
|
||||
type Primary = $t;
|
||||
|
||||
// This 0 variant enum is unconstructable,
|
||||
// and "has the same role as the ! “never” type":
|
||||
// <https://doc.rust-lang.org/std/convert/enum.Infallible.html#future-compatibility>.
|
||||
|
@ -121,14 +106,11 @@ impl_key! {
|
|||
// Implement `Key` for any [`DupKey`] using [`Copy`] types.
|
||||
impl<P, S> Key for DupKey<P, S>
|
||||
where
|
||||
// TODO: fix sanakirja serde bound.
|
||||
P: Pod + Copy,
|
||||
S: Pod + Copy,
|
||||
{
|
||||
const DUPLICATE: bool = true;
|
||||
|
||||
type Primary = P;
|
||||
|
||||
type Secondary = S;
|
||||
|
||||
#[inline]
|
||||
|
|
|
@ -229,14 +229,15 @@ compile_error!("Cuprate is only compatible with 64-bit CPUs");
|
|||
//---------------------------------------------------------------------------------------------------- Public API
|
||||
// Import private modules, export public types.
|
||||
//
|
||||
// Documentation for each module is
|
||||
// located in the respective file.
|
||||
// Documentation for each module is located in the respective file.
|
||||
|
||||
mod backend;
|
||||
pub use backend::ConcreteEnv;
|
||||
|
||||
mod constants;
|
||||
pub use constants::{CUPRATE_DATABASE_DIR, CUPRATE_DATABASE_FILE, DATABASE_BACKEND};
|
||||
pub use constants::{
|
||||
CUPRATE_DATABASE_CORRUPT_MSG, CUPRATE_DATABASE_DIR, CUPRATE_DATABASE_FILE, DATABASE_BACKEND,
|
||||
};
|
||||
|
||||
mod database;
|
||||
pub use database::Database;
|
||||
|
|
|
@ -27,16 +27,8 @@ pub trait Table {
|
|||
/// Primary key type.
|
||||
type Key: Key;
|
||||
|
||||
// TODO: fix this sanakirja bound.
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(all(feature = "sanakirja", not(feature = "heed")))] {
|
||||
/// Value type.
|
||||
type Value: Pod + sanakirja::Storable;
|
||||
} else {
|
||||
/// Value type.
|
||||
type Value: Pod;
|
||||
}
|
||||
}
|
||||
/// Value type.
|
||||
type Value: Pod;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------------------- Tests
|
||||
|
|
Loading…
Reference in a new issue