mirror of
https://github.com/Cuprate/cuprate.git
synced 2025-01-27 04:45:57 +00:00
51d9ccd02d
* error: add `NeedsResize` accidently removed, was `MapFull` before. We need this because we as the writer thread must react to this error in order to resize. The writer thread doesn't have access to `heed::Error`, but `Runtime::Error`, so this variant must exist * env/backend: add `MANUAL_RESIZE` and `resize()` * heed: respect `ReadersFull`, comment errors * free: add `resize_memory_map()` * env: add `Env::current_map_size` * service: add `resize_map()` * database: make `Env` itself cheaply clonable + threadsafe `heed::Env` already uses `Arc` internally, but `sanakirja` does not, so abstract this at the `Env` level instead of wrapping `ConcreteEnv` in `Arc` ourselves. * service: `Arc<ConcreteEnv>` -> `ConcreteEnv: Clone` * env: add `SYNCS_PER_TX`, `path()`, `shutdown()` * database: add `src/service/state.rs` * service: add to `state.rs`, add `DatabaseState` to readers/writer * add `parking_lot` Okay, turns out we need to take locks in `database/src/service/state.rs`... `std`'s lock fairness policies are not well defined and depend on the OS implementation, `parking_lot` on the other hand has a fairness policy which is important when the writer needs the lock but readers keep pouring in, essentially never letting the writer do stuff. * state: use `crossbeam::atomic::AtomicCell` We have crossbeam as a dep anyway. * heed: `heed::Env` -> `Arc<RwLock<heed::Env>>` * service: add reader shutdown handle, use `Select` for msgs * service: remove `state.rs` We don't need this, we will represent shutdowns with channel messages and `Select`, and mutual exclusion with a `RwLock`. * service: fix misc reader/writer stuff * database: add `config.rs` * service: use `ReaderThreads` when spawning readers * service: impl `shutdown()` for readers/writer * service: return `DatabaseReaderReceivers` on shutdown via `JoinHandle` Solves the issue of unfortunately timed `Request`s that come in _right_ as we are shutting down. If we (Cuprate) drop the database channels too early the requesting thread will probably panic as they probably use `.unwrap()`, never expecting a channel failure. Returning this structure containing all the channels allows the final shutdown code to carry these channels until the very end of the program, at which point, all threads exit - no panics. * remove `parking_lot` Could be used as the database mutual exclusion lock. Needs to be tested against `std`. * config: add `path` * env: `path()` -> `config()`, backend: impl `Drop` * `Arc<ConcreteEnv>`, shutdown `service` on channel disconnect * backend: add `Config` to `ConcreteEnv` * service: use `std:🧵:Builder` for readers/writer * config: `PathBuf` -> `Cow<'static, Path>` * misc docs, remove `RuntimeError::ShuttingDown` * service: init & shutdown docs * heed: impl `Env::resize_map()` * heed: impl `Env::current_map_size()` * lib.rs: add example crate usage test * heed: `RwLock` comment * helper: add `cuprate_database_dir()` * config: use `cuprate_database_dir()` * lib.rs: TODO example test * database: add `page_size` The database memory map size must be a multiple of the OS page size. Why doesn't `heed` re-expose this? It calls it when checking anyway... https://docs.rs/heed/0.20.0-alpha.9/src/heed/env.rs.html#772 * free: impl `resize_memory_map()` * free: docs * env: add `disk_size_bytes()` * config: impl `From<$num>` for `ReaderThreads` * move `fs`-related constants to `cuprate_helper::fs` * docs * add `resize.rs`, `ResizeAlgorithm` * env: use `ResizeAlgorithm` in `resize_map()` * TODO: account for LMDB reader limit * resize: docs, add `page_size()`, impl `fixed_bytes()` * resize: impl `percent()` * resize: docs * env: take `ResizeAlgorithm` by value (it impls `Copy`) * heed: TODO for `MDB_MAP_FULL` & `MDB_MAP_RESIZED` * config: `From<Into<usize>>` for `ReaderThreads` Co-authored-by: Boog900 <boog900@tutanota.com> * env: move mutual exclusion doc to backend * free: update invariant doc Co-authored-by: Boog900 <boog900@tutanota.com> * Update database/src/service/mod.rs Co-authored-by: Boog900 <boog900@tutanota.com> * fix `[allow(unused_imports)] // docs` * move DB filename from `helper/` -> `database/` * config: use `DATABASE_FILENAME` * config: add `db_file_path()` * lib: add non-`service` usage invariant docs * table: seal `Table` trait, impl for all `crate::tables` * fix docs * fix docs pt.2 --------- Co-authored-by: Boog900 <boog900@tutanota.com>
91 lines
3.1 KiB
Rust
91 lines
3.1 KiB
Rust
//! Database error types.
|
|
//! TODO: `InitError/RuntimeError` are maybe bad names.
|
|
|
|
//---------------------------------------------------------------------------------------------------- Import
|
|
use std::fmt::Debug;
|
|
|
|
//---------------------------------------------------------------------------------------------------- Types
|
|
/// Alias for a thread-safe boxed error.
|
|
type BoxError = Box<dyn std::error::Error + Send + Sync + 'static>;
|
|
|
|
//---------------------------------------------------------------------------------------------------- InitError
|
|
/// Errors that occur during ([`Env::open`](crate::env::Env::open)).
|
|
///
|
|
/// # 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 {
|
|
/// The given `Path/File` existed and was accessible,
|
|
/// but was not a valid database file.
|
|
#[error("database file exists but is not valid")]
|
|
Invalid,
|
|
|
|
/// The given `Path/File` existed, was a valid
|
|
/// database, but the version is incorrect.
|
|
#[error("database file is valid, but version is incorrect")]
|
|
InvalidVersion,
|
|
|
|
/// 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(BoxError),
|
|
}
|
|
|
|
//---------------------------------------------------------------------------------------------------- RuntimeError
|
|
/// 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.
|
|
#[error("key already existed")]
|
|
KeyExists,
|
|
|
|
/// The given key did not exist in the database.
|
|
#[error("key/value pair was not found")]
|
|
KeyNotFound,
|
|
|
|
/// The database memory map is full and needs a resize.
|
|
///
|
|
/// # Invariant
|
|
/// This error can only occur if [`Env::MANUAL_RESIZE`](crate::Env::MANUAL_RESIZE) is `true`.
|
|
#[error("database memory map must be resized")]
|
|
ResizeNeeded,
|
|
|
|
/// A [`std::io::Error`].
|
|
#[error("I/O error: {0}")]
|
|
Io(#[from] std::io::Error),
|
|
}
|