Memory management

This commit is contained in:
arkpar 2016-02-25 14:09:39 +01:00
parent 1a73d70334
commit 781f763f1f
11 changed files with 167 additions and 83 deletions

10
Cargo.lock generated
View File

@ -14,6 +14,7 @@ dependencies = [
"ethsync 0.9.99", "ethsync 0.9.99",
"fdlimit 0.1.0", "fdlimit 0.1.0",
"log 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
"number_prefix 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-serialize 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)",
"time 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
@ -255,6 +256,7 @@ dependencies = [
"env_logger 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
"ethcore 0.9.99", "ethcore 0.9.99",
"ethcore-util 0.9.99", "ethcore-util 0.9.99",
"heapsize 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
"time 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)",
@ -526,6 +528,14 @@ dependencies = [
"libc 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]]
name = "number_prefix"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"num 0.1.31 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "odds" name = "odds"
version = "0.2.12" version = "0.2.12"

View File

@ -20,6 +20,7 @@ ethcore-rpc = { path = "rpc", optional = true }
fdlimit = { path = "util/fdlimit" } fdlimit = { path = "util/fdlimit" }
daemonize = "0.2" daemonize = "0.2"
ethcore-devtools = { path = "devtools" } ethcore-devtools = { path = "devtools" }
number_prefix = "0.2"
[features] [features]
default = ["rpc"] default = ["rpc"]

View File

@ -28,6 +28,28 @@ use service::*;
use client::BlockStatus; use client::BlockStatus;
use util::panics::*; use util::panics::*;
known_heap_size!(0, UnVerifiedBlock, VerifyingBlock, PreVerifiedBlock);
/// Block queue configuration
#[derive(Debug)]
pub struct BlockQueueConfig {
/// Maximum number of blocks to keep in unverified queue.
/// When the limit is reached, is_full returns true.
pub max_queue_size: usize,
/// Maximum heap memory to use.
/// When the limit is reached, is_full returns true.
pub max_mem_use: usize,
}
impl Default for BlockQueueConfig {
fn default() -> Self {
BlockQueueConfig {
max_queue_size: 30000,
max_mem_use: 50 * 1024 * 1024,
}
}
}
/// Block queue status /// Block queue status
#[derive(Debug)] #[derive(Debug)]
pub struct BlockQueueInfo { pub struct BlockQueueInfo {
@ -37,6 +59,12 @@ pub struct BlockQueueInfo {
pub verified_queue_size: usize, pub verified_queue_size: usize,
/// Number of blocks being verified /// Number of blocks being verified
pub verifying_queue_size: usize, pub verifying_queue_size: usize,
/// Configured maximum number of blocks in the queue
pub max_queue_size: usize,
/// Configured maximum number of bytes to use
pub max_mem_use: usize,
/// Heap memory used in bytes
pub mem_used: usize,
} }
impl BlockQueueInfo { impl BlockQueueInfo {
@ -48,7 +76,8 @@ impl BlockQueueInfo {
/// Indicates that queue is full /// Indicates that queue is full
pub fn is_full(&self) -> bool { pub fn is_full(&self) -> bool {
self.unverified_queue_size + self.verified_queue_size + self.verifying_queue_size > MAX_UNVERIFIED_QUEUE_SIZE self.unverified_queue_size + self.verified_queue_size + self.verifying_queue_size > self.max_queue_size ||
self.mem_used > self.max_mem_use
} }
/// Indicates that queue is empty /// Indicates that queue is empty
@ -68,7 +97,9 @@ pub struct BlockQueue {
deleting: Arc<AtomicBool>, deleting: Arc<AtomicBool>,
ready_signal: Arc<QueueSignal>, ready_signal: Arc<QueueSignal>,
empty: Arc<Condvar>, empty: Arc<Condvar>,
processing: RwLock<HashSet<H256>> processing: RwLock<HashSet<H256>>,
max_queue_size: usize,
max_mem_use: usize,
} }
struct UnVerifiedBlock { struct UnVerifiedBlock {
@ -106,11 +137,9 @@ struct Verification {
bad: HashSet<H256>, bad: HashSet<H256>,
} }
const MAX_UNVERIFIED_QUEUE_SIZE: usize = 50000;
impl BlockQueue { impl BlockQueue {
/// Creates a new queue instance. /// Creates a new queue instance.
pub fn new(engine: Arc<Box<Engine>>, message_channel: IoChannel<NetSyncMessage>) -> BlockQueue { pub fn new(config: BlockQueueConfig, engine: Arc<Box<Engine>>, message_channel: IoChannel<NetSyncMessage>) -> BlockQueue {
let verification = Arc::new(Mutex::new(Verification::default())); let verification = Arc::new(Mutex::new(Verification::default()));
let more_to_verify = Arc::new(Condvar::new()); let more_to_verify = Arc::new(Condvar::new());
let ready_signal = Arc::new(QueueSignal { signalled: AtomicBool::new(false), message_channel: message_channel }); let ready_signal = Arc::new(QueueSignal { signalled: AtomicBool::new(false), message_channel: message_channel });
@ -133,7 +162,7 @@ impl BlockQueue {
.name(format!("Verifier #{}", i)) .name(format!("Verifier #{}", i))
.spawn(move || { .spawn(move || {
panic_handler.catch_panic(move || { panic_handler.catch_panic(move || {
BlockQueue::verify(verification, engine, more_to_verify, ready_signal, deleting, empty) BlockQueue::verify(verification, engine, more_to_verify, ready_signal, deleting, empty)
}).unwrap() }).unwrap()
}) })
.expect("Error starting block verification thread") .expect("Error starting block verification thread")
@ -149,6 +178,8 @@ impl BlockQueue {
deleting: deleting.clone(), deleting: deleting.clone(),
processing: RwLock::new(HashSet::new()), processing: RwLock::new(HashSet::new()),
empty: empty.clone(), empty: empty.clone(),
max_queue_size: config.max_queue_size,
max_mem_use: config.max_mem_use,
} }
} }
@ -334,8 +365,26 @@ impl BlockQueue {
verified_queue_size: verification.verified.len(), verified_queue_size: verification.verified.len(),
unverified_queue_size: verification.unverified.len(), unverified_queue_size: verification.unverified.len(),
verifying_queue_size: verification.verifying.len(), verifying_queue_size: verification.verifying.len(),
max_queue_size: self.max_queue_size,
max_mem_use: self.max_mem_use,
mem_used:
verification.unverified.heap_size_of_children()
+ verification.verifying.heap_size_of_children()
+ verification.verified.heap_size_of_children(),
// TODO: https://github.com/servo/heapsize/pull/50
//+ self.processing.read().unwrap().heap_size_of_children(),
} }
} }
pub fn collect_garbage(&self) {
{
let mut verification = self.verification.lock().unwrap();
verification.unverified.shrink_to_fit();
verification.verifying.shrink_to_fit();
verification.verified.shrink_to_fit();
}
self.processing.write().unwrap().shrink_to_fit();
}
} }
impl MayPanic for BlockQueue { impl MayPanic for BlockQueue {
@ -367,7 +416,7 @@ mod tests {
fn get_test_queue() -> BlockQueue { fn get_test_queue() -> BlockQueue {
let spec = get_test_spec(); let spec = get_test_spec();
let engine = spec.to_engine().unwrap(); let engine = spec.to_engine().unwrap();
BlockQueue::new(Arc::new(engine), IoChannel::disconnected()) BlockQueue::new(BlockQueueConfig::default(), Arc::new(engine), IoChannel::disconnected())
} }
#[test] #[test]
@ -375,7 +424,7 @@ mod tests {
// TODO better test // TODO better test
let spec = Spec::new_test(); let spec = Spec::new_test();
let engine = spec.to_engine().unwrap(); let engine = spec.to_engine().unwrap();
let _ = BlockQueue::new(Arc::new(engine), IoChannel::disconnected()); let _ = BlockQueue::new(BlockQueueConfig::default(), Arc::new(engine), IoChannel::disconnected());
} }
#[test] #[test]

View File

@ -23,6 +23,24 @@ use extras::*;
use transaction::*; use transaction::*;
use views::*; use views::*;
/// Blockchain configuration.
#[derive(Debug)]
pub struct BlockChainConfig {
/// Preferred cache size in bytes.
pub pref_cache_size: usize,
/// Maximum cache size in bytes.
pub max_cache_size: usize,
}
impl Default for BlockChainConfig {
fn default() -> Self {
BlockChainConfig {
pref_cache_size: 1 << 14,
max_cache_size: 1 << 20,
}
}
}
/// Represents a tree route between `from` block and `to` block: /// Represents a tree route between `from` block and `to` block:
pub struct TreeRoute { pub struct TreeRoute {
/// A vector of hashes of all blocks, ordered from `from` to `to`. /// A vector of hashes of all blocks, ordered from `from` to `to`.
@ -50,7 +68,7 @@ pub struct CacheSize {
impl CacheSize { impl CacheSize {
/// Total amount used by the cache. /// Total amount used by the cache.
fn total(&self) -> usize { self.blocks + self.block_details + self.transaction_addresses + self.block_logs + self.blocks_blooms } pub fn total(&self) -> usize { self.blocks + self.block_details + self.transaction_addresses + self.block_logs + self.blocks_blooms }
} }
/// Information about best block gathered together /// Information about best block gathered together
@ -220,33 +238,7 @@ const COLLECTION_QUEUE_SIZE: usize = 8;
impl BlockChain { impl BlockChain {
/// Create new instance of blockchain from given Genesis /// Create new instance of blockchain from given Genesis
/// pub fn new(config: BlockChainConfig, genesis: &[u8], path: &Path) -> BlockChain {
/// ```rust
/// extern crate ethcore_util as util;
/// extern crate ethcore;
/// use std::env;
/// use std::str::FromStr;
/// use ethcore::spec::*;
/// use ethcore::blockchain::*;
/// use ethcore::ethereum;
/// use util::hash::*;
/// use util::uint::*;
///
/// fn main() {
/// let spec = ethereum::new_frontier();
///
/// let mut dir = env::temp_dir();
/// dir.push(H32::random().hex());
///
/// let bc = BlockChain::new(&spec.genesis_block(), &dir);
///
/// let genesis_hash = "d4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3";
/// assert_eq!(bc.genesis_hash(), H256::from_str(genesis_hash).unwrap());
/// assert!(bc.is_known(&bc.genesis_hash()));
/// assert_eq!(bc.genesis_hash(), bc.block_hash(0).unwrap());
/// }
/// ```
pub fn new(genesis: &[u8], path: &Path) -> BlockChain {
// open extras db // open extras db
let mut extras_path = path.to_path_buf(); let mut extras_path = path.to_path_buf();
extras_path.push("extras"); extras_path.push("extras");
@ -261,8 +253,8 @@ impl BlockChain {
(0..COLLECTION_QUEUE_SIZE).foreach(|_| cache_man.cache_usage.push_back(HashSet::new())); (0..COLLECTION_QUEUE_SIZE).foreach(|_| cache_man.cache_usage.push_back(HashSet::new()));
let bc = BlockChain { let bc = BlockChain {
pref_cache_size: 1 << 14, pref_cache_size: config.pref_cache_size,
max_cache_size: 1 << 20, max_cache_size: config.max_cache_size,
best_block: RwLock::new(BestBlock::new()), best_block: RwLock::new(BestBlock::new()),
blocks: RwLock::new(HashMap::new()), blocks: RwLock::new(HashMap::new()),
block_details: RwLock::new(HashMap::new()), block_details: RwLock::new(HashMap::new()),
@ -536,6 +528,8 @@ impl BlockChain {
} }
/// Returns true if transaction is known. /// Returns true if transaction is known.
// TODO: Use me
#[allow(dead_code)]
pub fn is_known_transaction(&self, hash: &H256) -> bool { pub fn is_known_transaction(&self, hash: &H256) -> bool {
self.query_extras_exist(hash, &self.transaction_addresses) self.query_extras_exist(hash, &self.transaction_addresses)
} }
@ -556,6 +550,8 @@ impl BlockChain {
} }
/// Get the transactions' log blooms of a block. /// Get the transactions' log blooms of a block.
// TODO: Use me
#[allow(dead_code)]
pub fn log_blooms(&self, hash: &H256) -> Option<BlockLogBlooms> { pub fn log_blooms(&self, hash: &H256) -> Option<BlockLogBlooms> {
self.query_extras(hash, &self.block_logs) self.query_extras(hash, &self.block_logs)
} }
@ -671,7 +667,7 @@ mod tests {
let genesis = "f901fcf901f7a00000000000000000000000000000000000000000000000000000000000000000a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a0925002c3260b44e44c3edebad1cc442142b03020209df1ab8bb86752edbd2cd7a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302000080832fefd8808454c98c8142a0363659b251bf8b819179874c8cce7b9b983d7f3704cbb58a3b334431f7032871889032d09c281e1236c0c0".from_hex().unwrap(); let genesis = "f901fcf901f7a00000000000000000000000000000000000000000000000000000000000000000a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a0925002c3260b44e44c3edebad1cc442142b03020209df1ab8bb86752edbd2cd7a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302000080832fefd8808454c98c8142a0363659b251bf8b819179874c8cce7b9b983d7f3704cbb58a3b334431f7032871889032d09c281e1236c0c0".from_hex().unwrap();
let temp = RandomTempPath::new(); let temp = RandomTempPath::new();
let bc = BlockChain::new(&genesis, temp.as_path()); let bc = BlockChain::new(BlockChainConfig::default(), &genesis, temp.as_path());
let genesis_hash = H256::from_str("3caa2203f3d7c136c0295ed128a7d31cea520b1ca5e27afe17d0853331798942").unwrap(); let genesis_hash = H256::from_str("3caa2203f3d7c136c0295ed128a7d31cea520b1ca5e27afe17d0853331798942").unwrap();
@ -715,7 +711,7 @@ mod tests {
let best_block_hash = H256::from_str("c208f88c9f5bf7e00840439742c12e5226d9752981f3ec0521bdcb6dd08af277").unwrap(); let best_block_hash = H256::from_str("c208f88c9f5bf7e00840439742c12e5226d9752981f3ec0521bdcb6dd08af277").unwrap();
let temp = RandomTempPath::new(); let temp = RandomTempPath::new();
let bc = BlockChain::new(&genesis, temp.as_path()); let bc = BlockChain::new(BlockChainConfig::default(), &genesis, temp.as_path());
bc.insert_block(&b1); bc.insert_block(&b1);
bc.insert_block(&b2); bc.insert_block(&b2);
bc.insert_block(&b3a); bc.insert_block(&b3a);
@ -794,14 +790,14 @@ mod tests {
let temp = RandomTempPath::new(); let temp = RandomTempPath::new();
{ {
let bc = BlockChain::new(&genesis, temp.as_path()); let bc = BlockChain::new(BlockChainConfig::default(), &genesis, temp.as_path());
assert_eq!(bc.best_block_hash(), genesis_hash); assert_eq!(bc.best_block_hash(), genesis_hash);
bc.insert_block(&b1); bc.insert_block(&b1);
assert_eq!(bc.best_block_hash(), b1_hash); assert_eq!(bc.best_block_hash(), b1_hash);
} }
{ {
let bc = BlockChain::new(&genesis, temp.as_path()); let bc = BlockChain::new(BlockChainConfig::default(), &genesis, temp.as_path());
assert_eq!(bc.best_block_hash(), b1_hash); assert_eq!(bc.best_block_hash(), b1_hash);
} }
} }
@ -854,7 +850,7 @@ mod tests {
let b1_hash = H256::from_str("f53f268d23a71e85c7d6d83a9504298712b84c1a2ba220441c86eeda0bf0b6e3").unwrap(); let b1_hash = H256::from_str("f53f268d23a71e85c7d6d83a9504298712b84c1a2ba220441c86eeda0bf0b6e3").unwrap();
let temp = RandomTempPath::new(); let temp = RandomTempPath::new();
let bc = BlockChain::new(&genesis, temp.as_path()); let bc = BlockChain::new(BlockChainConfig::default(), &genesis, temp.as_path());
bc.insert_block(&b1); bc.insert_block(&b1);
let transactions = bc.transactions(&b1_hash).unwrap(); let transactions = bc.transactions(&b1_hash).unwrap();

View File

@ -19,7 +19,7 @@
use util::*; use util::*;
use util::panics::*; use util::panics::*;
use rocksdb::{Options, DB, DBCompactionStyle}; use rocksdb::{Options, DB, DBCompactionStyle};
use blockchain::{BlockChain, BlockProvider, CacheSize}; use blockchain::{BlockChain, BlockProvider};
use views::BlockView; use views::BlockView;
use error::*; use error::*;
use header::BlockNumber; use header::BlockNumber;
@ -27,14 +27,16 @@ use state::State;
use spec::Spec; use spec::Spec;
use engine::Engine; use engine::Engine;
use views::HeaderView; use views::HeaderView;
use block_queue::{BlockQueue, BlockQueueInfo}; use block_queue::BlockQueue;
use service::{NetSyncMessage, SyncMessage}; use service::{NetSyncMessage, SyncMessage};
use env_info::LastHashes; use env_info::LastHashes;
use verification::*; use verification::*;
use block::*; use block::*;
use transaction::LocalizedTransaction; use transaction::LocalizedTransaction;
use extras::TransactionAddress; use extras::TransactionAddress;
pub use blockchain::TreeRoute; pub use block_queue::{BlockQueueConfig, BlockQueueInfo};
pub use blockchain::{TreeRoute, BlockChainConfig, CacheSize as BlockChainCacheSize};
/// Uniquely identifies block. /// Uniquely identifies block.
#[derive(Debug, PartialEq, Clone)] #[derive(Debug, PartialEq, Clone)]
@ -73,7 +75,16 @@ pub enum BlockStatus {
Unknown, Unknown,
} }
/// Information about the blockchain gthered together. /// Client configuration. Includes configs for all sub-systems.
#[derive(Debug, Default)]
pub struct ClientConfig {
/// Block queue configuration.
pub queue: BlockQueueConfig,
/// Blockchain configuration.
pub blockchain: BlockChainConfig,
}
/// Information about the blockchain gathered together.
#[derive(Debug)] #[derive(Debug)]
pub struct BlockChainInfo { pub struct BlockChainInfo {
/// Blockchain difficulty. /// Blockchain difficulty.
@ -183,14 +194,14 @@ const CLIENT_DB_VER_STR: &'static str = "2.1";
impl Client { impl Client {
/// Create a new client with given spec and DB path. /// Create a new client with given spec and DB path.
pub fn new(spec: Spec, path: &Path, message_channel: IoChannel<NetSyncMessage> ) -> Result<Arc<Client>, Error> { pub fn new(config: ClientConfig, spec: Spec, path: &Path, message_channel: IoChannel<NetSyncMessage> ) -> Result<Arc<Client>, Error> {
let mut dir = path.to_path_buf(); let mut dir = path.to_path_buf();
dir.push(H64::from(spec.genesis_header().hash()).hex()); dir.push(H64::from(spec.genesis_header().hash()).hex());
//TODO: sec/fat: pruned/full versioning //TODO: sec/fat: pruned/full versioning
dir.push(format!("v{}-sec-pruned", CLIENT_DB_VER_STR)); dir.push(format!("v{}-sec-pruned", CLIENT_DB_VER_STR));
let path = dir.as_path(); let path = dir.as_path();
let gb = spec.genesis_block(); let gb = spec.genesis_block();
let chain = Arc::new(RwLock::new(BlockChain::new(&gb, path))); let chain = Arc::new(RwLock::new(BlockChain::new(config.blockchain, &gb, path)));
let mut opts = Options::new(); let mut opts = Options::new();
opts.set_max_open_files(256); opts.set_max_open_files(256);
opts.create_if_missing(true); opts.create_if_missing(true);
@ -223,7 +234,7 @@ impl Client {
state_db.commit(0, &engine.spec().genesis_header().hash(), None).expect("Error commiting genesis state to state DB"); state_db.commit(0, &engine.spec().genesis_header().hash(), None).expect("Error commiting genesis state to state DB");
} }
let block_queue = BlockQueue::new(engine.clone(), message_channel); let block_queue = BlockQueue::new(config.queue, engine.clone(), message_channel);
let panic_handler = PanicHandler::new_in_arc(); let panic_handler = PanicHandler::new_in_arc();
panic_handler.forward_from(&block_queue); panic_handler.forward_from(&block_queue);
@ -330,7 +341,7 @@ impl Client {
} }
/// Get info on the cache. /// Get info on the cache.
pub fn cache_info(&self) -> CacheSize { pub fn blockchain_cache_info(&self) -> BlockChainCacheSize {
self.chain.read().unwrap().cache_size() self.chain.read().unwrap().cache_size()
} }
@ -342,6 +353,7 @@ impl Client {
/// Tick the client. /// Tick the client.
pub fn tick(&self) { pub fn tick(&self) {
self.chain.read().unwrap().collect_garbage(); self.chain.read().unwrap().collect_garbage();
self.block_queue.read().unwrap().collect_garbage();
} }
/// Set up the cache behaviour. /// Set up the cache behaviour.

View File

@ -85,7 +85,7 @@
#[macro_use] extern crate lazy_static; #[macro_use] extern crate lazy_static;
extern crate rustc_serialize; extern crate rustc_serialize;
extern crate rocksdb; extern crate rocksdb;
extern crate heapsize; #[macro_use] extern crate heapsize;
extern crate crypto; extern crate crypto;
extern crate time; extern crate time;
extern crate env_logger; extern crate env_logger;
@ -96,8 +96,6 @@ extern crate crossbeam;
#[cfg(feature = "jit" )] extern crate evmjit; #[cfg(feature = "jit" )] extern crate evmjit;
pub mod block; pub mod block;
pub mod blockchain;
pub mod block_queue;
pub mod client; pub mod client;
pub mod error; pub mod error;
pub mod ethereum; pub mod ethereum;
@ -129,6 +127,8 @@ mod substate;
mod executive; mod executive;
mod externalities; mod externalities;
mod verification; mod verification;
mod block_queue;
mod blockchain;
#[cfg(test)] #[cfg(test)]
mod tests; mod tests;

View File

@ -20,7 +20,7 @@ use util::*;
use util::panics::*; use util::panics::*;
use spec::Spec; use spec::Spec;
use error::*; use error::*;
use client::Client; use client::{Client, ClientConfig};
/// Message type for external and internal events /// Message type for external and internal events
#[derive(Clone)] #[derive(Clone)]
@ -43,14 +43,14 @@ pub struct ClientService {
impl ClientService { impl ClientService {
/// Start the service in a separate thread. /// Start the service in a separate thread.
pub fn start(spec: Spec, net_config: NetworkConfiguration, db_path: &Path) -> Result<ClientService, Error> { pub fn start(config: ClientConfig, spec: Spec, net_config: NetworkConfiguration, db_path: &Path) -> Result<ClientService, Error> {
let panic_handler = PanicHandler::new_in_arc(); let panic_handler = PanicHandler::new_in_arc();
let mut net_service = try!(NetworkService::start(net_config)); let mut net_service = try!(NetworkService::start(net_config));
panic_handler.forward_from(&net_service); panic_handler.forward_from(&net_service);
info!("Starting {}", net_service.host_info()); info!("Starting {}", net_service.host_info());
info!("Configured for {} using {} engine", spec.name, spec.engine_name); info!("Configured for {} using {} engine", spec.name, spec.engine_name);
let client = try!(Client::new(spec, db_path, net_service.io().channel())); let client = try!(Client::new(config, spec, db_path, net_service.io().channel()));
panic_handler.forward_from(client.deref()); panic_handler.forward_from(client.deref());
let client_io = Arc::new(ClientIoHandler { let client_io = Arc::new(ClientIoHandler {
client: client.clone() client: client.clone()
@ -130,12 +130,13 @@ mod tests {
use tests::helpers::*; use tests::helpers::*;
use util::network::*; use util::network::*;
use devtools::*; use devtools::*;
use client::ClientConfig;
#[test] #[test]
fn it_can_be_started() { fn it_can_be_started() {
let spec = get_test_spec(); let spec = get_test_spec();
let temp_path = RandomTempPath::new(); let temp_path = RandomTempPath::new();
let service = ClientService::start(spec, NetworkConfiguration::new_with_port(40456), &temp_path.as_path()); let service = ClientService::start(ClientConfig::default(), spec, NetworkConfiguration::new_with_port(40456), &temp_path.as_path());
assert!(service.is_ok()); assert!(service.is_ok());
} }
} }

View File

@ -14,7 +14,7 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>. // along with Parity. If not, see <http://www.gnu.org/licenses/>.
use client::{BlockChainClient, Client, BlockId}; use client::{BlockChainClient, Client, ClientConfig, BlockId};
use tests::helpers::*; use tests::helpers::*;
use common::*; use common::*;
use devtools::*; use devtools::*;
@ -22,14 +22,14 @@ use devtools::*;
#[test] #[test]
fn created() { fn created() {
let dir = RandomTempPath::new(); let dir = RandomTempPath::new();
let client_result = Client::new(get_test_spec(), dir.as_path(), IoChannel::disconnected()); let client_result = Client::new(ClientConfig::default(), get_test_spec(), dir.as_path(), IoChannel::disconnected());
assert!(client_result.is_ok()); assert!(client_result.is_ok());
} }
#[test] #[test]
fn imports_from_empty() { fn imports_from_empty() {
let dir = RandomTempPath::new(); let dir = RandomTempPath::new();
let client = Client::new(get_test_spec(), dir.as_path(), IoChannel::disconnected()).unwrap(); let client = Client::new(ClientConfig::default(), get_test_spec(), dir.as_path(), IoChannel::disconnected()).unwrap();
client.import_verified_blocks(&IoChannel::disconnected()); client.import_verified_blocks(&IoChannel::disconnected());
client.flush_queue(); client.flush_queue();
} }
@ -37,7 +37,7 @@ fn imports_from_empty() {
#[test] #[test]
fn imports_good_block() { fn imports_good_block() {
let dir = RandomTempPath::new(); let dir = RandomTempPath::new();
let client = Client::new(get_test_spec(), dir.as_path(), IoChannel::disconnected()).unwrap(); let client = Client::new(ClientConfig::default(), get_test_spec(), dir.as_path(), IoChannel::disconnected()).unwrap();
let good_block = get_good_dummy_block(); let good_block = get_good_dummy_block();
if let Err(_) = client.import_block(good_block) { if let Err(_) = client.import_block(good_block) {
panic!("error importing block being good by definition"); panic!("error importing block being good by definition");
@ -52,7 +52,7 @@ fn imports_good_block() {
#[test] #[test]
fn query_none_block() { fn query_none_block() {
let dir = RandomTempPath::new(); let dir = RandomTempPath::new();
let client = Client::new(get_test_spec(), dir.as_path(), IoChannel::disconnected()).unwrap(); let client = Client::new(ClientConfig::default(), get_test_spec(), dir.as_path(), IoChannel::disconnected()).unwrap();
let non_existant = client.block_header(BlockId::Number(188)); let non_existant = client.block_header(BlockId::Number(188));
assert!(non_existant.is_none()); assert!(non_existant.is_none());
@ -104,5 +104,5 @@ fn can_collect_garbage() {
let client_result = generate_dummy_client(100); let client_result = generate_dummy_client(100);
let client = client_result.reference(); let client = client_result.reference();
client.tick(); client.tick();
assert!(client.cache_info().blocks < 100 * 1024); assert!(client.blockchain_cache_info().blocks < 100 * 1024);
} }

View File

@ -14,10 +14,10 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>. // along with Parity. If not, see <http://www.gnu.org/licenses/>.
use client::{BlockChainClient, Client}; use client::{BlockChainClient, Client, ClientConfig};
use common::*; use common::*;
use spec::*; use spec::*;
use blockchain::{BlockChain}; use blockchain::{BlockChain, BlockChainConfig};
use state::*; use state::*;
use rocksdb::*; use rocksdb::*;
use evm::{Schedule, Factory}; use evm::{Schedule, Factory};
@ -135,7 +135,7 @@ pub fn create_test_block_with_data(header: &Header, transactions: &[&SignedTrans
pub fn generate_dummy_client(block_number: u32) -> GuardedTempResult<Arc<Client>> { pub fn generate_dummy_client(block_number: u32) -> GuardedTempResult<Arc<Client>> {
let dir = RandomTempPath::new(); let dir = RandomTempPath::new();
let client = Client::new(get_test_spec(), dir.as_path(), IoChannel::disconnected()).unwrap(); let client = Client::new(ClientConfig::default(), get_test_spec(), dir.as_path(), IoChannel::disconnected()).unwrap();
let test_spec = get_test_spec(); let test_spec = get_test_spec();
let test_engine = test_spec.to_engine().unwrap(); let test_engine = test_spec.to_engine().unwrap();
let state_root = test_engine.spec().genesis_header().state_root; let state_root = test_engine.spec().genesis_header().state_root;
@ -173,7 +173,7 @@ pub fn generate_dummy_client(block_number: u32) -> GuardedTempResult<Arc<Client>
pub fn get_test_client_with_blocks(blocks: Vec<Bytes>) -> GuardedTempResult<Arc<Client>> { pub fn get_test_client_with_blocks(blocks: Vec<Bytes>) -> GuardedTempResult<Arc<Client>> {
let dir = RandomTempPath::new(); let dir = RandomTempPath::new();
let client = Client::new(get_test_spec(), dir.as_path(), IoChannel::disconnected()).unwrap(); let client = Client::new(ClientConfig::default(), get_test_spec(), dir.as_path(), IoChannel::disconnected()).unwrap();
for block in &blocks { for block in &blocks {
if let Err(_) = client.import_block(block.clone()) { if let Err(_) = client.import_block(block.clone()) {
panic!("panic importing block which is well-formed"); panic!("panic importing block which is well-formed");
@ -190,7 +190,7 @@ pub fn get_test_client_with_blocks(blocks: Vec<Bytes>) -> GuardedTempResult<Arc<
pub fn generate_dummy_blockchain(block_number: u32) -> GuardedTempResult<BlockChain> { pub fn generate_dummy_blockchain(block_number: u32) -> GuardedTempResult<BlockChain> {
let temp = RandomTempPath::new(); let temp = RandomTempPath::new();
let bc = BlockChain::new(&create_unverifiable_block(0, H256::zero()), temp.as_path()); let bc = BlockChain::new(BlockChainConfig::default(), &create_unverifiable_block(0, H256::zero()), temp.as_path());
for block_order in 1..block_number { for block_order in 1..block_number {
bc.insert_block(&create_unverifiable_block(block_order, bc.best_block_hash())); bc.insert_block(&create_unverifiable_block(block_order, bc.best_block_hash()));
} }
@ -203,7 +203,7 @@ pub fn generate_dummy_blockchain(block_number: u32) -> GuardedTempResult<BlockCh
pub fn generate_dummy_blockchain_with_extra(block_number: u32) -> GuardedTempResult<BlockChain> { pub fn generate_dummy_blockchain_with_extra(block_number: u32) -> GuardedTempResult<BlockChain> {
let temp = RandomTempPath::new(); let temp = RandomTempPath::new();
let bc = BlockChain::new(&create_unverifiable_block(0, H256::zero()), temp.as_path()); let bc = BlockChain::new(BlockChainConfig::default(), &create_unverifiable_block(0, H256::zero()), temp.as_path());
for block_order in 1..block_number { for block_order in 1..block_number {
bc.insert_block(&create_unverifiable_block_with_extra(block_order, bc.best_block_hash(), None)); bc.insert_block(&create_unverifiable_block_with_extra(block_order, bc.best_block_hash(), None));
} }
@ -216,7 +216,7 @@ pub fn generate_dummy_blockchain_with_extra(block_number: u32) -> GuardedTempRes
pub fn generate_dummy_empty_blockchain() -> GuardedTempResult<BlockChain> { pub fn generate_dummy_empty_blockchain() -> GuardedTempResult<BlockChain> {
let temp = RandomTempPath::new(); let temp = RandomTempPath::new();
let bc = BlockChain::new(&create_unverifiable_block(0, H256::zero()), temp.as_path()); let bc = BlockChain::new(BlockChainConfig::default(), &create_unverifiable_block(0, H256::zero()), temp.as_path());
GuardedTempResult::<BlockChain> { GuardedTempResult::<BlockChain> {
_temp: temp, _temp: temp,

View File

@ -31,6 +31,7 @@ extern crate ctrlc;
extern crate fdlimit; extern crate fdlimit;
extern crate daemonize; extern crate daemonize;
extern crate time; extern crate time;
extern crate number_prefix;
#[cfg(feature = "rpc")] #[cfg(feature = "rpc")]
extern crate ethcore_rpc as rpc; extern crate ethcore_rpc as rpc;
@ -47,10 +48,10 @@ use ethcore::spec::*;
use ethcore::client::*; use ethcore::client::*;
use ethcore::service::{ClientService, NetSyncMessage}; use ethcore::service::{ClientService, NetSyncMessage};
use ethcore::ethereum; use ethcore::ethereum;
use ethcore::blockchain::CacheSize;
use ethsync::{EthSync, SyncConfig}; use ethsync::{EthSync, SyncConfig};
use docopt::Docopt; use docopt::Docopt;
use daemonize::Daemonize; use daemonize::Daemonize;
use number_prefix::{binary_prefix, Standalone, Prefixed};
const USAGE: &'static str = " const USAGE: &'static str = "
Parity. Ethereum Client. Parity. Ethereum Client.
@ -78,6 +79,7 @@ Options:
--cache-pref-size BYTES Specify the prefered size of the blockchain cache in bytes [default: 16384]. --cache-pref-size BYTES Specify the prefered size of the blockchain cache in bytes [default: 16384].
--cache-max-size BYTES Specify the maximum size of the blockchain cache in bytes [default: 262144]. --cache-max-size BYTES Specify the maximum size of the blockchain cache in bytes [default: 262144].
--queue-max-size BYTES Specify the maximum size of memory to use for block queue [default: 52428800].
-j --jsonrpc Enable the JSON-RPC API sever. -j --jsonrpc Enable the JSON-RPC API sever.
--jsonrpc-url URL Specify URL for JSON-RPC API server [default: 127.0.0.1:8545]. --jsonrpc-url URL Specify URL for JSON-RPC API server [default: 127.0.0.1:8545].
@ -105,6 +107,7 @@ struct Args {
flag_node_key: Option<String>, flag_node_key: Option<String>,
flag_cache_pref_size: usize, flag_cache_pref_size: usize,
flag_cache_max_size: usize, flag_cache_max_size: usize,
flag_queue_max_size: usize,
flag_jsonrpc: bool, flag_jsonrpc: bool,
flag_jsonrpc_url: String, flag_jsonrpc_url: String,
flag_logging: Option<String>, flag_logging: Option<String>,
@ -285,9 +288,12 @@ impl Configuration {
sync_config.network_id = spec.network_id(); sync_config.network_id = spec.network_id();
// Build client // Build client
let mut service = ClientService::start(spec, net_settings, &Path::new(&self.path())).unwrap(); let mut client_config = ClientConfig::default();
client_config.blockchain.pref_cache_size = self.args.flag_cache_pref_size;
client_config.blockchain.max_cache_size = self.args.flag_cache_max_size;
client_config.queue.max_mem_use = self.args.flag_queue_max_size;
let mut service = ClientService::start(client_config, spec, net_settings, &Path::new(&self.path())).unwrap();
let client = service.client().clone(); let client = service.client().clone();
client.configure_cache(self.args.flag_cache_pref_size, self.args.flag_cache_max_size);
// Sync // Sync
let sync = EthSync::register(service.network(), sync_config, client); let sync = EthSync::register(service.network(), sync_config, client);
@ -331,7 +337,7 @@ fn main() {
struct Informant { struct Informant {
chain_info: RwLock<Option<BlockChainInfo>>, chain_info: RwLock<Option<BlockChainInfo>>,
cache_info: RwLock<Option<CacheSize>>, cache_info: RwLock<Option<BlockChainCacheSize>>,
report: RwLock<Option<ClientReport>>, report: RwLock<Option<ClientReport>>,
} }
@ -346,18 +352,26 @@ impl Default for Informant {
} }
impl Informant { impl Informant {
fn format_bytes(b: usize) -> String {
match binary_prefix(b as f64) {
Standalone(bytes) => format!("{} bytes", bytes),
Prefixed(prefix, n) => format!("{:.0} {}B", n, prefix),
}
}
pub fn tick(&self, client: &Client, sync: &EthSync) { pub fn tick(&self, client: &Client, sync: &EthSync) {
// 5 seconds betwen calls. TODO: calculate this properly. // 5 seconds betwen calls. TODO: calculate this properly.
let dur = 5usize; let dur = 5usize;
let chain_info = client.chain_info(); let chain_info = client.chain_info();
let queue_info = client.queue_info(); let queue_info = client.queue_info();
let cache_info = client.cache_info(); let cache_info = client.blockchain_cache_info();
let report = client.report(); let report = client.report();
let sync_info = sync.status(); let sync_info = sync.status();
if let (_, &Some(ref last_cache_info), &Some(ref last_report)) = (self.chain_info.read().unwrap().deref(), self.cache_info.read().unwrap().deref(), self.report.read().unwrap().deref()) { if let (_, _, &Some(ref last_report)) = (self.chain_info.read().unwrap().deref(), self.cache_info.read().unwrap().deref(), self.report.read().unwrap().deref()) {
println!("[ #{} {} ]---[ {} blk/s | {} tx/s | {} gas/s //··· {}/{} peers, #{}, {}+{} queued ···// {} ({}) bl {} ({}) ex ]", println!("[ #{} {} ]---[ {} blk/s | {} tx/s | {} gas/s //··· {}/{} peers, #{}, {}+{} queued ···// mem: {} chain, {} queue, {} sync ]",
chain_info.best_block_number, chain_info.best_block_number,
chain_info.best_block_hash, chain_info.best_block_hash,
(report.blocks_imported - last_report.blocks_imported) / dur, (report.blocks_imported - last_report.blocks_imported) / dur,
@ -370,10 +384,9 @@ impl Informant {
queue_info.unverified_queue_size, queue_info.unverified_queue_size,
queue_info.verified_queue_size, queue_info.verified_queue_size,
cache_info.blocks, Informant::format_bytes(cache_info.total()),
cache_info.blocks as isize - last_cache_info.blocks as isize, Informant::format_bytes(queue_info.mem_used),
cache_info.block_details, Informant::format_bytes(sync_info.mem_used),
cache_info.block_details as isize - last_cache_info.block_details as isize
); );
} }

View File

@ -16,7 +16,7 @@
use util::*; use util::*;
use ethcore::client::{BlockChainClient, BlockStatus, TreeRoute, BlockChainInfo, TransactionId, BlockId}; use ethcore::client::{BlockChainClient, BlockStatus, TreeRoute, BlockChainInfo, TransactionId, BlockId};
use ethcore::block_queue::BlockQueueInfo; use ethcore::BlockQueueInfo;
use ethcore::header::{Header as BlockHeader, BlockNumber}; use ethcore::header::{Header as BlockHeader, BlockNumber};
use ethcore::error::*; use ethcore::error::*;
use io::SyncIo; use io::SyncIo;
@ -242,6 +242,8 @@ impl BlockChainClient for TestBlockChainClient {
verified_queue_size: 0, verified_queue_size: 0,
unverified_queue_size: 0, unverified_queue_size: 0,
verifying_queue_size: 0, verifying_queue_size: 0,
max_unverified: 0,
mem_used: 0,
} }
} }