database: SyncMode changes ()

apply
This commit is contained in:
hinto-janai 2025-01-29 09:41:30 -05:00 committed by GitHub
parent d8d1e34266
commit 7e8e62135c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 22 additions and 88 deletions
binaries/cuprated
storage/database/src
backend
heed
redb
config

View file

@ -54,11 +54,11 @@ check_client_pool_interval = { secs = 30, nanos = 0 }
## Txpool storage config.
[storage.txpool]
## The database sync mode for the txpool.
sync_mode = "Async"
sync_mode = "Fast"
## The maximum size of all the txs in the pool (bytes).
max_txpool_byte_size = 100_000_000
## Blockchain storage config.
[storage.blockchain]
## The database sync mode for the blockchain.
sync_mode = "Async"
sync_mode = "Fast"

View file

@ -29,23 +29,13 @@ impl Default for StorageConfig {
}
/// The blockchain config.
#[derive(Debug, Deserialize, Serialize, PartialEq, Eq)]
#[derive(Default, Debug, Deserialize, Serialize, PartialEq, Eq)]
#[serde(deny_unknown_fields, default)]
pub struct BlockchainConfig {
#[serde(flatten)]
pub shared: SharedStorageConfig,
}
impl Default for BlockchainConfig {
fn default() -> Self {
Self {
shared: SharedStorageConfig {
sync_mode: SyncMode::Async,
},
}
}
}
/// The tx-pool config.
#[derive(Debug, Deserialize, Serialize, PartialEq, Eq)]
#[serde(deny_unknown_fields, default)]
@ -60,9 +50,7 @@ pub struct TxpoolConfig {
impl Default for TxpoolConfig {
fn default() -> Self {
Self {
shared: SharedStorageConfig {
sync_mode: SyncMode::Async,
},
shared: SharedStorageConfig::default(),
max_txpool_byte_size: 100_000_000,
}
}

View file

@ -122,10 +122,10 @@ impl Env for ConcreteEnv {
// <https://github.com/monero-project/monero/blob/059028a30a8ae9752338a7897329fe8012a310d5/src/blockchain_db/lmdb/db_lmdb.cpp#L1324>
let flags = match config.sync_mode {
SyncMode::Safe => EnvFlags::empty(),
SyncMode::Async => EnvFlags::MAP_ASYNC,
SyncMode::Fast => EnvFlags::NO_SYNC | EnvFlags::WRITE_MAP | EnvFlags::MAP_ASYNC,
// SOMEDAY: dynamic syncs are not implemented.
SyncMode::FastThenSafe | SyncMode::Threshold(_) => unimplemented!(),
// TODO: impl `FastThenSafe`
SyncMode::FastThenSafe | SyncMode::Fast => {
EnvFlags::NO_SYNC | EnvFlags::WRITE_MAP | EnvFlags::MAP_ASYNC
}
};
// SAFETY: the flags we're setting are 'unsafe'

View file

@ -52,15 +52,9 @@ impl Env for ConcreteEnv {
fn open(config: Config) -> Result<Self, InitError> {
// SOMEDAY: dynamic syncs are not implemented.
let durability = match config.sync_mode {
// FIXME: There's also `redb::Durability::Paranoid`:
// <https://docs.rs/redb/1.5.0/redb/enum.Durability.html#variant.Paranoid>
// should we use that instead of Immediate?
SyncMode::Safe => redb::Durability::Immediate,
// FIXME: `Fast` maps to `Eventual` instead of `None` because of:
// <https://github.com/Cuprate/cuprate/issues/149>
SyncMode::Async | SyncMode::Fast => redb::Durability::Eventual,
// SOMEDAY: dynamic syncs are not implemented.
SyncMode::FastThenSafe | SyncMode::Threshold(_) => unimplemented!(),
// TODO: impl `FastThenSafe`
SyncMode::FastThenSafe | SyncMode::Fast => redb::Durability::Eventual,
};
let env_builder = redb::Builder::new();

View file

@ -19,41 +19,15 @@ use serde::{Deserialize, Serialize};
///
/// Regardless of the variant chosen, dropping [`Env`](crate::Env)
/// will always cause it to fully sync to disk.
///
/// # Sync vs Async
/// All invariants except [`SyncMode::Async`] & [`SyncMode::Fast`]
/// are `synchronous`, as in the database will wait until the OS has
/// finished syncing all the data to disk before continuing.
///
/// `SyncMode::Async` & `SyncMode::Fast` are `asynchronous`, meaning
/// the database will _NOT_ wait until the data is fully synced to disk
/// before continuing. Note that this doesn't mean the database itself
/// won't be synchronized between readers/writers, but rather that the
/// data _on disk_ may not be immediately synchronized after a write.
///
/// Something like:
/// ```rust,ignore
/// db.put("key", value);
/// db.get("key");
/// ```
/// will be fine, most likely pulling from memory instead of disk.
///
/// # SOMEDAY
/// Dynamic sync's are not yet supported.
///
/// Only:
///
/// - [`SyncMode::Safe`]
/// - [`SyncMode::Async`]
/// - [`SyncMode::Fast`]
///
/// are supported, all other variants will panic on [`crate::Env::open`].
#[derive(Copy, Clone, Debug, Default, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum SyncMode {
/// Use [`SyncMode::Fast`] until fully synced,
/// then use [`SyncMode::Safe`].
///
/// # TODO
/// This is not implemented internally and has the same behavior as [`SyncMode::Fast`].
//
// # SOMEDAY: how to implement this?
// ref: <https://github.com/monero-project/monero/issues/1463>
// monerod-solution: <https://github.com/monero-project/monero/pull/1506>
@ -74,7 +48,6 @@ pub enum SyncMode {
// switch to safe mode, until then, go fast.
FastThenSafe,
#[default]
/// Fully sync to disk per transaction.
///
/// Every database transaction commit will
@ -88,27 +61,7 @@ pub enum SyncMode {
/// - [`redb::Durability::Immediate`](https://docs.rs/redb/1.5.0/redb/enum.Durability.html#variant.Immediate)
Safe,
/// Asynchrously sync to disk per transaction.
///
/// This is the same as [`SyncMode::Safe`],
/// but the syncs will be asynchronous, i.e.
/// each transaction commit will sync to disk,
/// but only eventually, not necessarily immediately.
///
/// This maps to:
/// - [`MDB_MAPASYNC`](http://www.lmdb.tech/doc/group__mdb__env.html#gab034ed0d8e5938090aef5ee0997f7e94)
/// - [`redb::Durability::Eventual`](https://docs.rs/redb/1.5.0/redb/enum.Durability.html#variant.Eventual)
Async,
/// Fully sync to disk after we cross this transaction threshold.
///
/// After committing [`usize`] amount of database
/// transactions, it will be sync to disk.
///
/// `0` behaves the same as [`SyncMode::Safe`], and a ridiculously large
/// number like `usize::MAX` is practically the same as [`SyncMode::Fast`].
Threshold(usize),
#[default]
/// Only flush at database shutdown.
///
/// This is the fastest, yet unsafest option.
@ -117,26 +70,25 @@ pub enum SyncMode {
/// letting the OS decide when to flush data to disk[^1].
///
/// This maps to:
/// - [`MDB_NOSYNC`](http://www.lmdb.tech/doc/group__mdb__env.html#ga5791dd1adb09123f82dd1f331209e12e) + [`MDB_MAPASYNC`](http://www.lmdb.tech/doc/group__mdb__env.html#gab034ed0d8e5938090aef5ee0997f7e94)
/// - [`MDB_NOSYNC | MDB_WRITEMAP | MDB_MAPASYNC`](https://github.com/monero-project/monero/blob/90359e31fd657251cb357ecba02c4de2442d1b5c/src/blockchain_db/lmdb/db_lmdb.cpp#L1444)
/// - [`redb::Durability::Eventual`](https://docs.rs/redb/1.5.0/redb/enum.Durability.html#variant.Eventual)
///
/// [`monerod` reference](https://github.com/monero-project/monero/blob/7b7958bbd9d76375c47dc418b4adabba0f0b1785/src/blockchain_db/lmdb/db_lmdb.cpp#L1380-L1381).
/// # Default
/// This is the default [`SyncMode`].
/// ```rust
/// use cuprate_database::config::SyncMode;
///
/// assert_eq!(SyncMode::default(), SyncMode::Fast);
/// ```
///
/// # Corruption
/// In the case of a system crash, the database
/// may become corrupted when using this option.
///
///
/// [^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,
/// transaction writes "should be persistent some time after `WriteTransaction::commit` returns."
/// Thus, [`SyncMode::Async`] will map to the same `redb::Durability::Eventual` as [`SyncMode::Fast`].
//
// FIXME: we could call this `unsafe`
// and use that terminology in the config file
// so users know exactly what they are getting
// themselves into.
Fast,
}