sort peers by response time before sending blocks requests

This commit is contained in:
Svyatoslav Nikolsky 2016-12-26 17:21:04 +03:00
parent 40ace5b0d4
commit c23b0f55a4
4 changed files with 333 additions and 272 deletions

View File

@ -19,7 +19,7 @@ use primitives::hash::H256;
use verification::BackwardsCompatibleChainVerifier as ChainVerifier; use verification::BackwardsCompatibleChainVerifier as ChainVerifier;
use synchronization_chain::{Chain, BlockState, TransactionState, BlockInsertionResult}; use synchronization_chain::{Chain, BlockState, TransactionState, BlockInsertionResult};
use synchronization_executor::{Task, TaskExecutor}; use synchronization_executor::{Task, TaskExecutor};
use synchronization_manager::{manage_synchronization_peers_blocks, manage_synchronization_peers_inventory, use synchronization_manager::{manage_synchronization_peers_blocks, manage_synchronization_peers_headers,
manage_unknown_orphaned_blocks, manage_orphaned_transactions, MANAGEMENT_INTERVAL_MS, manage_unknown_orphaned_blocks, manage_orphaned_transactions, MANAGEMENT_INTERVAL_MS,
ManagePeersConfig, ManageUnknownBlocksConfig, ManageOrphanTransactionsConfig}; ManagePeersConfig, ManageUnknownBlocksConfig, ManageOrphanTransactionsConfig};
use synchronization_peers_tasks::PeersTasks; use synchronization_peers_tasks::PeersTasks;
@ -261,7 +261,7 @@ impl<T> ClientCore for SynchronizationClientCore<T> where T: TaskExecutor {
let mut headers: Vec<_> = message.headers.into_iter().map(IndexedBlockHeader::from).collect(); let mut headers: Vec<_> = message.headers.into_iter().map(IndexedBlockHeader::from).collect();
// update peers to select next tasks // update peers to select next tasks
self.peers_tasks.on_inventory_received(peer_index); self.peers_tasks.on_headers_received(peer_index);
// headers are ordered // headers are ordered
// => if we know nothing about headers[0].parent // => if we know nothing about headers[0].parent
@ -459,13 +459,15 @@ impl<T> ClientCore for SynchronizationClientCore<T> where T: TaskExecutor {
} }
// we only interested in blocks, which we were asking before // we only interested in blocks, which we were asking before
if let Some(requested_blocks) = self.peers_tasks.get_blocks_tasks(peer_index) { let is_requested_block = if let Some(requested_blocks) = self.peers_tasks.get_blocks_tasks(peer_index) {
// check if peer has responded with notfound to requested blocks // check if peer has responded with notfound to requested blocks
if requested_blocks.intersection(&notfound_blocks).nth(0).is_none() { // if notfound some other blocks => just ignore the message
// if notfound some other blocks => just ignore the message requested_blocks.intersection(&notfound_blocks).nth(0).is_some()
return; } else {
} false
};
if is_requested_block {
// for now, let's exclude peer from synchronization - we are relying on full nodes for synchronization // for now, let's exclude peer from synchronization - we are relying on full nodes for synchronization
let removed_tasks = self.peers_tasks.reset_blocks_tasks(peer_index); let removed_tasks = self.peers_tasks.reset_blocks_tasks(peer_index);
self.peers_tasks.unuseful_peer(peer_index); self.peers_tasks.unuseful_peer(peer_index);
@ -536,22 +538,22 @@ impl<T> ClientCore for SynchronizationClientCore<T> where T: TaskExecutor {
} }
let mut blocks_requests: Option<Vec<H256>> = None; let mut blocks_requests: Option<Vec<H256>> = None;
let blocks_idle_peers = self.peers_tasks.idle_peers_for_blocks(); let blocks_idle_peers: Vec<_> = self.peers_tasks.idle_peers_for_blocks().iter().cloned().collect();
{ {
// check if we can query some blocks hashes // check if we can query some blocks headers
let inventory_idle_peers = self.peers_tasks.idle_peers_for_inventory(); let headers_idle_peers: Vec<_> = self.peers_tasks.idle_peers_for_headers().iter().cloned().collect();
if !inventory_idle_peers.is_empty() { if !headers_idle_peers.is_empty() {
let scheduled_hashes_len = self.chain.length_of_blocks_state(BlockState::Scheduled); let scheduled_hashes_len = self.chain.length_of_blocks_state(BlockState::Scheduled);
if scheduled_hashes_len < MAX_SCHEDULED_HASHES { if scheduled_hashes_len < MAX_SCHEDULED_HASHES {
for inventory_peer in &inventory_idle_peers { for header_peer in &headers_idle_peers {
self.peers_tasks.on_inventory_requested(*inventory_peer); self.peers_tasks.on_headers_requested(*header_peer);
} }
let block_locator_hashes = self.chain.block_locator_hashes(); let block_locator_hashes = self.chain.block_locator_hashes();
let inventory_tasks = inventory_idle_peers let headers_tasks = headers_idle_peers
.into_iter() .iter()
.map(move |peer_index| Task::GetHeaders(peer_index, types::GetHeaders::with_block_locator_hashes(block_locator_hashes.clone()))); .map(move |peer_index| Task::GetHeaders(*peer_index, types::GetHeaders::with_block_locator_hashes(block_locator_hashes.clone())));
tasks.extend(inventory_tasks); tasks.extend(headers_tasks);
} }
} }
@ -706,7 +708,7 @@ impl<T> SynchronizationClientCore<T> where T: TaskExecutor {
shared_state: shared_state, shared_state: shared_state,
state: State::Saturated, state: State::Saturated,
peers: peers, peers: peers,
peers_tasks: PeersTasks::new(), peers_tasks: PeersTasks::default(),
pool: CpuPool::new(config.threads_num), pool: CpuPool::new(config.threads_num),
management_worker: None, management_worker: None,
executor: executor, executor: executor,
@ -748,7 +750,7 @@ impl<T> SynchronizationClientCore<T> where T: TaskExecutor {
if blocks_to_forget.is_empty() { None } else { Some(blocks_to_forget) }, if blocks_to_forget.is_empty() { None } else { Some(blocks_to_forget) },
); );
manage_synchronization_peers_inventory(&peers_config, &mut client.peers_tasks); manage_synchronization_peers_headers(&peers_config, &mut client.peers_tasks);
manage_orphaned_transactions(&orphan_config, &mut client.orphaned_transactions_pool); manage_orphaned_transactions(&orphan_config, &mut client.orphaned_transactions_pool);
if let Some(orphans_to_remove) = manage_unknown_orphaned_blocks(&unknown_config, &mut client.orphaned_blocks_pool) { if let Some(orphans_to_remove) = manage_unknown_orphaned_blocks(&unknown_config, &mut client.orphaned_blocks_pool) {
for orphan_to_remove in orphans_to_remove { for orphan_to_remove in orphans_to_remove {
@ -896,10 +898,12 @@ impl<T> SynchronizationClientCore<T> where T: TaskExecutor {
} }
} }
fn prepare_blocks_requests_tasks(&mut self, peers: Vec<PeerIndex>, mut hashes: Vec<H256>) -> Vec<Task> { fn prepare_blocks_requests_tasks(&mut self, mut peers: Vec<PeerIndex>, mut hashes: Vec<H256>) -> Vec<Task> {
use std::mem::swap; use std::mem::swap;
// TODO: ask most fast peers for hashes at the beginning of `hashes` // ask fastest peers for hashes at the beginning of `hashes`
self.peers_tasks.sort_peers_for_blocks(&mut peers);
let chunk_size = min(MAX_BLOCKS_IN_REQUEST, max(hashes.len() as BlockHeight, MIN_BLOCKS_IN_REQUEST)); let chunk_size = min(MAX_BLOCKS_IN_REQUEST, max(hashes.len() as BlockHeight, MIN_BLOCKS_IN_REQUEST));
let last_peer_index = peers.len() - 1; let last_peer_index = peers.len() - 1;
let mut tasks: Vec<Task> = Vec::new(); let mut tasks: Vec<Task> = Vec::new();
@ -978,8 +982,8 @@ impl<T> SynchronizationClientCore<T> where T: TaskExecutor {
{ {
let block_locator_hashes: Vec<H256> = self.chain.block_locator_hashes(); let block_locator_hashes: Vec<H256> = self.chain.block_locator_hashes();
for peer in self.peers_tasks.all_peers() { for peer in self.peers_tasks.all_peers() {
self.executor.execute(Task::GetHeaders(peer, types::GetHeaders::with_block_locator_hashes(block_locator_hashes.clone()))); self.executor.execute(Task::GetHeaders(*peer, types::GetHeaders::with_block_locator_hashes(block_locator_hashes.clone())));
self.executor.execute(Task::MemoryPool(peer)); self.executor.execute(Task::MemoryPool(*peer));
} }
} }
} }

View File

@ -8,8 +8,8 @@ use utils::{OrphanBlocksPool, OrphanTransactionsPool};
pub const MANAGEMENT_INTERVAL_MS: u64 = 10 * 1000; pub const MANAGEMENT_INTERVAL_MS: u64 = 10 * 1000;
/// Response time before getting block to decrease peer score /// Response time before getting block to decrease peer score
const DEFAULT_PEER_BLOCK_FAILURE_INTERVAL_MS: u32 = 60 * 1000; const DEFAULT_PEER_BLOCK_FAILURE_INTERVAL_MS: u32 = 60 * 1000;
/// Response time before getting inventory to decrease peer score /// Response time before getting headers to decrease peer score
const DEFAULT_PEER_INVENTORY_FAILURE_INTERVAL_MS: u32 = 60 * 1000; const DEFAULT_PEER_HEADERS_FAILURE_INTERVAL_MS: u32 = 60 * 1000;
/// Unknown orphan block removal time /// Unknown orphan block removal time
const DEFAULT_UNKNOWN_BLOCK_REMOVAL_TIME_MS: u32 = 20 * 60 * 1000; const DEFAULT_UNKNOWN_BLOCK_REMOVAL_TIME_MS: u32 = 20 * 60 * 1000;
/// Maximal number of orphaned blocks /// Maximal number of orphaned blocks
@ -23,15 +23,15 @@ const DEFAULT_ORPHAN_TRANSACTIONS_MAX_LEN: usize = 10000;
pub struct ManagePeersConfig { pub struct ManagePeersConfig {
/// Time interval (in milliseconds) to wait block from the peer before penalizing && reexecuting tasks /// Time interval (in milliseconds) to wait block from the peer before penalizing && reexecuting tasks
pub block_failure_interval_ms: u32, pub block_failure_interval_ms: u32,
/// Time interval (in milliseconds) to wait inventory from the peer before penalizing && reexecuting tasks /// Time interval (in milliseconds) to wait headers from the peer before penalizing && reexecuting tasks
pub inventory_failure_interval_ms: u32, pub headers_failure_interval_ms: u32,
} }
impl Default for ManagePeersConfig { impl Default for ManagePeersConfig {
fn default() -> Self { fn default() -> Self {
ManagePeersConfig { ManagePeersConfig {
block_failure_interval_ms: DEFAULT_PEER_BLOCK_FAILURE_INTERVAL_MS, block_failure_interval_ms: DEFAULT_PEER_BLOCK_FAILURE_INTERVAL_MS,
inventory_failure_interval_ms: DEFAULT_PEER_INVENTORY_FAILURE_INTERVAL_MS, headers_failure_interval_ms: DEFAULT_PEER_HEADERS_FAILURE_INTERVAL_MS,
} }
} }
} }
@ -77,9 +77,10 @@ pub fn manage_synchronization_peers_blocks(config: &ManagePeersConfig, peers: &m
let now = precise_time_s(); let now = precise_time_s();
// reset tasks for peers, which has not responded during given period // reset tasks for peers, which has not responded during given period
for (worst_peer_index, worst_peer_time) in peers.ordered_blocks_requests() { let ordered_blocks_requests: Vec<_> = peers.ordered_blocks_requests().clone().into_iter().collect();
for (worst_peer_index, blocks_request) in ordered_blocks_requests {
// check if peer has not responded within given time // check if peer has not responded within given time
let time_diff = now - worst_peer_time; let time_diff = now - blocks_request.timestamp;
if time_diff <= config.block_failure_interval_ms as f64 / 1000f64 { if time_diff <= config.block_failure_interval_ms as f64 / 1000f64 {
break; break;
} }
@ -96,24 +97,26 @@ pub fn manage_synchronization_peers_blocks(config: &ManagePeersConfig, peers: &m
// if peer failed many times => forget it // if peer failed many times => forget it
if peers.on_peer_block_failure(worst_peer_index) { if peers.on_peer_block_failure(worst_peer_index) {
warn!(target: "sync", "Too many failures for peer#{}. Excluding from synchronization", worst_peer_index); warn!(target: "sync", "Too many failures for peer#{}. Excluding from synchronization", worst_peer_index);
peers.unuseful_peer(worst_peer_index);
} }
} }
(blocks_to_request, blocks_to_forget) (blocks_to_request, blocks_to_forget)
} }
/// Manage stalled synchronization peers inventory tasks /// Manage stalled synchronization peers headers tasks
pub fn manage_synchronization_peers_inventory(config: &ManagePeersConfig, peers: &mut PeersTasks) { pub fn manage_synchronization_peers_headers(config: &ManagePeersConfig, peers: &mut PeersTasks) {
let now = precise_time_s(); let now = precise_time_s();
// reset tasks for peers, which has not responded during given period // reset tasks for peers, which has not responded during given period
for (worst_peer_index, worst_peer_time) in peers.ordered_inventory_requests() { let ordered_headers_requests: Vec<_> = peers.ordered_headers_requests().clone().into_iter().collect();
for (worst_peer_index, headers_request) in ordered_headers_requests {
// check if peer has not responded within given time // check if peer has not responded within given time
let time_diff = now - worst_peer_time; let time_diff = now - headers_request.timestamp;
if time_diff <= config.inventory_failure_interval_ms as f64 / 1000f64 { if time_diff <= config.headers_failure_interval_ms as f64 / 1000f64 {
break; break;
} }
peers.on_peer_inventory_failure(worst_peer_index); peers.on_peer_headers_failure(worst_peer_index);
} }
} }
@ -198,11 +201,11 @@ mod tests {
#[test] #[test]
fn manage_good_peer() { fn manage_good_peer() {
let config = ManagePeersConfig { block_failure_interval_ms: 1000, ..Default::default() }; let config = ManagePeersConfig { block_failure_interval_ms: 1000, ..Default::default() };
let mut peers = PeersTasks::new(); let mut peers = PeersTasks::default();
peers.on_blocks_requested(1, &vec![H256::from(0), H256::from(1)]); peers.on_blocks_requested(1, &vec![H256::from(0), H256::from(1)]);
peers.on_block_received(1, &H256::from(0)); peers.on_block_received(1, &H256::from(0));
assert_eq!(manage_synchronization_peers_blocks(&config, &mut peers), (vec![], vec![])); assert_eq!(manage_synchronization_peers_blocks(&config, &mut peers), (vec![], vec![]));
assert_eq!(peers.idle_peers_for_blocks(), vec![]); assert_eq!(peers.idle_peers_for_blocks().len(), 0);
} }
#[test] #[test]
@ -210,7 +213,7 @@ mod tests {
use std::thread::sleep; use std::thread::sleep;
use std::time::Duration; use std::time::Duration;
let config = ManagePeersConfig { block_failure_interval_ms: 0, ..Default::default() }; let config = ManagePeersConfig { block_failure_interval_ms: 0, ..Default::default() };
let mut peers = PeersTasks::new(); let mut peers = PeersTasks::default();
peers.on_blocks_requested(1, &vec![H256::from(0)]); peers.on_blocks_requested(1, &vec![H256::from(0)]);
peers.on_blocks_requested(2, &vec![H256::from(1)]); peers.on_blocks_requested(2, &vec![H256::from(1)]);
sleep(Duration::from_millis(1)); sleep(Duration::from_millis(1));

View File

@ -1,35 +1,17 @@
use std::cmp::Ordering;
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
use std::collections::hash_map::Entry;
use linked_hash_map::LinkedHashMap; use linked_hash_map::LinkedHashMap;
use time::precise_time_s; use time::precise_time_s;
use primitives::hash::H256; use primitives::hash::H256;
use types::PeerIndex; use types::PeerIndex;
use utils::AverageSpeedMeter;
/// Max peer failures # before excluding from sync process /// Max peer failures # before excluding from sync process
const MAX_PEER_FAILURES: usize = 2; const MAX_PEER_FAILURES: usize = 2;
/// Max blocks failures # before forgetiing this block and restarting sync /// Max blocks failures # before forgetiing this block and restarting sync
const MAX_BLOCKS_FAILURES: usize = 6; const MAX_BLOCKS_FAILURES: usize = 6;
/// Number of blocks to inspect while calculating average response time
/// Set of peers selected for synchronization. const BLOCKS_TO_INSPECT: usize = 32;
#[derive(Debug)]
pub struct PeersTasks {
/// Peers that are marked as useful for current synchronization session && have no pending requests.
idle: HashSet<PeerIndex>,
/// Peers that are marked as non-useful for current synchronization session && have no pending requests.
unuseful: HashSet<PeerIndex>,
/// # of failures for given peer.
failures: HashMap<PeerIndex, usize>,
/// # of failures for given block.
blocks_failures: HashMap<H256, usize>,
/// Peers that are marked as useful for current synchronization session && have pending blocks requests.
blocks_requests: HashMap<PeerIndex, HashSet<H256>>,
/// Last block message time from peer.
blocks_requests_order: LinkedHashMap<PeerIndex, f64>,
/// Peers that are marked as useful for current synchronization session && have pending requests.
inventory_requests: HashSet<PeerIndex>,
/// Last inventory message time from peer.
inventory_requests_order: LinkedHashMap<PeerIndex, f64>,
}
/// Information on synchronization peers /// Information on synchronization peers
#[cfg(test)] #[cfg(test)]
@ -43,194 +25,233 @@ pub struct Information {
pub active: usize, pub active: usize,
} }
impl PeersTasks { /// Set of peers selected for synchronization.
pub fn new() -> Self { #[derive(Debug, Default)]
PeersTasks { pub struct PeersTasks {
idle: HashSet::new(), /// All known peers ids
unuseful: HashSet::new(), all: HashSet<PeerIndex>,
failures: HashMap::new(), /// All unuseful peers
blocks_failures: HashMap::new(), unuseful: HashSet<PeerIndex>,
blocks_requests: HashMap::new(), /// All peers without pending headers requests
blocks_requests_order: LinkedHashMap::new(), idle_for_headers: HashSet<PeerIndex>,
inventory_requests: HashSet::new(), /// All peers without pending blocks requests
inventory_requests_order: LinkedHashMap::new(), idle_for_blocks: HashSet<PeerIndex>,
} /// Pending headers requests sent to peers
} headers_requests: LinkedHashMap<PeerIndex, HeadersRequest>,
/// Pending blocks requests sent to peers
blocks_requests: LinkedHashMap<PeerIndex, BlocksRequest>,
/// Peers statistics
stats: HashMap<PeerIndex, PeerStats>,
/// Blocks statistics
blocks_stats: HashMap<H256, BlockStats>,
}
/// Pending headers request
#[derive(Debug, Clone)]
pub struct HeadersRequest {
/// Time when request has been sent
pub timestamp: f64,
}
/// Pending blocks request
#[derive(Debug, Clone)]
pub struct BlocksRequest {
/// Time when request has been sent
pub timestamp: f64,
/// Hashes of blocks that have been requested
pub blocks: HashSet<H256>,
}
/// Peer statistics
#[derive(Debug, Default)]
struct PeerStats {
/// Number of blocks requests failures
failures: usize,
/// Average block response time meter
speed: AverageSpeedMeter,
}
/// Block statistics
#[derive(Debug, Default)]
struct BlockStats {
/// Number of block request failures
failures: usize,
}
impl PeersTasks {
/// Get information on synchronization peers /// Get information on synchronization peers
#[cfg(test)] #[cfg(test)]
pub fn information(&self) -> Information { pub fn information(&self) -> Information {
let blocks_requests_peers: HashSet<_> = self.blocks_requests.keys().cloned().collect(); let active_for_headers: HashSet<_> = self.headers_requests.keys().cloned().collect();
let total_unuseful_peers = self.unuseful.difference(&self.inventory_requests).count();
let total_active_peers = blocks_requests_peers.union(&self.inventory_requests).count();
Information { Information {
idle: self.idle.len(), idle: self.idle_for_blocks.difference(&active_for_headers).count(),
unuseful: total_unuseful_peers, unuseful: self.unuseful.len(),
active: total_active_peers, active: active_for_headers.union(&self.blocks_requests.keys().cloned().collect()).count(),
} }
} }
/// Get all peers /// Get all peers
pub fn all_peers(&self) -> Vec<PeerIndex> { pub fn all_peers(&self) -> &HashSet<PeerIndex> {
let mut unique: Vec<_> = self.idle.iter().cloned() &self.all
.chain(self.unuseful.iter().cloned())
.chain(self.blocks_requests.keys().cloned())
.chain(self.inventory_requests.iter().cloned())
.collect();
// need stable (for tests) && unique peers here, as blocks_requests can intersect with inventory_requests
unique.sort();
unique.dedup();
unique
} }
/// Get useful peers /// Get useful peers
pub fn useful_peers(&self) -> Vec<PeerIndex> { pub fn useful_peers(&self) -> Vec<PeerIndex> {
let mut unique: Vec<_> = self.idle.iter().cloned() self.all.difference(&self.unuseful).cloned().collect()
.chain(self.blocks_requests.keys().cloned())
.chain(self.inventory_requests.iter().cloned())
.collect();
// need stable (for tests) && unique peers here, as blocks_requests can intersect with inventory_requests
unique.sort();
unique.dedup();
unique
} }
/// Get idle peers for inventory request. /// Get idle peers for headers request.
pub fn idle_peers_for_inventory(&self) -> Vec<PeerIndex> { pub fn idle_peers_for_headers(&self) -> &HashSet<PeerIndex> {
let peers: HashSet<_> = self.idle.iter().cloned() &self.idle_for_headers
.chain(self.blocks_requests.keys().cloned())
.collect();
let except: HashSet<_> = self.inventory_requests.iter().cloned().collect();
peers.difference(&except).cloned().collect()
} }
/// Get idle peers for blocks request. /// Get idle peers for blocks request.
pub fn idle_peers_for_blocks(&self) -> Vec<PeerIndex> { pub fn idle_peers_for_blocks(&self) -> &HashSet<PeerIndex> {
let peers: HashSet<_> = self.idle.iter().cloned() &self.idle_for_blocks
.chain(self.inventory_requests.iter().cloned()) }
.collect();
let except: HashSet<_> = self.blocks_requests.keys().cloned().collect(); /// Sort peers for blocks request
peers.difference(&except).cloned().collect() pub fn sort_peers_for_blocks(&self, peers: &mut Vec<PeerIndex>) {
peers.sort_by(|left, right| {
let left_speed = self.stats.get(&left).map(|s| s.speed.speed()).unwrap_or(0f64);
let right_speed = self.stats.get(&right).map(|s| s.speed.speed()).unwrap_or(0f64);
// larger speed => better
right_speed.partial_cmp(&left_speed).unwrap_or(Ordering::Equal)
})
}
/// Get active headers requests, sorted by last response time (oldest first).
pub fn ordered_headers_requests(&self) -> &LinkedHashMap<PeerIndex, HeadersRequest> {
&self.headers_requests
} }
/// Get active blocks requests, sorted by last response time (oldest first). /// Get active blocks requests, sorted by last response time (oldest first).
pub fn ordered_blocks_requests(&self) -> Vec<(PeerIndex, f64)> { pub fn ordered_blocks_requests(&self) -> &LinkedHashMap<PeerIndex, BlocksRequest> {
self.blocks_requests_order.iter() &self.blocks_requests
.map(|(&pi, &t)| (pi, t))
.collect()
}
/// Get active inventory requests, sorted by last response time (oldest first).
pub fn ordered_inventory_requests(&self) -> Vec<(PeerIndex, f64)> {
self.inventory_requests_order.iter()
.map(|(&pi, &t)| (pi, t))
.collect()
} }
/// Get peer tasks /// Get peer tasks
pub fn get_blocks_tasks(&self, peer_index: PeerIndex) -> Option<HashSet<H256>> { pub fn get_blocks_tasks(&self, peer_index: PeerIndex) -> Option<&HashSet<H256>> {
self.blocks_requests.get(&peer_index).cloned() self.blocks_requests
.get(&peer_index)
.map(|br| &br.blocks)
} }
/// Mark peer as useful. /// Mark peer as useful.
pub fn useful_peer(&mut self, peer_index: PeerIndex) { pub fn useful_peer(&mut self, peer_index: PeerIndex) {
// if peer is unknown => insert to idle queue // if peer is unknown => insert to idle queue
// if peer is known && not useful => insert to idle queue // if peer is known && not useful => insert to idle queue
if !self.idle.contains(&peer_index) if self.all.insert(peer_index)
&& !self.blocks_requests.contains_key(&peer_index) || self.unuseful.remove(&peer_index) {
&& !self.inventory_requests.contains(&peer_index) { self.idle_for_headers.insert(peer_index);
self.idle.insert(peer_index); self.idle_for_blocks.insert(peer_index);
self.unuseful.remove(&peer_index); self.stats.insert(peer_index, PeerStats::new());
self.failures.remove(&peer_index);
} }
} }
/// Mark peer as unuseful. /// Mark peer as unuseful.
pub fn unuseful_peer(&mut self, peer_index: PeerIndex) { pub fn unuseful_peer(&mut self, peer_index: PeerIndex) {
// if peer is unknown => insert to idle queue // blocks should be rerequested from another peers
// if peer is known && not useful => insert to idle queue
assert!(!self.blocks_requests.contains_key(&peer_index)); assert!(!self.blocks_requests.contains_key(&peer_index));
assert!(!self.blocks_requests_order.contains_key(&peer_index));
self.idle.remove(&peer_index); if self.all.insert(peer_index) {
self.stats.insert(peer_index, PeerStats::new());
}
self.unuseful.insert(peer_index); self.unuseful.insert(peer_index);
self.failures.remove(&peer_index); self.idle_for_headers.remove(&peer_index);
self.inventory_requests.remove(&peer_index); self.idle_for_blocks.remove(&peer_index);
self.inventory_requests_order.remove(&peer_index);
} }
/// Peer has been disconnected /// Peer has been disconnected
pub fn disconnect(&mut self, peer_index: PeerIndex) { pub fn disconnect(&mut self, peer_index: PeerIndex) {
// forget this peer without any chances to reuse // blocks should be rerequested from another peers
self.idle.remove(&peer_index); assert!(!self.blocks_requests.contains_key(&peer_index));
self.all.remove(&peer_index);
self.unuseful.remove(&peer_index); self.unuseful.remove(&peer_index);
self.failures.remove(&peer_index); self.idle_for_headers.remove(&peer_index);
self.idle_for_blocks.remove(&peer_index);
self.headers_requests.remove(&peer_index);
self.blocks_requests.remove(&peer_index); self.blocks_requests.remove(&peer_index);
self.blocks_requests_order.remove(&peer_index); self.stats.remove(&peer_index);
self.inventory_requests.remove(&peer_index);
self.inventory_requests_order.remove(&peer_index);
} }
/// Block is received from peer. /// Block is received from peer.
pub fn on_block_received(&mut self, peer_index: PeerIndex, block_hash: &H256) { pub fn on_block_received(&mut self, peer_index: PeerIndex, block_hash: &H256) {
// forget block failures // block received => reset failures
self.blocks_failures.remove(block_hash); self.blocks_stats.remove(&block_hash);
// if this is requested block && it is last requested block => remove from blocks_requests let is_last_requested_block_received = if let Some(blocks_request) = self.blocks_requests.get_mut(&peer_index) {
let try_mark_as_idle = match self.blocks_requests.entry(peer_index) { // if block hasn't been requested => do nothing
Entry::Occupied(mut requests_entry) => { if !blocks_request.blocks.remove(&block_hash) {
requests_entry.get_mut().remove(block_hash); return;
self.blocks_requests_order.remove(&peer_index); }
if requests_entry.get().is_empty() {
requests_entry.remove_entry(); blocks_request.blocks.is_empty()
true } else {
} else { // this peers hasn't been requested for blocks at all
self.blocks_requests_order.insert(peer_index, precise_time_s()); return;
false
}
},
_ => false,
}; };
// try to mark as idle // it was requested block => update block response time
if try_mark_as_idle { self.stats.get_mut(&peer_index).map(|br| br.speed.checkpoint());
self.try_mark_idle(peer_index);
// if it hasn't been last requested block => just return
if !is_last_requested_block_received {
return;
}
// no more requested blocks => pause requests speed meter
self.stats.get_mut(&peer_index).map(|br| br.speed.stop());
// mark this peer as idle for blocks request
self.blocks_requests.remove(&peer_index);
self.idle_for_blocks.insert(peer_index);
// also mark as available for headers request if not yet
if !self.headers_requests.contains_key(&peer_index) {
self.idle_for_headers.insert(peer_index);
} }
} }
/// Inventory received from peer. /// Headers received from peer.
pub fn on_inventory_received(&mut self, peer_index: PeerIndex) { pub fn on_headers_received(&mut self, peer_index: PeerIndex) {
// if we have requested inventory => remove from inventory_requests self.headers_requests.remove(&peer_index);
self.inventory_requests.remove(&peer_index); // we only ask for new headers when peer is also not asked for blocks
self.inventory_requests_order.remove(&peer_index); // => only insert to idle queue if no active blocks requests
if !self.blocks_requests.contains_key(&peer_index) {
// try to mark as idle self.idle_for_headers.insert(peer_index);
self.try_mark_idle(peer_index); }
} }
/// Blocks have been requested from peer. /// Blocks have been requested from peer.
pub fn on_blocks_requested(&mut self, peer_index: PeerIndex, blocks_hashes: &[H256]) { pub fn on_blocks_requested(&mut self, peer_index: PeerIndex, blocks_hashes: &[H256]) {
// mark peer as active if !self.all.contains(&peer_index) {
self.idle.remove(&peer_index); self.unuseful_peer(peer_index);
}
self.unuseful.remove(&peer_index); self.unuseful.remove(&peer_index);
self.blocks_requests.entry(peer_index).or_insert_with(HashSet::new).extend(blocks_hashes.iter().cloned()); self.idle_for_blocks.remove(&peer_index);
self.blocks_requests_order.remove(&peer_index);
self.blocks_requests_order.insert(peer_index, precise_time_s()); if !self.blocks_requests.contains_key(&peer_index) {
self.blocks_requests.insert(peer_index, BlocksRequest::new());
}
self.blocks_requests.get_mut(&peer_index)
.expect("inserted one line above")
.blocks.extend(blocks_hashes.iter().cloned());
// no more requested blocks => pause requests speed meter
self.stats.get_mut(&peer_index).map(|br| br.speed.start());
} }
/// Inventory has been requested from peer. /// Headers hashave been requested from peer.
pub fn on_inventory_requested(&mut self, peer_index: PeerIndex) { pub fn on_headers_requested(&mut self, peer_index: PeerIndex) {
self.inventory_requests.insert(peer_index); if !self.all.contains(&peer_index) {
self.inventory_requests_order.remove(&peer_index); self.unuseful_peer(peer_index);
self.inventory_requests_order.insert(peer_index, precise_time_s());
// mark peer as active
if self.idle.remove(&peer_index) {
self.unuseful.insert(peer_index);
} }
// peer is now out-of-synchronization process (will not request blocks from him), because:
// 1) if it has new blocks, it will respond with `inventory` message && will be inserted back here self.idle_for_headers.remove(&peer_index);
// 2) if it has no new blocks => either synchronization is completed, or it is behind us in sync self.headers_requests.remove(&peer_index);
self.headers_requests.insert(peer_index, HeadersRequest::new());
} }
/// We have failed to get blocks /// We have failed to get blocks
@ -238,20 +259,16 @@ impl PeersTasks {
let mut failed_blocks: Vec<H256> = Vec::new(); let mut failed_blocks: Vec<H256> = Vec::new();
let mut normal_blocks: Vec<H256> = Vec::with_capacity(hashes.len()); let mut normal_blocks: Vec<H256> = Vec::with_capacity(hashes.len());
for hash in hashes { for hash in hashes {
match self.blocks_failures.entry(hash.clone()) { let is_failed_block = {
Entry::Vacant(entry) => { let block_stats = self.blocks_stats.entry(hash.clone()).or_insert(BlockStats::default());
normal_blocks.push(hash); block_stats.failures += 1;
entry.insert(0); block_stats.failures > MAX_BLOCKS_FAILURES
}, };
Entry::Occupied(mut entry) => { if is_failed_block {
*entry.get_mut() += 1; self.blocks_stats.remove(&hash);
if *entry.get() >= MAX_BLOCKS_FAILURES { failed_blocks.push(hash);
entry.remove(); } else {
failed_blocks.push(hash); normal_blocks.push(hash);
} else {
normal_blocks.push(hash);
}
}
} }
} }
@ -260,78 +277,77 @@ impl PeersTasks {
/// We have failed to get block from peer during given period /// We have failed to get block from peer during given period
pub fn on_peer_block_failure(&mut self, peer_index: PeerIndex) -> bool { pub fn on_peer_block_failure(&mut self, peer_index: PeerIndex) -> bool {
let peer_failures = match self.failures.entry(peer_index) { self.stats.get_mut(&peer_index)
Entry::Occupied(mut entry) => { .map(|s| {
let failures = entry.get() + 1; s.failures += 1;
entry.insert(failures); s.failures > MAX_PEER_FAILURES
failures })
}, .unwrap_or_default()
Entry::Vacant(entry) => *entry.insert(1),
};
let too_much_failures = peer_failures >= MAX_PEER_FAILURES;
if too_much_failures {
self.idle.remove(&peer_index);
self.unuseful.insert(peer_index);
self.failures.remove(&peer_index);
self.blocks_requests.remove(&peer_index);
self.blocks_requests_order.remove(&peer_index);
}
too_much_failures
} }
/// We have failed to get inventory from peer during given period /// We have failed to get headers from peer during given period
pub fn on_peer_inventory_failure(&mut self, peer_index: PeerIndex) { pub fn on_peer_headers_failure(&mut self, peer_index: PeerIndex) {
// ignore inventory failures // we never penalize peers for header requests failures
self.inventory_requests.remove(&peer_index); self.headers_requests.remove(&peer_index);
self.inventory_requests_order.remove(&peer_index); self.idle_for_headers.insert(peer_index);
if !self.blocks_requests.contains_key(&peer_index) {
self.idle.insert(peer_index);
self.unuseful.remove(&peer_index);
}
} }
/// Reset all peers state to the unuseful /// Reset all peers state to the unuseful
pub fn reset(&mut self) { pub fn reset(&mut self) {
self.unuseful.extend(self.idle.drain()); self.unuseful.clear();
self.unuseful.extend(self.blocks_requests.drain().map(|(k, _)| k)); self.unuseful.extend(self.all.iter().cloned());
self.unuseful.extend(self.inventory_requests.drain()); self.idle_for_headers.clear();
self.failures.clear(); self.idle_for_blocks.clear();
self.inventory_requests_order.clear(); self.headers_requests.clear();
self.blocks_requests_order.clear(); self.blocks_requests.clear();
} }
/// Reset peer tasks && move peer to idle state /// Reset peer tasks && move peer to idle state
pub fn reset_blocks_tasks(&mut self, peer_index: PeerIndex) -> Vec<H256> { pub fn reset_blocks_tasks(&mut self, peer_index: PeerIndex) -> Vec<H256> {
let requests = self.blocks_requests.remove(&peer_index); self.idle_for_blocks.insert(peer_index);
self.blocks_requests_order.remove(&peer_index); self.blocks_requests.remove(&peer_index)
self.try_mark_idle(peer_index); .map(|mut br| br.blocks.drain().collect())
requests.unwrap_or_default().into_iter().collect() .unwrap_or_default()
} }
}
/// Try to mark peer as idle impl HeadersRequest {
fn try_mark_idle(&mut self, peer_index: PeerIndex) { pub fn new() -> Self {
if self.blocks_requests.contains_key(&peer_index) HeadersRequest {
|| self.inventory_requests.contains(&peer_index) { timestamp: precise_time_s(),
return;
} }
}
}
self.idle.insert(peer_index); impl BlocksRequest {
self.unuseful.remove(&peer_index); pub fn new() -> Self {
BlocksRequest {
timestamp: precise_time_s(),
blocks: HashSet::new(),
}
}
}
impl PeerStats {
pub fn new() -> Self {
PeerStats {
failures: 0,
speed: AverageSpeedMeter::with_inspect_items(BLOCKS_TO_INSPECT),
}
} }
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::{PeersTasks, MAX_PEER_FAILURES, MAX_BLOCKS_FAILURES};
use primitives::hash::H256; use primitives::hash::H256;
use super::{PeersTasks, MAX_PEER_FAILURES, MAX_BLOCKS_FAILURES};
use types::PeerIndex;
#[test] #[test]
fn peers_empty_on_start() { fn peers_empty_on_start() {
let peers = PeersTasks::new(); let peers = PeersTasks::default();
assert_eq!(peers.idle_peers_for_blocks(), vec![]); assert_eq!(peers.idle_peers_for_blocks().len(), 0);
assert_eq!(peers.idle_peers_for_inventory(), vec![]); assert_eq!(peers.idle_peers_for_headers().len(), 0);
let info = peers.information(); let info = peers.information();
assert_eq!(info.idle, 0); assert_eq!(info.idle, 0);
@ -340,7 +356,7 @@ mod tests {
#[test] #[test]
fn peers_all_unuseful_after_reset() { fn peers_all_unuseful_after_reset() {
let mut peers = PeersTasks::new(); let mut peers = PeersTasks::default();
peers.on_blocks_requested(7, &vec![H256::default()]); peers.on_blocks_requested(7, &vec![H256::default()]);
peers.on_blocks_requested(8, &vec![H256::default()]); peers.on_blocks_requested(8, &vec![H256::default()]);
assert_eq!(peers.information().idle, 0); assert_eq!(peers.information().idle, 0);
@ -354,7 +370,7 @@ mod tests {
#[test] #[test]
fn peer_idle_after_reset_tasks() { fn peer_idle_after_reset_tasks() {
let mut peers = PeersTasks::new(); let mut peers = PeersTasks::default();
peers.on_blocks_requested(7, &vec![H256::default()]); peers.on_blocks_requested(7, &vec![H256::default()]);
assert_eq!(peers.information().idle, 0); assert_eq!(peers.information().idle, 0);
assert_eq!(peers.information().unuseful, 0); assert_eq!(peers.information().unuseful, 0);
@ -366,14 +382,14 @@ mod tests {
} }
#[test] #[test]
fn peers_active_after_inventory_request() { fn peers_active_after_headers_request() {
let mut peers = PeersTasks::new(); let mut peers = PeersTasks::default();
peers.useful_peer(5); peers.useful_peer(5);
peers.useful_peer(7); peers.useful_peer(7);
assert_eq!(peers.information().idle, 2); assert_eq!(peers.information().idle, 2);
assert_eq!(peers.information().unuseful, 0); assert_eq!(peers.information().unuseful, 0);
assert_eq!(peers.information().active, 0); assert_eq!(peers.information().active, 0);
peers.on_inventory_requested(5); peers.on_headers_requested(5);
assert_eq!(peers.information().idle, 1); assert_eq!(peers.information().idle, 1);
assert_eq!(peers.information().unuseful, 0); assert_eq!(peers.information().unuseful, 0);
assert_eq!(peers.information().active, 1); assert_eq!(peers.information().active, 1);
@ -381,37 +397,39 @@ mod tests {
#[test] #[test]
fn peers_insert_remove_idle() { fn peers_insert_remove_idle() {
let mut peers = PeersTasks::new(); let mut peers = PeersTasks::default();
peers.useful_peer(0); peers.useful_peer(0);
assert_eq!(peers.information().idle, 1); assert_eq!(peers.information().idle, 1);
assert_eq!(peers.information().unuseful, 0); assert_eq!(peers.information().unuseful, 0);
assert_eq!(peers.information().active, 0); assert_eq!(peers.information().active, 0);
assert_eq!(peers.idle_peers_for_blocks(), vec![0]); assert_eq!(peers.idle_peers_for_blocks().len(), 1);
assert!(peers.idle_peers_for_blocks().contains(&0));
peers.useful_peer(5); peers.useful_peer(5);
assert_eq!(peers.information().idle, 2); assert_eq!(peers.information().idle, 2);
assert_eq!(peers.information().active, 0); assert_eq!(peers.information().active, 0);
let idle_peers = peers.idle_peers_for_blocks(); let idle_peers: Vec<_> = peers.idle_peers_for_blocks().iter().cloned().collect();
assert!(idle_peers[0] == 0 || idle_peers[0] == 5); assert!(idle_peers[0] == 0 || idle_peers[0] == 5);
assert!(idle_peers[1] == 0 || idle_peers[1] == 5); assert!(idle_peers[1] == 0 || idle_peers[1] == 5);
peers.disconnect(7); peers.disconnect(7);
assert_eq!(peers.information().idle, 2); assert_eq!(peers.information().idle, 2);
assert_eq!(peers.information().active, 0); assert_eq!(peers.information().active, 0);
let idle_peers = peers.idle_peers_for_blocks(); let idle_peers: Vec<_> = peers.idle_peers_for_blocks().iter().cloned().collect();
assert!(idle_peers[0] == 0 || idle_peers[0] == 5); assert!(idle_peers[0] == 0 || idle_peers[0] == 5);
assert!(idle_peers[1] == 0 || idle_peers[1] == 5); assert!(idle_peers[1] == 0 || idle_peers[1] == 5);
peers.disconnect(0); peers.disconnect(0);
assert_eq!(peers.information().idle, 1); assert_eq!(peers.information().idle, 1);
assert_eq!(peers.information().active, 0); assert_eq!(peers.information().active, 0);
assert_eq!(peers.idle_peers_for_blocks(), vec![5]); assert_eq!(peers.idle_peers_for_blocks().len(), 1);
assert!(peers.idle_peers_for_blocks().contains(&5));
} }
#[test] #[test]
fn peers_request_blocks() { fn peers_request_blocks() {
let mut peers = PeersTasks::new(); let mut peers = PeersTasks::default();
peers.useful_peer(5); peers.useful_peer(5);
@ -448,20 +466,20 @@ mod tests {
#[test] #[test]
fn peers_worst() { fn peers_worst() {
let mut peers = PeersTasks::new(); let mut peers = PeersTasks::default();
peers.useful_peer(1); peers.useful_peer(1);
peers.useful_peer(2); peers.useful_peer(2);
assert_eq!(peers.ordered_blocks_requests(), vec![]); assert_eq!(peers.ordered_blocks_requests().len(), 0);
peers.on_blocks_requested(1, &vec![H256::default()]); peers.on_blocks_requested(1, &vec![H256::default()]);
assert_eq!(peers.ordered_blocks_requests().len(), 1); assert_eq!(peers.ordered_blocks_requests().len(), 1);
assert_eq!(peers.ordered_blocks_requests()[0].0, 1); assert_eq!(*peers.ordered_blocks_requests().keys().nth(0).unwrap(), 1);
peers.on_blocks_requested(2, &vec![H256::default()]); peers.on_blocks_requested(2, &vec![H256::default()]);
assert_eq!(peers.ordered_blocks_requests().len(), 2); assert_eq!(peers.ordered_blocks_requests().len(), 2);
assert_eq!(peers.ordered_blocks_requests()[0].0, 1); assert_eq!(*peers.ordered_blocks_requests().keys().nth(0).unwrap(), 1);
assert_eq!(peers.ordered_blocks_requests()[1].0, 2); assert_eq!(*peers.ordered_blocks_requests().keys().nth(1).unwrap(), 2);
assert_eq!(peers.information().idle, 0); assert_eq!(peers.information().idle, 0);
assert_eq!(peers.information().unuseful, 0); assert_eq!(peers.information().unuseful, 0);
@ -474,10 +492,13 @@ mod tests {
assert_eq!(peers.information().active, 1); assert_eq!(peers.information().active, 1);
assert_eq!(peers.ordered_blocks_requests().len(), 1); assert_eq!(peers.ordered_blocks_requests().len(), 1);
assert_eq!(peers.ordered_blocks_requests()[0].0, 2); assert_eq!(*peers.ordered_blocks_requests().keys().nth(0).unwrap(), 2);
for _ in 0..MAX_PEER_FAILURES { for _ in 0..(MAX_PEER_FAILURES + 1) {
peers.on_peer_block_failure(2); if peers.on_peer_block_failure(2) {
peers.reset_blocks_tasks(2);
peers.unuseful_peer(2);
}
} }
assert_eq!(peers.ordered_blocks_requests().len(), 0); assert_eq!(peers.ordered_blocks_requests().len(), 0);
@ -488,7 +509,7 @@ mod tests {
#[test] #[test]
fn peer_not_inserted_when_known() { fn peer_not_inserted_when_known() {
let mut peers = PeersTasks::new(); let mut peers = PeersTasks::default();
peers.useful_peer(1); peers.useful_peer(1);
peers.useful_peer(1); peers.useful_peer(1);
assert_eq!(peers.information().active + peers.information().idle + peers.information().unuseful, 1); assert_eq!(peers.information().active + peers.information().idle + peers.information().unuseful, 1);
@ -496,7 +517,10 @@ mod tests {
peers.useful_peer(1); peers.useful_peer(1);
assert_eq!(peers.information().active + peers.information().idle + peers.information().unuseful, 1); assert_eq!(peers.information().active + peers.information().idle + peers.information().unuseful, 1);
for _ in 0..MAX_PEER_FAILURES { for _ in 0..MAX_PEER_FAILURES {
peers.on_peer_block_failure(1); if peers.on_peer_block_failure(1) {
peers.reset_blocks_tasks(1);
peers.unuseful_peer(1);
}
} }
peers.useful_peer(1); peers.useful_peer(1);
assert_eq!(peers.information().active + peers.information().idle + peers.information().unuseful, 1); assert_eq!(peers.information().active + peers.information().idle + peers.information().unuseful, 1);
@ -504,7 +528,7 @@ mod tests {
#[test] #[test]
fn peer_block_failures() { fn peer_block_failures() {
let mut peers = PeersTasks::new(); let mut peers = PeersTasks::default();
peers.useful_peer(1); peers.useful_peer(1);
peers.on_blocks_requested(1, &vec![H256::from(1)]); peers.on_blocks_requested(1, &vec![H256::from(1)]);
for _ in 0..MAX_BLOCKS_FAILURES { for _ in 0..MAX_BLOCKS_FAILURES {
@ -519,4 +543,25 @@ mod tests {
assert_eq!(blocks_to_request, vec![]); assert_eq!(blocks_to_request, vec![]);
assert_eq!(blocks_to_forget, vec![H256::from(1)]); assert_eq!(blocks_to_forget, vec![H256::from(1)]);
} }
#[test]
fn peer_sort_peers_for_blocks() {
let mut peers = PeersTasks::default();
peers.on_blocks_requested(1, &vec![H256::from(1), H256::from(2)]);
peers.on_blocks_requested(2, &vec![H256::from(3), H256::from(4)]);
peers.on_block_received(2, &H256::from(3));
peers.on_block_received(2, &H256::from(4));
use std::thread;
use std::time::Duration;
thread::park_timeout(Duration::from_millis(50));
peers.on_block_received(1, &H256::from(1));
peers.on_block_received(1, &H256::from(2));
let mut peers_for_blocks: Vec<PeerIndex> = vec![1, 2];
peers.sort_peers_for_blocks(&mut peers_for_blocks);
assert_eq!(peers_for_blocks[0], 2);
assert_eq!(peers_for_blocks[1], 1);
}
} }

View File

@ -2,6 +2,7 @@ use std::collections::VecDeque;
use time; use time;
/// Speed meter with given items number /// Speed meter with given items number
#[derive(Debug, Default)]
pub struct AverageSpeedMeter { pub struct AverageSpeedMeter {
/// Number of items to inspect /// Number of items to inspect
inspect_items: usize, inspect_items: usize,
@ -49,4 +50,12 @@ impl AverageSpeedMeter {
} }
self.last_timestamp = Some(now); self.last_timestamp = Some(now);
} }
pub fn start(&mut self) {
self.last_timestamp = Some(time::precise_time_s());
}
pub fn stop(&mut self) {
self.last_timestamp = None;
}
} }