Skip to content
46 changes: 44 additions & 2 deletions crates/node/core/src/args/database.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,12 @@ use crate::version::default_client_version;
use clap::{
builder::{PossibleValue, TypedValueParser},
error::ErrorKind,
Arg, Args, Command, Error,
value_parser, Arg, Args, Command, Error,
};
use reth_db::{
mdbx::{MaxReadTransactionDuration, SyncMode},
ClientVersion,
};
use reth_db::{mdbx::MaxReadTransactionDuration, ClientVersion};
use reth_storage_errors::db::LogLevel;

/// Parameters for database configuration
Expand All @@ -34,6 +37,12 @@ pub struct DatabaseArgs {
/// Maximum number of readers allowed to access the database concurrently.
#[arg(long = "db.max-readers")]
pub max_readers: Option<u64>,
/// Controls how aggressively the database synchronizes data to disk.
#[arg(
long = "db.sync-mode",
value_parser = value_parser!(SyncMode),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unsure if we even need this now that syncmode is fromstr

)]
pub sync_mode: Option<SyncMode>,
}

impl DatabaseArgs {
Expand Down Expand Up @@ -61,6 +70,7 @@ impl DatabaseArgs {
.with_geometry_max_size(self.max_size)
.with_growth_step(self.growth_step)
.with_max_readers(self.max_readers)
.with_sync_mode(self.sync_mode)
}
}

Expand Down Expand Up @@ -340,4 +350,36 @@ mod tests {
let cmd = CommandParser::<DatabaseArgs>::try_parse_from(["reth"]).unwrap();
assert_eq!(cmd.args.log_level, None);
}

#[test]
fn test_command_parser_with_valid_default_sync_mode() {
let cmd = CommandParser::<DatabaseArgs>::try_parse_from(["reth"]).unwrap();
assert!(cmd.args.sync_mode.is_none());
}

#[test]
fn test_command_parser_with_valid_sync_mode_durable() {
let cmd =
CommandParser::<DatabaseArgs>::try_parse_from(["reth", "--db.sync-mode", "durable"])
.unwrap();
assert!(matches!(cmd.args.sync_mode, Some(SyncMode::Durable)));
}

#[test]
fn test_command_parser_with_valid_sync_mode_safe_no_sync() {
let cmd = CommandParser::<DatabaseArgs>::try_parse_from([
"reth",
"--db.sync-mode",
"safe-no-sync",
])
.unwrap();
assert!(matches!(cmd.args.sync_mode, Some(SyncMode::SafeNoSync)));
}

#[test]
fn test_command_parser_with_invalid_sync_mode() {
let result =
CommandParser::<DatabaseArgs>::try_parse_from(["reth", "--db.sync-mode", "ultra-fast"]);
assert!(result.is_err());
}
}
28 changes: 27 additions & 1 deletion crates/storage/db/src/implementation/mdbx/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,22 @@ pub struct DatabaseArguments {
/// MDBX allows up to 32767 readers (`MDBX_READERS_LIMIT`). This arg is to configure the max
/// readers.
max_readers: Option<u64>,
/// Defines the synchronization strategy used by the MDBX database when writing data to disk.
///
/// This determines how aggressively MDBX ensures data durability versus prioritizing
/// performance. The available modes are:
///
/// - [`SyncMode::Durable`]: Ensures all transactions are fully flushed to disk before they are
/// considered committed. This provides the highest level of durability and crash safety
/// but may have a performance cost.
/// - [`SyncMode::SafeNoSync`]: Skips certain fsync operations to improve write performance.
/// This mode still maintains database integrity but may lose the most recent transactions if
/// the system crashes unexpectedly.
///
/// Choose `Durable` if consistency and crash safety are critical (e.g., production
/// environments). Choose `SafeNoSync` if performance is more important and occasional data
/// loss is acceptable (e.g., testing or ephemeral data).
sync_mode: SyncMode,
}

impl Default for DatabaseArguments {
Expand All @@ -126,6 +142,7 @@ impl DatabaseArguments {
max_read_transaction_duration: None,
exclusive: None,
max_readers: None,
sync_mode: SyncMode::Durable,
}
}

Expand All @@ -137,6 +154,15 @@ impl DatabaseArguments {
self
}

/// Sets the database sync mode.
pub const fn with_sync_mode(mut self, sync_mode: Option<SyncMode>) -> Self {
if let Some(sync_mode) = sync_mode {
self.sync_mode = sync_mode;
}

self
}

/// Configures the database growth step in bytes.
pub const fn with_growth_step(mut self, growth_step: Option<usize>) -> Self {
if let Some(growth_step) = growth_step {
Expand Down Expand Up @@ -329,7 +355,7 @@ impl DatabaseEnv {
DatabaseEnvKind::RW => {
// enable writemap mode in RW mode
inner_env.write_map();
Mode::ReadWrite { sync_mode: SyncMode::Durable }
Mode::ReadWrite { sync_mode: args.sync_mode }
}
};

Expand Down
19 changes: 18 additions & 1 deletion crates/storage/libmdbx-rs/src/flags.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
use std::str::FromStr;

use bitflags::bitflags;
use ffi::*;

/// MDBX sync mode
#[derive(Clone, Copy, Debug, Default)]
#[derive(PartialEq, Eq, Clone, Copy, Debug, Default)]
pub enum SyncMode {
/// Default robust and durable sync mode.
/// Metadata is written and flushed to disk after a data is written and flushed, which
Expand Down Expand Up @@ -119,6 +121,21 @@ impl From<Mode> for EnvironmentFlags {
}
}

impl FromStr for SyncMode {
type Err = String;

fn from_str(s: &str) -> Result<Self, Self::Err> {
let val = s.trim().to_ascii_lowercase();
match val.as_str() {
"durable" => Ok(Self::Durable),
"safe-no-sync" | "safenosync" | "safe_no_sync" => Ok(Self::SafeNoSync),
_ => Err(format!(
"invalid value '{s}' for sync mode. valid values: durable, safe-no-sync"
)),
}
}
}

#[derive(Clone, Copy, Debug, Default)]
pub struct EnvironmentFlags {
pub no_sub_dir: bool,
Expand Down
3 changes: 3 additions & 0 deletions docs/vocs/docs/pages/cli/reth/db.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,9 @@ Database:
--db.max-readers <MAX_READERS>
Maximum number of readers allowed to access the database concurrently

--db.sync-mode <SYNC_MODE>
Controls how aggressively the database synchronizes data to disk

Logging:
--log.stdout.format <FORMAT>
The format to use for logs written to stdout
Expand Down
3 changes: 3 additions & 0 deletions docs/vocs/docs/pages/cli/reth/db/diff.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ Database:
--db.max-readers <MAX_READERS>
Maximum number of readers allowed to access the database concurrently

--db.sync-mode <SYNC_MODE>
Controls how aggressively the database synchronizes data to disk

--table <TABLE>
The table name to diff. If not specified, all tables are diffed.

Expand Down
3 changes: 3 additions & 0 deletions docs/vocs/docs/pages/cli/reth/download.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@ Database:
--db.max-readers <MAX_READERS>
Maximum number of readers allowed to access the database concurrently

--db.sync-mode <SYNC_MODE>
Controls how aggressively the database synchronizes data to disk

-u, --url <URL>
Specify a snapshot URL or let the command propose a default one.

Expand Down
3 changes: 3 additions & 0 deletions docs/vocs/docs/pages/cli/reth/export-era.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@ Database:
--db.max-readers <MAX_READERS>
Maximum number of readers allowed to access the database concurrently

--db.sync-mode <SYNC_MODE>
Controls how aggressively the database synchronizes data to disk

--first-block-number <first-block-number>
Optional first block number to export from the db.
It is by default 0.
Expand Down
3 changes: 3 additions & 0 deletions docs/vocs/docs/pages/cli/reth/import-era.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@ Database:
--db.max-readers <MAX_READERS>
Maximum number of readers allowed to access the database concurrently

--db.sync-mode <SYNC_MODE>
Controls how aggressively the database synchronizes data to disk

--path <IMPORT_ERA_PATH>
The path to a directory for import.

Expand Down
3 changes: 3 additions & 0 deletions docs/vocs/docs/pages/cli/reth/import.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@ Database:
--db.max-readers <MAX_READERS>
Maximum number of readers allowed to access the database concurrently

--db.sync-mode <SYNC_MODE>
Controls how aggressively the database synchronizes data to disk

--no-state
Disables stages that require state.

Expand Down
3 changes: 3 additions & 0 deletions docs/vocs/docs/pages/cli/reth/init-state.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@ Database:
--db.max-readers <MAX_READERS>
Maximum number of readers allowed to access the database concurrently

--db.sync-mode <SYNC_MODE>
Controls how aggressively the database synchronizes data to disk

--without-evm
Specifies whether to initialize the state without relying on EVM historical data.

Expand Down
3 changes: 3 additions & 0 deletions docs/vocs/docs/pages/cli/reth/init.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@ Database:
--db.max-readers <MAX_READERS>
Maximum number of readers allowed to access the database concurrently

--db.sync-mode <SYNC_MODE>
Controls how aggressively the database synchronizes data to disk

Logging:
--log.stdout.format <FORMAT>
The format to use for logs written to stdout
Expand Down
3 changes: 3 additions & 0 deletions docs/vocs/docs/pages/cli/reth/node.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -712,6 +712,9 @@ Database:
--db.max-readers <MAX_READERS>
Maximum number of readers allowed to access the database concurrently

--db.sync-mode <SYNC_MODE>
Controls how aggressively the database synchronizes data to disk

Dev testnet:
--dev
Start the node in dev mode
Expand Down
3 changes: 3 additions & 0 deletions docs/vocs/docs/pages/cli/reth/prune.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@ Database:
--db.max-readers <MAX_READERS>
Maximum number of readers allowed to access the database concurrently

--db.sync-mode <SYNC_MODE>
Controls how aggressively the database synchronizes data to disk

Logging:
--log.stdout.format <FORMAT>
The format to use for logs written to stdout
Expand Down
3 changes: 3 additions & 0 deletions docs/vocs/docs/pages/cli/reth/re-execute.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@ Database:
--db.max-readers <MAX_READERS>
Maximum number of readers allowed to access the database concurrently

--db.sync-mode <SYNC_MODE>
Controls how aggressively the database synchronizes data to disk

--from <FROM>
The height to start at

Expand Down
3 changes: 3 additions & 0 deletions docs/vocs/docs/pages/cli/reth/stage/drop.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@ Database:
--db.max-readers <MAX_READERS>
Maximum number of readers allowed to access the database concurrently

--db.sync-mode <SYNC_MODE>
Controls how aggressively the database synchronizes data to disk

<STAGE>
Possible values:
- headers: The headers stage within the pipeline
Expand Down
3 changes: 3 additions & 0 deletions docs/vocs/docs/pages/cli/reth/stage/dump.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,9 @@ Database:
--db.max-readers <MAX_READERS>
Maximum number of readers allowed to access the database concurrently

--db.sync-mode <SYNC_MODE>
Controls how aggressively the database synchronizes data to disk

Logging:
--log.stdout.format <FORMAT>
The format to use for logs written to stdout
Expand Down
3 changes: 3 additions & 0 deletions docs/vocs/docs/pages/cli/reth/stage/run.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@ Database:
--db.max-readers <MAX_READERS>
Maximum number of readers allowed to access the database concurrently

--db.sync-mode <SYNC_MODE>
Controls how aggressively the database synchronizes data to disk

--metrics <SOCKET>
Enable Prometheus metrics.

Expand Down
3 changes: 3 additions & 0 deletions docs/vocs/docs/pages/cli/reth/stage/unwind.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,9 @@ Database:
--db.max-readers <MAX_READERS>
Maximum number of readers allowed to access the database concurrently

--db.sync-mode <SYNC_MODE>
Controls how aggressively the database synchronizes data to disk

--offline
If this is enabled, then all stages except headers, bodies, and sender recovery will be unwound

Expand Down
Loading