Avoid long state queries when serving GetNodeData requests (#11444)
* Remove dead bootnodes, add new geth bootnodes * More granular locking when fetching state Finish GetDataNode requests early if queries take too long * typo * Use latest kvdb-rocksdb * Cleanup * Update ethcore/sync/src/chain/supplier.rs Co-Authored-By: Andronik Ordian <write@reusable.software> * Address review grumbles * Fix compilation * Address review grumbles Co-authored-by: Andronik Ordian <write@reusable.software>
This commit is contained in:
@@ -162,7 +162,12 @@ pub const PAR_PROTOCOL_VERSION_4: (u8, u8) = (4, 0x20);
|
||||
|
||||
pub const MAX_BODIES_TO_SEND: usize = 256;
|
||||
pub const MAX_HEADERS_TO_SEND: usize = 512;
|
||||
/// Maximum number of "entries" to include in a GetDataNode request.
|
||||
pub const MAX_NODE_DATA_TO_SEND: usize = 1024;
|
||||
/// Maximum allowed duration for serving a batch GetNodeData request.
|
||||
const MAX_NODE_DATA_TOTAL_DURATION: Duration = Duration::from_secs(2);
|
||||
/// Maximum allowed duration for serving a single GetNodeData request.
|
||||
const MAX_NODE_DATA_SINGLE_DURATION: Duration = Duration::from_millis(100);
|
||||
pub const MAX_RECEIPTS_HEADERS_TO_SEND: usize = 256;
|
||||
const MIN_PEERS_PROPAGATION: usize = 4;
|
||||
const MAX_PEERS_PROPAGATION: usize = 128;
|
||||
|
||||
@@ -15,13 +15,14 @@
|
||||
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use std::cmp;
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
use crate::sync_io::SyncIo;
|
||||
|
||||
use bytes::Bytes;
|
||||
use enum_primitive::FromPrimitive;
|
||||
use ethereum_types::H256;
|
||||
use log::{debug, trace};
|
||||
use log::{debug, trace, warn};
|
||||
use network::{self, PeerId};
|
||||
use parking_lot::RwLock;
|
||||
use rlp::{Rlp, RlpStream};
|
||||
@@ -56,6 +57,8 @@ use super::{
|
||||
MAX_BODIES_TO_SEND,
|
||||
MAX_HEADERS_TO_SEND,
|
||||
MAX_NODE_DATA_TO_SEND,
|
||||
MAX_NODE_DATA_TOTAL_DURATION,
|
||||
MAX_NODE_DATA_SINGLE_DURATION,
|
||||
MAX_RECEIPTS_HEADERS_TO_SEND,
|
||||
};
|
||||
|
||||
@@ -258,9 +261,9 @@ impl SyncSupplier {
|
||||
|
||||
/// Respond to GetNodeData request
|
||||
fn return_node_data(io: &dyn SyncIo, r: &Rlp, peer_id: PeerId) -> RlpResponseResult {
|
||||
let payload_soft_limit = io.payload_soft_limit();
|
||||
let payload_soft_limit = io.payload_soft_limit(); // 4Mb
|
||||
let mut count = r.item_count().unwrap_or(0);
|
||||
trace!(target: "sync", "{} -> GetNodeData: {} entries", peer_id, count);
|
||||
trace!(target: "sync", "{} -> GetNodeData: {} entries requested", peer_id, count);
|
||||
if count == 0 {
|
||||
debug!(target: "sync", "Empty GetNodeData request, ignoring.");
|
||||
return Ok(None);
|
||||
@@ -269,10 +272,20 @@ impl SyncSupplier {
|
||||
let mut added = 0usize;
|
||||
let mut data = Vec::new();
|
||||
let mut total_bytes = 0;
|
||||
let mut total_elpsd = Duration::from_secs(0);
|
||||
for i in 0..count {
|
||||
if let Some(node) = io.chain().state_data(&r.val_at::<H256>(i)?) {
|
||||
let hash = &r.val_at(i)?;
|
||||
let elpsd = Instant::now();
|
||||
let state = io.chain().state_data(hash);
|
||||
|
||||
total_elpsd += elpsd.elapsed();
|
||||
if elpsd.elapsed() > MAX_NODE_DATA_SINGLE_DURATION || total_elpsd > MAX_NODE_DATA_TOTAL_DURATION {
|
||||
warn!(target: "sync", "{} -> GetNodeData: item {}/{} – slow state fetch for hash {:?}; took {:?}",
|
||||
peer_id, i, count, hash, elpsd);
|
||||
break;
|
||||
}
|
||||
if let Some(node) = state {
|
||||
total_bytes += node.len();
|
||||
// Check that the packet won't be oversized
|
||||
if total_bytes > payload_soft_limit {
|
||||
break;
|
||||
}
|
||||
@@ -280,7 +293,8 @@ impl SyncSupplier {
|
||||
added += 1;
|
||||
}
|
||||
}
|
||||
trace!(target: "sync", "{} -> GetNodeData: return {} entries", peer_id, added);
|
||||
trace!(target: "sync", "{} -> GetNodeData: returning {}/{} entries ({} bytes total in {:?})",
|
||||
peer_id, added, count, total_bytes, total_elpsd);
|
||||
let mut rlp = RlpStream::new_list(added);
|
||||
for d in data {
|
||||
rlp.append(&d);
|
||||
@@ -540,7 +554,7 @@ mod test {
|
||||
let rlp_result = result.unwrap();
|
||||
assert!(rlp_result.is_some());
|
||||
|
||||
// the length of one rlp-encoded hashe
|
||||
// the length of one rlp-encoded hash
|
||||
let rlp = rlp_result.unwrap().1.out();
|
||||
let rlp = Rlp::new(&rlp);
|
||||
assert_eq!(Ok(1), rlp.item_count());
|
||||
|
||||
Reference in New Issue
Block a user