Skip to content

Commit

Permalink
feat(db): add helper enum for table name (paradigmxyz#2935)
Browse files Browse the repository at this point in the history
Co-authored-by: Matthias Seitz <[email protected]>
  • Loading branch information
Majchrzak and mattsse authored Jun 22, 2023
1 parent 0dfb757 commit 6810cd1
Show file tree
Hide file tree
Showing 10 changed files with 334 additions and 201 deletions.
94 changes: 39 additions & 55 deletions bin/reth/src/db/get.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
use crate::utils::DbTool;
use clap::Parser;
use eyre::WrapErr;
use reth_db::{database::Database, table::Table, tables};
use serde::Deserialize;

use reth_db::{database::Database, table::Table, TableType, TableViewer, Tables};
use tracing::error;

/// The arguments for the `reth db get` command
Expand All @@ -12,75 +11,60 @@ pub struct Command {
///
/// NOTE: The dupsort tables are not supported now.
#[arg()]
pub table: String, // TODO: Convert to enum
pub table: Tables,

/// The key to get content for
/// The key to get content for
#[arg(value_parser = maybe_json_value_parser)]
pub key: String,
}

impl Command {
/// Execute `db get` command
pub fn execute<DB: Database>(self, mut tool: DbTool<'_, DB>) -> eyre::Result<()> {
macro_rules! table_get {
([$($table:ident),*]) => {
match self.table.as_str() {
$(stringify!($table) => {
let table_key = self.table_key::<tables::$table>().wrap_err("Could not parse the given table key.")?;

match tool.get::<tables::$table>(table_key)? {
Some(content) => {
println!("{}", serde_json::to_string_pretty(&content)?);
}
None => {
error!(target: "reth::cli", "No content for the given table key.");
},
};
return Ok(());
},)*
_ => {
error!(target: "reth::cli", "Unknown or unsupported table.");
return Ok(());
}
}
}
pub fn execute<DB: Database>(self, tool: &DbTool<'_, DB>) -> eyre::Result<()> {
if self.table.table_type() == TableType::DupSort {
error!(target: "reth::cli", "Unsupported table.");

return Ok(())
}

table_get!([
CanonicalHeaders,
HeaderTD,
HeaderNumbers,
Headers,
BlockBodyIndices,
BlockOmmers,
BlockWithdrawals,
TransactionBlock,
Transactions,
TxHashNumber,
Receipts,
PlainAccountState,
Bytecodes,
AccountHistory,
StorageHistory,
HashedAccount,
AccountsTrie,
TxSenders,
SyncStage,
SyncStageProgress
]);
self.table.view(&GetValueViewer { tool, args: &self })?;

Ok(())
}

/// Get an instance of key for given table
fn table_key<T: Table>(&self) -> Result<T::Key, eyre::Error>
where
for<'a> T::Key: Deserialize<'a>,
{
assert_eq!(T::NAME, self.table);
pub fn table_key<T: Table>(&self) -> Result<T::Key, eyre::Error> {
assert_eq!(T::NAME, self.table.name());

serde_json::from_str::<T::Key>(&self.key).map_err(|e| eyre::eyre!(e))
}
}

struct GetValueViewer<'a, DB: Database> {
tool: &'a DbTool<'a, DB>,
args: &'a Command,
}

impl<DB: Database> TableViewer<()> for GetValueViewer<'_, DB> {
type Error = eyre::Report;

fn view<T: Table>(&self) -> Result<(), Self::Error> {
// get a key for given table
let key = self.args.table_key::<T>()?;

match self.tool.get::<T>(key)? {
Some(content) => {
println!("{}", serde_json::to_string_pretty(&content)?);
}
None => {
error!(target: "reth::cli", "No content for the given table key.");
}
};

Ok(())
}
}

/// Map the user input value to json
fn maybe_json_value_parser(value: &str) -> Result<String, eyre::Error> {
if serde_json::from_str::<serde::de::IgnoredAny>(value).is_ok() {
Expand Down
85 changes: 85 additions & 0 deletions bin/reth/src/db/list.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
use crate::utils::DbTool;
use clap::Parser;

use super::tui::DbListTUI;
use eyre::WrapErr;
use reth_db::{
database::Database,
mdbx::{Env, WriteMap},
table::Table,
TableType, TableViewer, Tables,
};
use tracing::error;

const DEFAULT_NUM_ITEMS: &str = "5";

#[derive(Parser, Debug)]
/// The arguments for the `reth db list` command
pub struct Command {
/// The table name
table: Tables,
/// Skip first N entries
#[arg(long, short, default_value = "0")]
skip: usize,
/// Reverse the order of the entries. If enabled last table entries are read.
#[arg(long, short, default_value = "false")]
reverse: bool,
/// How many items to take from the walker
#[arg(long, short, default_value = DEFAULT_NUM_ITEMS)]
len: usize,
/// Dump as JSON instead of using TUI.
#[arg(long, short)]
json: bool,
}

impl Command {
/// Execute `db list` command
pub fn execute(self, tool: &DbTool<'_, Env<WriteMap>>) -> eyre::Result<()> {
if self.table.table_type() == TableType::DupSort {
error!(target: "reth::cli", "Unsupported table.");
}

self.table.view(&ListTableViewer { tool, args: &self })?;

Ok(())
}
}

struct ListTableViewer<'a> {
tool: &'a DbTool<'a, Env<WriteMap>>,
args: &'a Command,
}

impl TableViewer<()> for ListTableViewer<'_> {
type Error = eyre::Report;

fn view<T: Table>(&self) -> Result<(), Self::Error> {
self.tool.db.view(|tx| {
let table_db = tx.inner.open_db(Some(self.args.table.name())).wrap_err("Could not open db.")?;
let stats = tx.inner.db_stat(&table_db).wrap_err(format!("Could not find table: {}", stringify!($table)))?;
let total_entries = stats.entries();
if self.args.skip > total_entries - 1 {
error!(
target: "reth::cli",
"Start index {start} is greater than the final entry index ({final_entry_idx}) in the table {table}",
start = self.args.skip,
final_entry_idx = total_entries - 1,
table = self.args.table.name()
);
return Ok(());
}

if self.args.json {
let list_result = self.tool.list::<T>(self.args.skip, self.args.len, self.args.reverse)?.into_iter().collect::<Vec<_>>();
println!("{}", serde_json::to_string_pretty(&list_result)?);
Ok(())
} else {
DbListTUI::<_, T>::new(|skip, count| {
self.tool.list::<T>(skip, count, self.args.reverse).unwrap()
}, self.args.skip, self.args.len, total_entries).run()
}
})??;

Ok(())
}
}
99 changes: 7 additions & 92 deletions bin/reth/src/db/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,15 @@ use eyre::WrapErr;
use human_bytes::human_bytes;
use reth_db::{
database::Database,
tables,
version::{get_db_version, DatabaseVersionError, DB_VERSION},
Tables,
};
use reth_primitives::ChainSpec;
use reth_staged_sync::utils::init::init_db;
use std::sync::Arc;
use tracing::error;

mod get;
mod list;
/// DB List TUI
mod tui;

Expand Down Expand Up @@ -57,15 +57,13 @@ pub struct Command {
command: Subcommands,
}

const DEFAULT_NUM_ITEMS: &str = "5";

#[derive(Subcommand, Debug)]
/// `reth db` subcommands
pub enum Subcommands {
/// Lists all the tables, their entry count and their size
Stats,
/// Lists the contents of a table
List(ListArgs),
List(list::Command),
/// Gets the content of a table for the given key
Get(get::Command),
/// Deletes all database entries
Expand All @@ -76,25 +74,6 @@ pub enum Subcommands {
Path,
}

#[derive(Parser, Debug)]
/// The arguments for the `reth db list` command
pub struct ListArgs {
/// The table name
table: String, // TODO: Convert to enum
/// Skip first N entries
#[arg(long, short, default_value = "0")]
skip: usize,
/// Reverse the order of the entries. If enabled last table entries are read.
#[arg(long, short, default_value = "false")]
reverse: bool,
/// How many items to take from the walker
#[arg(long, short, default_value = DEFAULT_NUM_ITEMS)]
len: usize,
/// Dump as JSON instead of using TUI.
#[arg(long, short)]
json: bool,
}

impl Command {
/// Execute `db` command
pub async fn execute(self) -> eyre::Result<()> {
Expand Down Expand Up @@ -122,7 +101,7 @@ impl Command {

tool.db.view(|tx| {
let mut tables =
tables::TABLES.iter().map(|(_, name)| name).collect::<Vec<_>>();
Tables::ALL.iter().map(|table| table.name()).collect::<Vec<_>>();
tables.sort();
for table in tables {
let table_db =
Expand Down Expand Up @@ -157,75 +136,11 @@ impl Command {

println!("{stats_table}");
}
Subcommands::List(args) => {
macro_rules! table_tui {
($arg:expr, $start:expr, $len:expr => [$($table:ident),*]) => {
match $arg {
$(stringify!($table) => {
tool.db.view(|tx| {
let table_db = tx.inner.open_db(Some(stringify!($table))).wrap_err("Could not open db.")?;
let stats = tx.inner.db_stat(&table_db).wrap_err(format!("Could not find table: {}", stringify!($table)))?;
let total_entries = stats.entries();
if $start > total_entries - 1 {
error!(
target: "reth::cli",
"Start index {start} is greater than the final entry index ({final_entry_idx}) in the table {table}",
start = $start,
final_entry_idx = total_entries - 1,
table = stringify!($table)
);
return Ok(());
}

if args.json {
let list_result = tool.list::<tables::$table>(args.skip, args.len,args.reverse)?.into_iter().collect::<Vec<_>>();
println!("{}", serde_json::to_string_pretty(&list_result)?);
Ok(())
} else {
tui::DbListTUI::<_, tables::$table>::new(|skip, count| {
tool.list::<tables::$table>(skip, count, args.reverse).unwrap()
}, $start, $len, total_entries).run()
}
})??
},)*
_ => {
error!(target: "reth::cli", "Unknown table.");
return Ok(());
}
}
}
}

table_tui!(args.table.as_str(), args.skip, args.len => [
CanonicalHeaders,
HeaderTD,
HeaderNumbers,
Headers,
BlockBodyIndices,
BlockOmmers,
BlockWithdrawals,
TransactionBlock,
Transactions,
TxHashNumber,
Receipts,
PlainStorageState,
PlainAccountState,
Bytecodes,
AccountHistory,
StorageHistory,
AccountChangeSet,
StorageChangeSet,
HashedAccount,
HashedStorage,
AccountsTrie,
StoragesTrie,
TxSenders,
SyncStage,
SyncStageProgress
]);
Subcommands::List(command) => {
command.execute(&tool)?;
}
Subcommands::Get(command) => {
command.execute(tool)?;
command.execute(&tool)?;
}
Subcommands::Drop => {
tool.drop(db_path)?;
Expand Down
10 changes: 5 additions & 5 deletions bin/reth/src/prometheus_exporter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ pub(crate) async fn initialize_with_db_metrics(
// TODO: A generic stats abstraction for other DB types to deduplicate this and `reth db
// stats`
let _ = db.view(|tx| {
for table in tables::TABLES.iter().map(|(_, name)| name) {
for table in tables::Tables::ALL.iter().map(|table| table.name()) {
let table_db =
tx.inner.open_db(Some(table)).wrap_err("Could not open db.")?;

Expand All @@ -89,10 +89,10 @@ pub(crate) async fn initialize_with_db_metrics(
let num_pages = leaf_pages + branch_pages + overflow_pages;
let table_size = page_size * num_pages;

absolute_counter!("db.table_size", table_size as u64, "table" => *table);
absolute_counter!("db.table_pages", leaf_pages as u64, "table" => *table, "type" => "leaf");
absolute_counter!("db.table_pages", branch_pages as u64, "table" => *table, "type" => "branch");
absolute_counter!("db.table_pages", overflow_pages as u64, "table" => *table, "type" => "overflow");
absolute_counter!("db.table_size", table_size as u64, "table" => table);
absolute_counter!("db.table_pages", leaf_pages as u64, "table" => table, "type" => "leaf");
absolute_counter!("db.table_pages", branch_pages as u64, "table" => table, "type" => "branch");
absolute_counter!("db.table_pages", overflow_pages as u64, "table" => table, "type" => "overflow");
}

Ok::<(), eyre::Report>(())
Expand Down
Loading

0 comments on commit 6810cd1

Please sign in to comment.