From 860a45393057567e0f5dfd6c050d0aed2ec45b65 Mon Sep 17 00:00:00 2001 From: Alexey Shekhirin <5773434+shekhirin@users.noreply.github.com> Date: Fri, 14 Nov 2025 22:37:01 +0000 Subject: [PATCH] refactor(cli): db subcommands (#19754) --- crates/cli/commands/src/db/clear.rs | 12 ++--- crates/cli/commands/src/db/mod.rs | 46 ++++++++++++------- crates/cli/commands/src/db/repair_trie.rs | 22 ++++----- .../cli/commands/src/db/static_file_header.rs | 25 +++------- crates/cli/commands/src/db/stats.rs | 4 ++ docs/vocs/docs/pages/cli/reth/db/stats.mdx | 3 ++ 6 files changed, 57 insertions(+), 55 deletions(-) diff --git a/crates/cli/commands/src/db/clear.rs b/crates/cli/commands/src/db/clear.rs index fc3852154f..4ba0a63df8 100644 --- a/crates/cli/commands/src/db/clear.rs +++ b/crates/cli/commands/src/db/clear.rs @@ -6,8 +6,9 @@ use reth_db_api::{ transaction::{DbTx, DbTxMut}, TableViewer, Tables, }; +use reth_db_common::DbTool; use reth_node_builder::NodeTypesWithDB; -use reth_provider::{ProviderFactory, StaticFileProviderFactory}; +use reth_provider::StaticFileProviderFactory; use reth_static_file_types::StaticFileSegment; /// The arguments for the `reth db clear` command @@ -19,16 +20,13 @@ pub struct Command { impl Command { /// Execute `db clear` command - pub fn execute( - self, - provider_factory: ProviderFactory, - ) -> eyre::Result<()> { + pub fn execute(self, tool: &DbTool) -> eyre::Result<()> { match self.subcommand { Subcommands::Mdbx { table } => { - table.view(&ClearViewer { db: provider_factory.db_ref() })? + table.view(&ClearViewer { db: tool.provider_factory.db_ref() })? } Subcommands::StaticFile { segment } => { - let static_file_provider = provider_factory.static_file_provider(); + let static_file_provider = tool.provider_factory.static_file_provider(); let static_files = iter_static_files(static_file_provider.directory())?; if let Some(segment_static_files) = static_files.get(&segment) { diff --git a/crates/cli/commands/src/db/mod.rs b/crates/cli/commands/src/db/mod.rs index ecac88fc28..2355e96f78 100644 --- a/crates/cli/commands/src/db/mod.rs +++ b/crates/cli/commands/src/db/mod.rs @@ -60,10 +60,11 @@ pub enum Subcommands { Path, } -/// `db_ro_exec` opens a database in read-only mode, and then execute with the provided command -macro_rules! db_ro_exec { - ($env:expr, $tool:ident, $N:ident, $command:block) => { - let Environment { provider_factory, .. } = $env.init::<$N>(AccessRights::RO)?; +/// Initializes a provider factory with specified access rights, and then execute with the provided +/// command +macro_rules! db_exec { + ($env:expr, $tool:ident, $N:ident, $access_rights:expr, $command:block) => { + let Environment { provider_factory, .. } = $env.init::<$N>($access_rights)?; let $tool = DbTool::new(provider_factory)?; $command; @@ -91,27 +92,32 @@ impl> Command match self.command { // TODO: We'll need to add this on the DB trait. Subcommands::Stats(command) => { - db_ro_exec!(self.env, tool, N, { + let access_rights = if command.skip_consistency_checks { + AccessRights::RoInconsistent + } else { + AccessRights::RO + }; + db_exec!(self.env, tool, N, access_rights, { command.execute(data_dir, &tool)?; }); } Subcommands::List(command) => { - db_ro_exec!(self.env, tool, N, { + db_exec!(self.env, tool, N, AccessRights::RO, { command.execute(&tool)?; }); } Subcommands::Checksum(command) => { - db_ro_exec!(self.env, tool, N, { + db_exec!(self.env, tool, N, AccessRights::RO, { command.execute(&tool)?; }); } Subcommands::Diff(command) => { - db_ro_exec!(self.env, tool, N, { + db_exec!(self.env, tool, N, AccessRights::RO, { command.execute(&tool)?; }); } Subcommands::Get(command) => { - db_ro_exec!(self.env, tool, N, { + db_exec!(self.env, tool, N, AccessRights::RO, { command.execute(&tool)?; }); } @@ -133,21 +139,27 @@ impl> Command } } - let Environment { provider_factory, .. } = self.env.init::(AccessRights::RW)?; - let tool = DbTool::new(provider_factory)?; - tool.drop(db_path, static_files_path, exex_wal_path)?; + db_exec!(self.env, tool, N, AccessRights::RW, { + tool.drop(db_path, static_files_path, exex_wal_path)?; + }); } Subcommands::Clear(command) => { - let Environment { provider_factory, .. } = self.env.init::(AccessRights::RW)?; - command.execute(provider_factory)?; + db_exec!(self.env, tool, N, AccessRights::RW, { + command.execute(&tool)?; + }); } Subcommands::RepairTrie(command) => { let access_rights = if command.dry_run { AccessRights::RO } else { AccessRights::RW }; - let Environment { provider_factory, .. } = self.env.init::(access_rights)?; - command.execute(provider_factory)?; + db_exec!(self.env, tool, N, access_rights, { + command.execute(&tool)?; + }); + } + Subcommands::StaticFileHeader(command) => { + db_exec!(self.env, tool, N, AccessRights::RoInconsistent, { + command.execute(&tool)?; + }); } - Subcommands::StaticFileHeader(command) => command.execute::(self.env)?, Subcommands::Version => { let local_db_version = match get_db_version(&db_path) { Ok(version) => Some(version), diff --git a/crates/cli/commands/src/db/repair_trie.rs b/crates/cli/commands/src/db/repair_trie.rs index f7dea67b76..ade18a3ff8 100644 --- a/crates/cli/commands/src/db/repair_trie.rs +++ b/crates/cli/commands/src/db/repair_trie.rs @@ -5,8 +5,9 @@ use reth_db_api::{ tables, transaction::{DbTx, DbTxMut}, }; +use reth_db_common::DbTool; use reth_node_builder::NodeTypesWithDB; -use reth_provider::{providers::ProviderNodeTypes, ProviderFactory, StageCheckpointReader}; +use reth_provider::{providers::ProviderNodeTypes, StageCheckpointReader}; use reth_stages::StageId; use reth_trie::{ verify::{Output, Verifier}, @@ -29,23 +30,20 @@ pub struct Command { impl Command { /// Execute `db repair-trie` command - pub fn execute( - self, - provider_factory: ProviderFactory, - ) -> eyre::Result<()> { + pub fn execute(self, tool: &DbTool) -> eyre::Result<()> { if self.dry_run { - verify_only(provider_factory)? + verify_only(tool)? } else { - verify_and_repair(provider_factory)? + verify_and_repair(tool)? } Ok(()) } } -fn verify_only(provider_factory: ProviderFactory) -> eyre::Result<()> { +fn verify_only(tool: &DbTool) -> eyre::Result<()> { // Get a database transaction directly from the database - let db = provider_factory.db_ref(); + let db = tool.provider_factory.db_ref(); let mut tx = db.tx()?; tx.disable_long_read_transaction_safety(); @@ -114,11 +112,9 @@ fn verify_checkpoints(provider: impl StageCheckpointReader) -> eyre::Result<()> Ok(()) } -fn verify_and_repair( - provider_factory: ProviderFactory, -) -> eyre::Result<()> { +fn verify_and_repair(tool: &DbTool) -> eyre::Result<()> { // Get a read-write database provider - let mut provider_rw = provider_factory.provider_rw()?; + let mut provider_rw = tool.provider_factory.provider_rw()?; // Check that a pipeline sync isn't in progress. verify_checkpoints(provider_rw.as_ref())?; diff --git a/crates/cli/commands/src/db/static_file_header.rs b/crates/cli/commands/src/db/static_file_header.rs index dea7bb40cc..4c0ff27464 100644 --- a/crates/cli/commands/src/db/static_file_header.rs +++ b/crates/cli/commands/src/db/static_file_header.rs @@ -1,12 +1,10 @@ use clap::{Parser, Subcommand}; -use reth_cli::chainspec::ChainSpecParser; -use reth_provider::StaticFileProviderFactory; +use reth_db_common::DbTool; +use reth_provider::{providers::ProviderNodeTypes, StaticFileProviderFactory}; use reth_static_file_types::StaticFileSegment; use std::path::PathBuf; use tracing::warn; -use crate::common::{AccessRights, CliNodeTypes, EnvironmentArgs}; - /// The arguments for the `reth db static-file-header` command #[derive(Parser, Debug)] pub struct Command { @@ -34,21 +32,12 @@ enum Source { impl Command { /// Execute `db static-file-header` command - pub fn execute>( - self, - env: EnvironmentArgs, - ) -> eyre::Result<()> { - // Try to initialize the environment as read-only. If it fails, try to initialize it as - // read-only without consistency checks. - let provider_factory = match env.init::(AccessRights::RO) { - Ok(env) => env, - Err(err) => { - warn!(?err, "Failed to initialize environment"); - env.init::(AccessRights::RoInconsistent)? - } + pub fn execute(self, tool: &DbTool) -> eyre::Result<()> { + let static_file_provider = tool.provider_factory.static_file_provider(); + if let Err(err) = static_file_provider.check_consistency(&tool.provider_factory.provider()?) + { + warn!("Error checking consistency of static files: {err}"); } - .provider_factory; - let static_file_provider = provider_factory.static_file_provider(); // Get the provider based on the source let provider = match self.source { diff --git a/crates/cli/commands/src/db/stats.rs b/crates/cli/commands/src/db/stats.rs index 2aef43c582..e225b2f991 100644 --- a/crates/cli/commands/src/db/stats.rs +++ b/crates/cli/commands/src/db/stats.rs @@ -18,6 +18,10 @@ use std::{sync::Arc, time::Duration}; #[derive(Parser, Debug)] /// The arguments for the `reth db stats` command pub struct Command { + /// Skip consistency checks for static files. + #[arg(long, default_value_t = false)] + pub(crate) skip_consistency_checks: bool, + /// Show only the total size for static files. #[arg(long, default_value_t = false)] detailed_sizes: bool, diff --git a/docs/vocs/docs/pages/cli/reth/db/stats.mdx b/docs/vocs/docs/pages/cli/reth/db/stats.mdx index 5bd316847c..5d985385e6 100644 --- a/docs/vocs/docs/pages/cli/reth/db/stats.mdx +++ b/docs/vocs/docs/pages/cli/reth/db/stats.mdx @@ -9,6 +9,9 @@ $ reth db stats --help Usage: reth db stats [OPTIONS] Options: + --skip-consistency-checks + Skip consistency checks for static files + --detailed-sizes Show only the total size for static files