Added purge subcommand to purge beacon chain db (#971)

This commit is contained in:
ethDreamer 2020-04-13 20:45:02 -04:00 committed by GitHub
parent 869b0621d6
commit 065ea15c9f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 94 additions and 4 deletions

View File

@ -221,7 +221,7 @@ where
.get::<PersistedBeaconChain>(&Hash256::from_slice(&BEACON_CHAIN_DB_KEY)) .get::<PersistedBeaconChain>(&Hash256::from_slice(&BEACON_CHAIN_DB_KEY))
.map_err(|e| format!("DB error when reading persisted beacon chain: {:?}", e))? .map_err(|e| format!("DB error when reading persisted beacon chain: {:?}", e))?
.ok_or_else(|| { .ok_or_else(|| {
"No persisted beacon chain found in store. Try deleting the .lighthouse/beacon dir." "No persisted beacon chain found in store. Try purging the beacon chain database."
.to_string() .to_string()
})?; })?;

View File

@ -1,3 +1,4 @@
use beacon_chain::builder::PUBKEY_CACHE_FILENAME;
use network::NetworkConfig; use network::NetworkConfig;
use serde_derive::{Deserialize, Serialize}; use serde_derive::{Deserialize, Serialize};
use std::fs; use std::fs;
@ -11,6 +12,9 @@ const TESTNET_SPEC_CONSTANTS: &str = "minimal";
/// Default directory name for the freezer database under the top-level data dir. /// Default directory name for the freezer database under the top-level data dir.
const DEFAULT_FREEZER_DB_DIR: &str = "freezer_db"; const DEFAULT_FREEZER_DB_DIR: &str = "freezer_db";
/// Trap file indicating if chain_db was purged
const CHAIN_DB_PURGED_TRAP_FILE: &str = ".db_purged";
/// Defines how the client should initialize the `BeaconChain` and other components. /// Defines how the client should initialize the `BeaconChain` and other components.
#[derive(PartialEq, Debug, Clone, Serialize, Deserialize)] #[derive(PartialEq, Debug, Clone, Serialize, Deserialize)]
pub enum ClientGenesis { pub enum ClientGenesis {
@ -97,6 +101,68 @@ impl Config {
.map(|data_dir| data_dir.join(&self.db_name)) .map(|data_dir| data_dir.join(&self.db_name))
} }
/// Get the path of the chain db purged trap file
pub fn get_db_purged_trap_file_path(&self) -> Option<PathBuf> {
self.get_data_dir()
.map(|data_dir| data_dir.join(CHAIN_DB_PURGED_TRAP_FILE))
}
/// returns whether chain_db was recently purged
pub fn chain_db_was_purged(&self) -> bool {
self.get_db_purged_trap_file_path()
.map_or(false, |trap_file| trap_file.exists())
}
/// purges the chain_db and creates trap file
pub fn purge_chain_db(&self) -> Result<(), String> {
// create the trap file
let trap_file = self
.get_db_purged_trap_file_path()
.ok_or("Failed to get trap file path".to_string())?;
fs::File::create(trap_file)
.map_err(|err| format!("Failed to create trap file: {}", err))?;
// remove the chain_db
fs::remove_dir_all(
self.get_db_path()
.ok_or("Failed to get db_path".to_string())?,
)
.map_err(|err| format!("Failed to remove chain_db: {}", err))?;
// remove the freezer db
fs::remove_dir_all(
self.get_freezer_db_path()
.ok_or("Failed to get freezer db path".to_string())?,
)
.map_err(|err| format!("Failed to remove chain_db: {}", err))?;
// also need to remove pubkey cache file if it exists
let pubkey_cache_file = self
.get_data_dir()
.map(|data_dir| data_dir.join(PUBKEY_CACHE_FILENAME))
.ok_or("Failed to get pubkey cache file path".to_string())?;
if !pubkey_cache_file.exists() {
return Ok(());
}
fs::remove_file(pubkey_cache_file)
.map_err(|err| format!("Failed to remove pubkey cache: {}", err))?;
Ok(())
}
/// cleans up purge_db trap file
pub fn cleanup_after_purge_db(&self) -> Result<(), String> {
let trap_file = self
.get_db_purged_trap_file_path()
.ok_or("Failed to get trap file path".to_string())?;
if !trap_file.exists() {
return Ok(());
}
fs::remove_file(trap_file).map_err(|err| format!("Failed to remove trap file: {}", err))?;
Ok(())
}
/// Get the database path, creating it if necessary. /// Get the database path, creating it if necessary.
pub fn create_db_path(&self) -> Result<PathBuf, String> { pub fn create_db_path(&self) -> Result<PathBuf, String> {
let db_path = self let db_path = self

View File

@ -309,4 +309,12 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
.help("A file from which to read the state")) .help("A file from which to read the state"))
) )
) )
/*
* The "purge" sub-command.
*
* Allows user to purge beacon database
*/
.subcommand(SubCommand::with_name("purge")
.about("Purge the beacon chain database.")
)
} }

View File

@ -189,15 +189,27 @@ pub fn get_config<E: EthSpec>(
("testnet", Some(sub_cmd_args)) => { ("testnet", Some(sub_cmd_args)) => {
process_testnet_subcommand(&mut client_config, &eth2_config, sub_cmd_args)? process_testnet_subcommand(&mut client_config, &eth2_config, sub_cmd_args)?
} }
("purge", _) => {
client_config.purge_chain_db()?;
println!("Successfully purged chain db");
std::process::exit(0);
}
// No sub-command assumes a resume operation. // No sub-command assumes a resume operation.
_ => { _ => {
// If no primary subcommand was given, start the beacon chain from an existing // If no primary subcommand was given, start the beacon chain from an existing
// database. // database.
client_config.genesis = ClientGenesis::Resume; client_config.genesis = ClientGenesis::Resume;
let db_path_exists: bool = match client_config.get_db_path() {
Some(path) => path.exists(),
None => false,
};
// Whilst there is no large testnet or mainnet force the user to specify how they want // Whilst there is no large testnet or mainnet force the user to specify how they want
// to start a new chain (e.g., from a genesis YAML file, another node, etc). // to start a new chain (e.g., from a genesis YAML file, another node, etc).
if !client_config.data_dir.exists() { if !client_config.data_dir.exists()
|| (!db_path_exists && client_config.chain_db_was_purged())
{
info!( info!(
log, log,
"Starting from an empty database"; "Starting from an empty database";
@ -392,7 +404,8 @@ fn init_new_client<E: EthSpec>(
/// ///
/// Returns an error if `self.data_dir` already exists. /// Returns an error if `self.data_dir` already exists.
pub fn create_new_datadir(client_config: &ClientConfig, eth2_config: &Eth2Config) -> Result<()> { pub fn create_new_datadir(client_config: &ClientConfig, eth2_config: &Eth2Config) -> Result<()> {
if client_config.data_dir.exists() { let rebuild_db = client_config.chain_db_was_purged();
if client_config.data_dir.exists() && !rebuild_db {
return Err(format!( return Err(format!(
"Data dir already exists at {:?}", "Data dir already exists at {:?}",
client_config.data_dir client_config.data_dir
@ -407,7 +420,9 @@ pub fn create_new_datadir(client_config: &ClientConfig, eth2_config: &Eth2Config
($file: ident, $variable: ident) => { ($file: ident, $variable: ident) => {
let file = client_config.data_dir.join($file); let file = client_config.data_dir.join($file);
if file.exists() { if file.exists() {
return Err(format!("Datadir is not clean, {} exists.", $file)); if !rebuild_db {
return Err(format!("Datadir is not clean, {} exists.", $file));
}
} else { } else {
// Write the onfig to a TOML file in the datadir. // Write the onfig to a TOML file in the datadir.
write_to_file(client_config.data_dir.join($file), $variable) write_to_file(client_config.data_dir.join($file), $variable)
@ -418,6 +433,7 @@ pub fn create_new_datadir(client_config: &ClientConfig, eth2_config: &Eth2Config
write_to_file!(CLIENT_CONFIG_FILENAME, client_config); write_to_file!(CLIENT_CONFIG_FILENAME, client_config);
write_to_file!(ETH2_CONFIG_FILENAME, eth2_config); write_to_file!(ETH2_CONFIG_FILENAME, eth2_config);
client_config.cleanup_after_purge_db()?;
Ok(()) Ok(())
} }