Unify and limit rocksdb dependency places (#8371)
* secret_store: remove kvdb_rocksdb dependency * cli: init db mod for open dispatch * cli: move db, client_db, restoration_db, secretstore_db to a separate mod * migration: rename to migration-rocksdb and remove ethcore-migrations * ethcore: re-move kvdb-rocksdb dep to test * mark test_helpers as test only and fix migration mod naming * Move restoration_db_handler to test_helpers_internal * Fix missing preambles in test_helpers_internal and rocksdb/helpers * Move test crates downward * Fix missing docs * cli, db::open_db: move each argument to a separate line * Use featuregate instead of dead code for `open_secretstore_db` * Move pathbuf import to open_secretstore_db Because it's only used there behind a feature gate
This commit is contained in:
38
parity/db/rocksdb/helpers.rs
Normal file
38
parity/db/rocksdb/helpers.rs
Normal file
@@ -0,0 +1,38 @@
|
||||
// Copyright 2015-2018 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use std::path::Path;
|
||||
use ethcore::db::NUM_COLUMNS;
|
||||
use ethcore::client::{ClientConfig, DatabaseCompactionProfile};
|
||||
use super::kvdb_rocksdb::{CompactionProfile, DatabaseConfig};
|
||||
|
||||
pub fn compaction_profile(profile: &DatabaseCompactionProfile, db_path: &Path) -> CompactionProfile {
|
||||
match profile {
|
||||
&DatabaseCompactionProfile::Auto => CompactionProfile::auto(db_path),
|
||||
&DatabaseCompactionProfile::SSD => CompactionProfile::ssd(),
|
||||
&DatabaseCompactionProfile::HDD => CompactionProfile::hdd(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn client_db_config(client_path: &Path, client_config: &ClientConfig) -> DatabaseConfig {
|
||||
let mut client_db_config = DatabaseConfig::with_columns(NUM_COLUMNS);
|
||||
|
||||
client_db_config.memory_budget = client_config.db_cache_size;
|
||||
client_db_config.compaction = compaction_profile(&client_config.db_compaction, &client_path);
|
||||
client_db_config.wal = client_config.db_wal;
|
||||
|
||||
client_db_config
|
||||
}
|
||||
224
parity/db/rocksdb/migration.rs
Normal file
224
parity/db/rocksdb/migration.rs
Normal file
@@ -0,0 +1,224 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use std::fs;
|
||||
use std::io::{Read, Write, Error as IoError, ErrorKind};
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::fmt::{Display, Formatter, Error as FmtError};
|
||||
use super::migration_rocksdb::{self, Manager as MigrationManager, Config as MigrationConfig, ChangeColumns};
|
||||
use super::kvdb_rocksdb::CompactionProfile;
|
||||
use ethcore::client::DatabaseCompactionProfile;
|
||||
|
||||
use super::helpers;
|
||||
|
||||
/// The migration from v10 to v11.
|
||||
/// Adds a column for node info.
|
||||
pub const TO_V11: ChangeColumns = ChangeColumns {
|
||||
pre_columns: Some(6),
|
||||
post_columns: Some(7),
|
||||
version: 11,
|
||||
};
|
||||
|
||||
/// The migration from v11 to v12.
|
||||
/// Adds a column for light chain storage.
|
||||
pub const TO_V12: ChangeColumns = ChangeColumns {
|
||||
pre_columns: Some(7),
|
||||
post_columns: Some(8),
|
||||
version: 12,
|
||||
};
|
||||
|
||||
|
||||
/// Database is assumed to be at default version, when no version file is found.
|
||||
const DEFAULT_VERSION: u32 = 5;
|
||||
/// Current version of database models.
|
||||
const CURRENT_VERSION: u32 = 12;
|
||||
/// First version of the consolidated database.
|
||||
const CONSOLIDATION_VERSION: u32 = 9;
|
||||
/// Defines how many items are migrated to the new version of database at once.
|
||||
const BATCH_SIZE: usize = 1024;
|
||||
/// Version file name.
|
||||
const VERSION_FILE_NAME: &'static str = "db_version";
|
||||
|
||||
/// Migration related erorrs.
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
/// Returned when current version cannot be read or guessed.
|
||||
UnknownDatabaseVersion,
|
||||
/// Existing DB is newer than the known one.
|
||||
FutureDBVersion,
|
||||
/// Migration is not possible.
|
||||
MigrationImpossible,
|
||||
/// Internal migration error.
|
||||
Internal(migration_rocksdb::Error),
|
||||
/// Migration was completed succesfully,
|
||||
/// but there was a problem with io.
|
||||
Io(IoError),
|
||||
}
|
||||
|
||||
impl Display for Error {
|
||||
fn fmt(&self, f: &mut Formatter) -> Result<(), FmtError> {
|
||||
let out = match *self {
|
||||
Error::UnknownDatabaseVersion => "Current database version cannot be read".into(),
|
||||
Error::FutureDBVersion => "Database was created with newer client version. Upgrade your client or delete DB and resync.".into(),
|
||||
Error::MigrationImpossible => format!("Database migration to version {} is not possible.", CURRENT_VERSION),
|
||||
Error::Internal(ref err) => format!("{}", err),
|
||||
Error::Io(ref err) => format!("Unexpected io error on DB migration: {}.", err),
|
||||
};
|
||||
|
||||
write!(f, "{}", out)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<IoError> for Error {
|
||||
fn from(err: IoError) -> Self {
|
||||
Error::Io(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<migration_rocksdb::Error> for Error {
|
||||
fn from(err: migration_rocksdb::Error) -> Self {
|
||||
match err.into() {
|
||||
migration_rocksdb::ErrorKind::Io(e) => Error::Io(e),
|
||||
err => Error::Internal(err.into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the version file path.
|
||||
fn version_file_path(path: &Path) -> PathBuf {
|
||||
let mut file_path = path.to_owned();
|
||||
file_path.push(VERSION_FILE_NAME);
|
||||
file_path
|
||||
}
|
||||
|
||||
/// Reads current database version from the file at given path.
|
||||
/// If the file does not exist returns `DEFAULT_VERSION`.
|
||||
fn current_version(path: &Path) -> Result<u32, Error> {
|
||||
match fs::File::open(version_file_path(path)) {
|
||||
Err(ref err) if err.kind() == ErrorKind::NotFound => Ok(DEFAULT_VERSION),
|
||||
Err(_) => Err(Error::UnknownDatabaseVersion),
|
||||
Ok(mut file) => {
|
||||
let mut s = String::new();
|
||||
file.read_to_string(&mut s).map_err(|_| Error::UnknownDatabaseVersion)?;
|
||||
u32::from_str_radix(&s, 10).map_err(|_| Error::UnknownDatabaseVersion)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Writes current database version to the file.
|
||||
/// Creates a new file if the version file does not exist yet.
|
||||
fn update_version(path: &Path) -> Result<(), Error> {
|
||||
fs::create_dir_all(path)?;
|
||||
let mut file = fs::File::create(version_file_path(path))?;
|
||||
file.write_all(format!("{}", CURRENT_VERSION).as_bytes())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Consolidated database path
|
||||
fn consolidated_database_path(path: &Path) -> PathBuf {
|
||||
let mut state_path = path.to_owned();
|
||||
state_path.push("db");
|
||||
state_path
|
||||
}
|
||||
|
||||
/// Database backup
|
||||
fn backup_database_path(path: &Path) -> PathBuf {
|
||||
let mut backup_path = path.to_owned();
|
||||
backup_path.pop();
|
||||
backup_path.push("temp_backup");
|
||||
backup_path
|
||||
}
|
||||
|
||||
/// Default migration settings.
|
||||
pub fn default_migration_settings(compaction_profile: &CompactionProfile) -> MigrationConfig {
|
||||
MigrationConfig {
|
||||
batch_size: BATCH_SIZE,
|
||||
compaction_profile: *compaction_profile,
|
||||
}
|
||||
}
|
||||
|
||||
/// Migrations on the consolidated database.
|
||||
fn consolidated_database_migrations(compaction_profile: &CompactionProfile) -> Result<MigrationManager, Error> {
|
||||
let mut manager = MigrationManager::new(default_migration_settings(compaction_profile));
|
||||
manager.add_migration(TO_V11).map_err(|_| Error::MigrationImpossible)?;
|
||||
manager.add_migration(TO_V12).map_err(|_| Error::MigrationImpossible)?;
|
||||
Ok(manager)
|
||||
}
|
||||
|
||||
/// Migrates database at given position with given migration rules.
|
||||
fn migrate_database(version: u32, db_path: PathBuf, mut migrations: MigrationManager) -> Result<(), Error> {
|
||||
// check if migration is needed
|
||||
if !migrations.is_needed(version) {
|
||||
return Ok(())
|
||||
}
|
||||
|
||||
let backup_path = backup_database_path(&db_path);
|
||||
// remove the backup dir if it exists
|
||||
let _ = fs::remove_dir_all(&backup_path);
|
||||
|
||||
// migrate old database to the new one
|
||||
let temp_path = migrations.execute(&db_path, version)?;
|
||||
|
||||
// completely in-place migration leads to the paths being equal.
|
||||
// in that case, no need to shuffle directories.
|
||||
if temp_path == db_path { return Ok(()) }
|
||||
|
||||
// create backup
|
||||
fs::rename(&db_path, &backup_path)?;
|
||||
|
||||
// replace the old database with the new one
|
||||
if let Err(err) = fs::rename(&temp_path, &db_path) {
|
||||
// if something went wrong, bring back backup
|
||||
fs::rename(&backup_path, &db_path)?;
|
||||
return Err(err.into());
|
||||
}
|
||||
|
||||
// remove backup
|
||||
fs::remove_dir_all(&backup_path).map_err(Into::into)
|
||||
}
|
||||
|
||||
fn exists(path: &Path) -> bool {
|
||||
fs::metadata(path).is_ok()
|
||||
}
|
||||
|
||||
/// Migrates the database.
|
||||
pub fn migrate(path: &Path, compaction_profile: &DatabaseCompactionProfile) -> Result<(), Error> {
|
||||
let compaction_profile = helpers::compaction_profile(&compaction_profile, path);
|
||||
|
||||
// read version file.
|
||||
let version = current_version(path)?;
|
||||
|
||||
// migrate the databases.
|
||||
// main db directory may already exists, so let's check if we have blocks dir
|
||||
if version > CURRENT_VERSION {
|
||||
return Err(Error::FutureDBVersion);
|
||||
}
|
||||
|
||||
// We are in the latest version, yay!
|
||||
if version == CURRENT_VERSION {
|
||||
return Ok(())
|
||||
}
|
||||
|
||||
// Further migrations
|
||||
if version >= CONSOLIDATION_VERSION && version < CURRENT_VERSION && exists(&consolidated_database_path(path)) {
|
||||
println!("Migrating database from version {} to {}", ::std::cmp::max(CONSOLIDATION_VERSION, version), CURRENT_VERSION);
|
||||
migrate_database(version, consolidated_database_path(path), consolidated_database_migrations(&compaction_profile)?)?;
|
||||
println!("Migration finished");
|
||||
}
|
||||
|
||||
// update version file.
|
||||
update_version(path)
|
||||
}
|
||||
91
parity/db/rocksdb/mod.rs
Normal file
91
parity/db/rocksdb/mod.rs
Normal file
@@ -0,0 +1,91 @@
|
||||
// Copyright 2015-2018 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
extern crate kvdb_rocksdb;
|
||||
extern crate migration_rocksdb;
|
||||
|
||||
use std::sync::Arc;
|
||||
use std::path::Path;
|
||||
use ethcore::db::NUM_COLUMNS;
|
||||
use ethcore::client::{ClientConfig, DatabaseCompactionProfile};
|
||||
use kvdb::{KeyValueDB, KeyValueDBHandler};
|
||||
use self::kvdb_rocksdb::{Database, DatabaseConfig};
|
||||
|
||||
use cache::CacheConfig;
|
||||
|
||||
mod migration;
|
||||
mod helpers;
|
||||
|
||||
pub use self::migration::migrate;
|
||||
|
||||
/// Open a secret store DB using the given secret store data path. The DB path is one level beneath the data path.
|
||||
#[cfg(feature = "secretstore")]
|
||||
pub fn open_secretstore_db(data_path: &str) -> Result<Arc<KeyValueDB>, String> {
|
||||
use std::path::PathBuf;
|
||||
|
||||
let mut db_path = PathBuf::from(data_path);
|
||||
db_path.push("db");
|
||||
let db_path = db_path.to_str().ok_or_else(|| "Invalid secretstore path".to_string())?;
|
||||
Ok(Arc::new(Database::open_default(&db_path).map_err(|e| format!("Error opening database: {:?}", e))?))
|
||||
}
|
||||
|
||||
/// Open a new client DB.
|
||||
pub fn open_client_db(client_path: &Path, client_config: &ClientConfig) -> Result<Arc<KeyValueDB>, String> {
|
||||
let client_db_config = helpers::client_db_config(client_path, client_config);
|
||||
|
||||
let client_db = Arc::new(Database::open(
|
||||
&client_db_config,
|
||||
&client_path.to_str().expect("DB path could not be converted to string.")
|
||||
).map_err(|e| format!("Client service database error: {:?}", e))?);
|
||||
|
||||
Ok(client_db)
|
||||
}
|
||||
|
||||
/// Create a restoration db handler using the config generated by `client_path` and `client_config`.
|
||||
pub fn restoration_db_handler(client_path: &Path, client_config: &ClientConfig) -> Box<KeyValueDBHandler> {
|
||||
use kvdb::Error;
|
||||
|
||||
let client_db_config = helpers::client_db_config(client_path, client_config);
|
||||
|
||||
struct RestorationDBHandler {
|
||||
config: DatabaseConfig,
|
||||
}
|
||||
|
||||
impl KeyValueDBHandler for RestorationDBHandler {
|
||||
fn open(&self, db_path: &Path) -> Result<Arc<KeyValueDB>, Error> {
|
||||
Ok(Arc::new(Database::open(&self.config, &db_path.to_string_lossy())?))
|
||||
}
|
||||
}
|
||||
|
||||
Box::new(RestorationDBHandler {
|
||||
config: client_db_config,
|
||||
})
|
||||
}
|
||||
|
||||
/// Open a new main DB.
|
||||
pub fn open_db(client_path: &str, cache_config: &CacheConfig, compaction: &DatabaseCompactionProfile, wal: bool) -> Result<Arc<KeyValueDB>, String> {
|
||||
let db_config = DatabaseConfig {
|
||||
memory_budget: Some(cache_config.blockchain() as usize * 1024 * 1024),
|
||||
compaction: helpers::compaction_profile(&compaction, &Path::new(client_path)),
|
||||
wal: wal,
|
||||
.. DatabaseConfig::with_columns(NUM_COLUMNS)
|
||||
};
|
||||
|
||||
Ok(Arc::new(Database::open(
|
||||
&db_config,
|
||||
client_path
|
||||
).map_err(|e| format!("Failed to open database: {}", e))?))
|
||||
}
|
||||
Reference in New Issue
Block a user