Merge branch 'master' into journaldb_commit
This commit is contained in:
@@ -143,12 +143,12 @@ impl CompactionProfile {
|
||||
}
|
||||
|
||||
/// Database configuration
|
||||
#[derive(Clone, Copy)]
|
||||
#[derive(Clone)]
|
||||
pub struct DatabaseConfig {
|
||||
/// Max number of open files.
|
||||
pub max_open_files: i32,
|
||||
/// Cache-size
|
||||
pub cache_size: Option<usize>,
|
||||
/// Cache sizes (in MiB) for specific columns.
|
||||
pub cache_sizes: HashMap<Option<u32>, usize>,
|
||||
/// Compaction profile
|
||||
pub compaction: CompactionProfile,
|
||||
/// Set number of columns
|
||||
@@ -159,17 +159,23 @@ pub struct DatabaseConfig {
|
||||
|
||||
impl DatabaseConfig {
|
||||
/// Create new `DatabaseConfig` with default parameters and specified set of columns.
|
||||
/// Note that cache sizes must be explicitly set.
|
||||
pub fn with_columns(columns: Option<u32>) -> Self {
|
||||
let mut config = Self::default();
|
||||
config.columns = columns;
|
||||
config
|
||||
}
|
||||
|
||||
/// Set the column cache size in MiB.
|
||||
pub fn set_cache(&mut self, col: Option<u32>, size: usize) {
|
||||
self.cache_sizes.insert(col, size);
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for DatabaseConfig {
|
||||
fn default() -> DatabaseConfig {
|
||||
DatabaseConfig {
|
||||
cache_size: None,
|
||||
cache_sizes: HashMap::new(),
|
||||
max_open_files: 512,
|
||||
compaction: CompactionProfile::default(),
|
||||
columns: None,
|
||||
@@ -213,6 +219,9 @@ impl Database {
|
||||
|
||||
/// Open database file. Creates if it does not exist.
|
||||
pub fn open(config: &DatabaseConfig, path: &str) -> Result<Database, String> {
|
||||
// default cache size for columns not specified.
|
||||
const DEFAULT_CACHE: usize = 2;
|
||||
|
||||
let mut opts = Options::new();
|
||||
if let Some(rate_limit) = config.compaction.write_rate_limit {
|
||||
try!(opts.set_parsed_options(&format!("rate_limiter_bytes_per_sec={}", rate_limit)));
|
||||
@@ -232,17 +241,22 @@ impl Database {
|
||||
|
||||
let mut cf_options = Vec::with_capacity(config.columns.unwrap_or(0) as usize);
|
||||
|
||||
for _ in 0 .. config.columns.unwrap_or(0) {
|
||||
for col in 0 .. config.columns.unwrap_or(0) {
|
||||
let mut opts = Options::new();
|
||||
opts.set_compaction_style(DBCompactionStyle::DBUniversalCompaction);
|
||||
opts.set_target_file_size_base(config.compaction.initial_file_size);
|
||||
opts.set_target_file_size_multiplier(config.compaction.file_size_multiplier);
|
||||
if let Some(cache_size) = config.cache_size {
|
||||
|
||||
let col_opt = config.columns.map(|_| col);
|
||||
|
||||
{
|
||||
let cache_size = config.cache_sizes.get(&col_opt).cloned().unwrap_or(DEFAULT_CACHE);
|
||||
let mut block_opts = BlockBasedOptions::new();
|
||||
// all goes to read cache
|
||||
// all goes to read cache.
|
||||
block_opts.set_cache(Cache::new(cache_size * 1024 * 1024));
|
||||
opts.set_block_based_table_factory(&block_opts);
|
||||
}
|
||||
|
||||
cf_options.push(opts);
|
||||
}
|
||||
|
||||
|
||||
@@ -20,7 +20,9 @@ mod tests;
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
use std::fs;
|
||||
use std::fmt;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::sync::Arc;
|
||||
|
||||
use ::kvdb::{CompactionProfile, Database, DatabaseConfig, DBTransaction};
|
||||
|
||||
@@ -96,20 +98,39 @@ pub enum Error {
|
||||
Custom(String),
|
||||
}
|
||||
|
||||
impl fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
match *self {
|
||||
Error::CannotAddMigration => write!(f, "Cannot add migration"),
|
||||
Error::MigrationImpossible => write!(f, "Migration impossible"),
|
||||
Error::Io(ref err) => write!(f, "{}", err),
|
||||
Error::Custom(ref err) => write!(f, "{}", err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<::std::io::Error> for Error {
|
||||
fn from(e: ::std::io::Error) -> Self {
|
||||
Error::Io(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<String> for Error {
|
||||
fn from(e: String) -> Self {
|
||||
Error::Custom(e)
|
||||
}
|
||||
}
|
||||
|
||||
/// A generalized migration from the given db to a destination db.
|
||||
pub trait Migration: 'static {
|
||||
/// Number of columns in the database before the migration.
|
||||
fn pre_columns(&self) -> Option<u32> { self.columns() }
|
||||
/// Number of columns in database after the migration.
|
||||
fn columns(&self) -> Option<u32>;
|
||||
/// Version of the database after the migration.
|
||||
fn version(&self) -> u32;
|
||||
/// Migrate a source to a destination.
|
||||
fn migrate(&mut self, source: &Database, config: &Config, destination: &mut Database, col: Option<u32>) -> Result<(), Error>;
|
||||
fn migrate(&mut self, source: Arc<Database>, config: &Config, destination: &mut Database, col: Option<u32>) -> Result<(), Error>;
|
||||
}
|
||||
|
||||
/// A simple migration over key-value pairs.
|
||||
@@ -128,7 +149,7 @@ impl<T: SimpleMigration> Migration for T {
|
||||
|
||||
fn version(&self) -> u32 { SimpleMigration::version(self) }
|
||||
|
||||
fn migrate(&mut self, source: &Database, config: &Config, dest: &mut Database, col: Option<u32>) -> Result<(), Error> {
|
||||
fn migrate(&mut self, source: Arc<Database>, config: &Config, dest: &mut Database, col: Option<u32>) -> Result<(), Error> {
|
||||
let mut batch = Batch::new(config, col);
|
||||
|
||||
for (key, value) in source.iter(col) {
|
||||
@@ -195,6 +216,7 @@ impl Manager {
|
||||
Some(last) => migration.version() > last.version(),
|
||||
None => true,
|
||||
};
|
||||
|
||||
match is_new {
|
||||
true => Ok(self.migrations.push(Box::new(migration))),
|
||||
false => Err(Error::CannotAddMigration),
|
||||
@@ -205,12 +227,16 @@ impl Manager {
|
||||
/// and producing a path where the final migration lives.
|
||||
pub fn execute(&mut self, old_path: &Path, version: u32) -> Result<PathBuf, Error> {
|
||||
let config = self.config.clone();
|
||||
let columns = self.no_of_columns_at(version);
|
||||
let migrations = self.migrations_from(version);
|
||||
trace!(target: "migration", "Total migrations to execute for version {}: {}", version, migrations.len());
|
||||
if migrations.is_empty() { return Err(Error::MigrationImpossible) };
|
||||
|
||||
let columns = migrations.iter().nth(0).and_then(|m| m.pre_columns());
|
||||
|
||||
trace!(target: "migration", "Expecting database to contain {:?} columns", columns);
|
||||
let mut db_config = DatabaseConfig {
|
||||
max_open_files: 64,
|
||||
cache_size: None,
|
||||
cache_sizes: Default::default(),
|
||||
compaction: config.compaction_profile,
|
||||
columns: columns,
|
||||
wal: true,
|
||||
@@ -222,7 +248,7 @@ impl Manager {
|
||||
|
||||
// start with the old db.
|
||||
let old_path_str = try!(old_path.to_str().ok_or(Error::MigrationImpossible));
|
||||
let mut cur_db = try!(Database::open(&db_config, old_path_str).map_err(Error::Custom));
|
||||
let mut cur_db = Arc::new(try!(Database::open(&db_config, old_path_str).map_err(Error::Custom)));
|
||||
|
||||
for migration in migrations {
|
||||
// Change number of columns in new db
|
||||
@@ -237,16 +263,16 @@ impl Manager {
|
||||
// perform the migration from cur_db to new_db.
|
||||
match current_columns {
|
||||
// migrate only default column
|
||||
None => try!(migration.migrate(&cur_db, &config, &mut new_db, None)),
|
||||
None => try!(migration.migrate(cur_db.clone(), &config, &mut new_db, None)),
|
||||
Some(v) => {
|
||||
// Migrate all columns in previous DB
|
||||
for col in 0..v {
|
||||
try!(migration.migrate(&cur_db, &config, &mut new_db, Some(col)))
|
||||
try!(migration.migrate(cur_db.clone(), &config, &mut new_db, Some(col)))
|
||||
}
|
||||
}
|
||||
}
|
||||
// next iteration, we will migrate from this db into the other temp.
|
||||
cur_db = new_db;
|
||||
cur_db = Arc::new(new_db);
|
||||
temp_idx.swap();
|
||||
|
||||
// remove the other temporary migration database.
|
||||
@@ -267,14 +293,6 @@ impl Manager {
|
||||
fn migrations_from(&mut self, version: u32) -> Vec<&mut Box<Migration>> {
|
||||
self.migrations.iter_mut().filter(|m| m.version() > version).collect()
|
||||
}
|
||||
|
||||
fn no_of_columns_at(&self, version: u32) -> Option<u32> {
|
||||
let migration = self.migrations.iter().find(|m| m.version() == version);
|
||||
match migration {
|
||||
Some(m) => m.columns(),
|
||||
None => None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Prints a dot every `max` ticks
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
//! are performed in temp sub-directories.
|
||||
|
||||
use common::*;
|
||||
use migration::{Config, SimpleMigration, Manager};
|
||||
use migration::{Batch, Config, Error, SimpleMigration, Migration, Manager};
|
||||
use kvdb::Database;
|
||||
|
||||
use devtools::RandomTempPath;
|
||||
@@ -62,11 +62,10 @@ impl SimpleMigration for Migration0 {
|
||||
|
||||
fn version(&self) -> u32 { 1 }
|
||||
|
||||
fn simple_migrate(&mut self, key: Vec<u8>, value: Vec<u8>) -> Option<(Vec<u8>, Vec<u8>)> {
|
||||
let mut key = key;
|
||||
fn simple_migrate(&mut self, mut key: Vec<u8>, mut value: Vec<u8>) -> Option<(Vec<u8>, Vec<u8>)> {
|
||||
key.push(0x11);
|
||||
let mut value = value;
|
||||
value.push(0x22);
|
||||
|
||||
Some((key, value))
|
||||
}
|
||||
}
|
||||
@@ -83,6 +82,31 @@ impl SimpleMigration for Migration1 {
|
||||
}
|
||||
}
|
||||
|
||||
struct AddsColumn;
|
||||
|
||||
impl Migration for AddsColumn {
|
||||
fn pre_columns(&self) -> Option<u32> { None }
|
||||
|
||||
fn columns(&self) -> Option<u32> { Some(1) }
|
||||
|
||||
fn version(&self) -> u32 { 1 }
|
||||
|
||||
fn migrate(&mut self, source: Arc<Database>, config: &Config, dest: &mut Database, col: Option<u32>) -> Result<(), Error> {
|
||||
let mut batch = Batch::new(config, col);
|
||||
|
||||
for (key, value) in source.iter(col) {
|
||||
try!(batch.insert(key.to_vec(), value.to_vec(), dest));
|
||||
}
|
||||
|
||||
|
||||
if col == Some(1) {
|
||||
try!(batch.insert(vec![1, 2, 3], vec![4, 5, 6], dest));
|
||||
}
|
||||
|
||||
batch.commit(dest)
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn one_simple_migration() {
|
||||
let dir = RandomTempPath::create_dir();
|
||||
@@ -189,3 +213,16 @@ fn is_migration_needed() {
|
||||
assert!(manager.is_needed(1));
|
||||
assert!(!manager.is_needed(2));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pre_columns() {
|
||||
let mut manager = Manager::new(Config::default());
|
||||
manager.add_migration(AddsColumn).unwrap();
|
||||
|
||||
let dir = RandomTempPath::create_dir();
|
||||
let db_path = db_path(dir.as_path());
|
||||
|
||||
// this shouldn't fail to open the database even though it's one column
|
||||
// short of the one before it.
|
||||
manager.execute(&db_path, 0).unwrap();
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ use target_info::Target;
|
||||
include!(concat!(env!("OUT_DIR"), "/version.rs"));
|
||||
include!(concat!(env!("OUT_DIR"), "/rustc_version.rs"));
|
||||
|
||||
#[derive(PartialEq,Eq,Clone,Copy)]
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||
/// Boolean type for clean/dirty status.
|
||||
pub enum Filth {
|
||||
/// Data has not been changed.
|
||||
|
||||
@@ -46,4 +46,4 @@ pub use rustc_serialize::hex::{FromHex, FromHexError};
|
||||
pub use heapsize::HeapSizeOf;
|
||||
pub use itertools::Itertools;
|
||||
|
||||
pub use parking_lot::{Condvar, Mutex, MutexGuard, RwLock, RwLockReadGuard, RwLockWriteGuard};
|
||||
pub use parking_lot::{Condvar, Mutex, MutexGuard, RwLock, RwLockReadGuard, RwLockWriteGuard};
|
||||
|
||||
@@ -233,4 +233,7 @@ impl TrieFactory {
|
||||
TrieSpec::Fat => Ok(Box::new(try!(FatDBMut::from_existing(db, root)))),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true iff the trie DB is a fat DB (allows enumeration of keys).
|
||||
pub fn is_fat(&self) -> bool { self.spec == TrieSpec::Fat }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user