use a database in ethcore-light

This commit is contained in:
Robert Habermeier 2017-03-21 20:57:13 +01:00
parent 21771aa1a6
commit bc9c1d4824
7 changed files with 88 additions and 16 deletions

1
Cargo.lock generated
View File

@ -550,6 +550,7 @@ name = "ethcore-light"
version = "1.7.0" version = "1.7.0"
dependencies = [ dependencies = [
"ethcore 1.7.0", "ethcore 1.7.0",
"ethcore-devtools 1.7.0",
"ethcore-io 1.7.0", "ethcore-io 1.7.0",
"ethcore-ipc 1.7.0", "ethcore-ipc 1.7.0",
"ethcore-ipc-codegen 1.7.0", "ethcore-ipc-codegen 1.7.0",

View File

@ -17,6 +17,7 @@ ethcore-util = { path = "../../util" }
ethcore-network = { path = "../../util/network" } ethcore-network = { path = "../../util/network" }
ethcore-io = { path = "../../util/io" } ethcore-io = { path = "../../util/io" }
ethcore-ipc = { path = "../../ipc/rpc", optional = true } ethcore-ipc = { path = "../../ipc/rpc", optional = true }
ethcore-devtools = { path = "../../devtools" }
rlp = { path = "../../util/rlp" } rlp = { path = "../../util/rlp" }
time = "0.1" time = "0.1"
smallvec = "0.3.1" smallvec = "0.3.1"

View File

@ -20,7 +20,7 @@ use std::sync::Arc;
use ethcore::block_import_error::BlockImportError; use ethcore::block_import_error::BlockImportError;
use ethcore::block_status::BlockStatus; use ethcore::block_status::BlockStatus;
use ethcore::client::{ClientReport, EnvInfo}; use ethcore::client::{ClientReport, EnvInfo, DatabaseCompactionProfile};
use ethcore::engines::Engine; use ethcore::engines::Engine;
use ethcore::ids::BlockId; use ethcore::ids::BlockId;
use ethcore::header::Header; use ethcore::header::Header;
@ -31,7 +31,7 @@ use ethcore::service::ClientIoMessage;
use ethcore::encoded; use ethcore::encoded;
use io::IoChannel; use io::IoChannel;
use util::{H256, Mutex, RwLock}; use util::{H256, Mutex, RwLock, KeyValueDB};
use self::header_chain::{AncestryIter, HeaderChain}; use self::header_chain::{AncestryIter, HeaderChain};
@ -45,6 +45,14 @@ mod service;
pub struct Config { pub struct Config {
/// Verification queue config. /// Verification queue config.
pub queue: queue::Config, pub queue: queue::Config,
/// Chain column in database.
pub chain_column: Option<u32>,
/// Database cache size. `None` => rocksdb default.
pub db_cache_size: Option<usize>,
/// State db compaction profile
pub db_compaction: DatabaseCompactionProfile,
/// Should db have WAL enabled?
pub db_wal: bool,
} }
/// Trait for interacting with the header chain abstractly. /// Trait for interacting with the header chain abstractly.
@ -106,22 +114,30 @@ pub struct Client {
chain: HeaderChain, chain: HeaderChain,
report: RwLock<ClientReport>, report: RwLock<ClientReport>,
import_lock: Mutex<()>, import_lock: Mutex<()>,
db: Arc<KeyValueDB>,
} }
impl Client { impl Client {
/// Create a new `Client`. /// Create a new `Client`.
pub fn new(config: Config, spec: &Spec, io_channel: IoChannel<ClientIoMessage>) -> Self { pub fn new(config: Config, db: Arc<KeyValueDB>, chain_col: Option<u32>, spec: &Spec, io_channel: IoChannel<ClientIoMessage>) -> Result<Self, String> {
// TODO: use real DB.
let db = ::util::kvdb::in_memory(0);
let gh = ::rlp::encode(&spec.genesis_header()); let gh = ::rlp::encode(&spec.genesis_header());
Client { Ok(Client {
queue: HeaderQueue::new(config.queue, spec.engine.clone(), io_channel, true), queue: HeaderQueue::new(config.queue, spec.engine.clone(), io_channel, true),
engine: spec.engine.clone(), engine: spec.engine.clone(),
chain: HeaderChain::new(Arc::new(db), None, &gh).expect("new db every time"), chain: HeaderChain::new(db.clone(), chain_col, &gh)?,
report: RwLock::new(ClientReport::default()), report: RwLock::new(ClientReport::default()),
import_lock: Mutex::new(()), import_lock: Mutex::new(()),
db: db,
})
} }
/// Create a new `Client` backed purely in-memory.
/// This will ignore all database options in the configuration.
pub fn in_memory(config: Config, spec: &Spec, io_channel: IoChannel<ClientIoMessage>) -> Self {
let db = ::util::kvdb::in_memory(0);
Client::new(config, Arc::new(db), None, spec, io_channel).expect("New DB creation infallible; qed")
} }
/// Import a header to the queue for additional verification. /// Import a header to the queue for additional verification.
@ -205,7 +221,7 @@ impl Client {
for verified_header in self.queue.drain(MAX) { for verified_header in self.queue.drain(MAX) {
let (num, hash) = (verified_header.number(), verified_header.hash()); let (num, hash) = (verified_header.number(), verified_header.hash());
let mut tx = unimplemented!(); let mut tx = self.db.transaction();
match self.chain.insert(&mut tx, verified_header) { match self.chain.insert(&mut tx, verified_header) {
Ok(()) => { Ok(()) => {
good.push(hash); good.push(hash);
@ -216,6 +232,11 @@ impl Client {
bad.push(hash); bad.push(hash);
} }
} }
self.db.write_buffered(tx);
if let Err(e) = self.db.flush() {
panic!("Database flush failed: {}. Check disk health and space.", e);
}
} }
self.queue.mark_as_bad(&bad); self.queue.mark_as_bad(&bad);

View File

@ -17,14 +17,36 @@
//! Minimal IO service for light client. //! Minimal IO service for light client.
//! Just handles block import messages and passes them to the client. //! Just handles block import messages and passes them to the client.
use std::fmt;
use std::path::Path;
use std::sync::Arc; use std::sync::Arc;
use ethcore::db;
use ethcore::service::ClientIoMessage; use ethcore::service::ClientIoMessage;
use ethcore::spec::Spec; use ethcore::spec::Spec;
use io::{IoContext, IoError, IoHandler, IoService}; use io::{IoContext, IoError, IoHandler, IoService};
use util::kvdb::{Database, DatabaseConfig};
use super::{Client, Config as ClientConfig}; use super::{Client, Config as ClientConfig};
/// Errors on service initialization.
#[derive(Debug)]
pub enum Error {
/// Database error.
Database(String),
/// I/O service error.
Io(IoError),
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Error::Database(ref msg) => write!(f, "Database error: {}", msg),
Error::Io(ref err) => write!(f, "I/O service error: {}", err),
}
}
}
/// Light client service. /// Light client service.
pub struct Service { pub struct Service {
client: Arc<Client>, client: Arc<Client>,
@ -33,11 +55,31 @@ pub struct Service {
impl Service { impl Service {
/// Start the service: initialize I/O workers and client itself. /// Start the service: initialize I/O workers and client itself.
pub fn start(config: ClientConfig, spec: &Spec) -> Result<Self, IoError> { pub fn start(config: ClientConfig, spec: &Spec, path: &Path) -> Result<Self, Error> {
let io_service = try!(IoService::<ClientIoMessage>::start()); // initialize database.
let client = Arc::new(Client::new(config, spec, io_service.channel())); let mut db_config = DatabaseConfig::with_columns(db::NUM_COLUMNS);
try!(io_service.register_handler(Arc::new(ImportBlocks(client.clone()))));
// give all rocksdb cache to the header chain column.
if let Some(size) = config.db_cache_size {
db_config.set_cache(db::COL_LIGHT_CHAIN, size);
}
db_config.compaction = config.db_compaction.compaction_profile(path);
db_config.wal = config.db_wal;
let db = Arc::new(Database::open(
&db_config,
&path.to_str().expect("DB path could not be converted to string.")
).map_err(Error::Database)?);
let io_service = IoService::<ClientIoMessage>::start().map_err(Error::Io)?;
let client = Arc::new(Client::new(config,
db,
db::COL_LIGHT_CHAIN,
spec,
io_service.channel(),
).map_err(Error::Database)?);
io_service.register_handler(Arc::new(ImportBlocks(client.clone()))).map_err(Error::Io)?;
Ok(Service { Ok(Service {
client: client, client: client,
_io_service: io_service, _io_service: io_service,
@ -63,11 +105,13 @@ impl IoHandler<ClientIoMessage> for ImportBlocks {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::Service; use super::Service;
use devtools::RandomTempPath;
use ethcore::spec::Spec; use ethcore::spec::Spec;
#[test] #[test]
fn it_works() { fn it_works() {
let spec = Spec::new_test(); let spec = Spec::new_test();
Service::start(Default::default(), &spec).unwrap(); let temp_path = RandomTempPath::new();
Service::start(Default::default(), &spec, temp_path.as_path()).unwrap();
} }
} }

View File

@ -76,3 +76,6 @@ extern crate stats;
#[cfg(feature = "ipc")] #[cfg(feature = "ipc")]
extern crate ethcore_ipc as ipc; extern crate ethcore_ipc as ipc;
#[cfg(test)]
extern crate ethcore_devtools as devtools;

View File

@ -26,7 +26,7 @@ use verification::{VerifierType, QueueConfig};
use util::{journaldb, CompactionProfile}; use util::{journaldb, CompactionProfile};
/// Client state db compaction profile /// Client state db compaction profile
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq, Clone)]
pub enum DatabaseCompactionProfile { pub enum DatabaseCompactionProfile {
/// Try to determine compaction profile automatically /// Try to determine compaction profile automatically
Auto, Auto,

View File

@ -38,8 +38,10 @@ pub const COL_TRACE: Option<u32> = Some(4);
pub const COL_ACCOUNT_BLOOM: Option<u32> = Some(5); pub const COL_ACCOUNT_BLOOM: Option<u32> = Some(5);
/// Column for general information from the local node which can persist. /// Column for general information from the local node which can persist.
pub const COL_NODE_INFO: Option<u32> = Some(6); pub const COL_NODE_INFO: Option<u32> = Some(6);
/// Column for the light client chain.
pub const COL_LIGHT_CHAIN: Option<u32> = Some(7);
/// Number of columns in DB /// Number of columns in DB
pub const NUM_COLUMNS: Option<u32> = Some(7); pub const NUM_COLUMNS: Option<u32> = Some(8);
/// Modes for updating caches. /// Modes for updating caches.
#[derive(Clone, Copy)] #[derive(Clone, Copy)]