More reliable peers check (#3824)
* peer: unknown state for new peers, check peers state on every monitor (128 healthy non-connected + 128 defuncts + 128 unknown), mark peer as defunct when ping not passed, do not crash on toml parse with dns failure * p2p: cleanup before selection at monitor, add outbound to connected list only when there is not enough peers + disconnect extra peer immediately, reconnect to seeds at monitor to avoid stuck, update only defunct state to unknown when received existing peer address * p2p: reduced amount of total peers to check at monitor * p2p: do not check healthy and defunct peers more often than once per hour, store last connection attempt, do not ask for more peers when there is enough outbound * peer: update last_attempt when changing peer state to other than Banned * fix: log of peers amount to check
This commit is contained in:
+26
-14
@@ -61,6 +61,7 @@ impl Peers {
|
||||
/// Adds the peer to our internal peer mapping. Note that the peer is still
|
||||
/// returned so the server can run it.
|
||||
pub fn add_connected(&self, peer: Arc<Peer>) -> Result<(), Error> {
|
||||
let enough_outbound = self.enough_outbound_peers();
|
||||
let peer_data: PeerData;
|
||||
{
|
||||
// Scope for peers vector lock - dont hold the peers lock while adding to lmdb
|
||||
@@ -76,9 +77,12 @@ impl Peers {
|
||||
last_banned: 0,
|
||||
ban_reason: ReasonForBan::None,
|
||||
last_connected: Utc::now().timestamp(),
|
||||
last_attempt: Utc::now().timestamp(),
|
||||
};
|
||||
debug!("Adding newly connected peer {}.", peer_data.addr);
|
||||
peers.insert(peer_data.addr, peer);
|
||||
if !enough_outbound || !peer.info.is_outbound() {
|
||||
debug!("Adding newly connected peer {}.", peer_data.addr);
|
||||
peers.insert(peer_data.addr, peer);
|
||||
}
|
||||
}
|
||||
debug!("Saving newly connected peer {}.", peer_data.addr);
|
||||
if let Err(e) = self.save_peer(&peer_data) {
|
||||
@@ -98,6 +102,7 @@ impl Peers {
|
||||
last_banned: Utc::now().timestamp(),
|
||||
ban_reason,
|
||||
last_connected: Utc::now().timestamp(),
|
||||
last_attempt: Utc::now().timestamp(),
|
||||
};
|
||||
debug!("Banning peer {}.", addr);
|
||||
self.save_peer(&peer_data)
|
||||
@@ -142,6 +147,7 @@ impl Peers {
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
/// Ban a peer, disconnecting it if we're currently connected
|
||||
pub fn ban_peer(&self, peer_addr: PeerAddr, ban_reason: ReasonForBan) -> Result<(), Error> {
|
||||
// Update the peer in peers db
|
||||
@@ -261,6 +267,8 @@ impl Peers {
|
||||
break;
|
||||
}
|
||||
};
|
||||
// Mark peer as defunct after ping failure.
|
||||
let _ = self.update_state(p.info.addr, State::Defunct);
|
||||
p.stop();
|
||||
peers.remove(&p.info.addr);
|
||||
}
|
||||
@@ -702,21 +710,25 @@ impl NetAdapter for Peers {
|
||||
trace!("Received {} peer addrs, saving.", peer_addrs.len());
|
||||
let mut to_save: Vec<PeerData> = Vec::new();
|
||||
for pa in peer_addrs {
|
||||
if let Ok(e) = self.exists_peer(pa) {
|
||||
if e {
|
||||
if let Ok(mut p) = self.get_peer(pa) {
|
||||
if p.flags != State::Defunct {
|
||||
continue;
|
||||
}
|
||||
p.flags = State::Unknown;
|
||||
to_save.push(p);
|
||||
} else {
|
||||
let peer = PeerData {
|
||||
addr: pa,
|
||||
capabilities: Capabilities::UNKNOWN,
|
||||
user_agent: "".to_string(),
|
||||
flags: State::Unknown,
|
||||
last_banned: 0,
|
||||
ban_reason: ReasonForBan::None,
|
||||
last_connected: 0,
|
||||
last_attempt: 0,
|
||||
};
|
||||
to_save.push(peer);
|
||||
}
|
||||
let peer = PeerData {
|
||||
addr: pa,
|
||||
capabilities: Capabilities::UNKNOWN,
|
||||
user_agent: "".to_string(),
|
||||
flags: State::Healthy,
|
||||
last_banned: 0,
|
||||
ban_reason: ReasonForBan::None,
|
||||
last_connected: Utc::now().timestamp(),
|
||||
};
|
||||
to_save.push(peer);
|
||||
}
|
||||
if let Err(e) = self.save_peers(to_save) {
|
||||
error!("Could not save received peer addresses: {:?}", e);
|
||||
|
||||
@@ -187,6 +187,9 @@ impl Server {
|
||||
&self.handshake,
|
||||
self.peers.clone(),
|
||||
)?;
|
||||
if self.peers.enough_outbound_peers() {
|
||||
peer.stop();
|
||||
}
|
||||
let peer = Arc::new(peer);
|
||||
self.peers.add_connected(peer.clone())?;
|
||||
Ok(peer)
|
||||
|
||||
+15
-9
@@ -27,13 +27,14 @@ const STORE_SUBPATH: &str = "peers";
|
||||
|
||||
const PEER_PREFIX: u8 = b'P';
|
||||
|
||||
// Types of messages
|
||||
// Types of peers
|
||||
enum_from_primitive! {
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
|
||||
pub enum State {
|
||||
Healthy = 0,
|
||||
Banned = 1,
|
||||
Defunct = 2,
|
||||
Unknown = 3,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,6 +56,8 @@ pub struct PeerData {
|
||||
pub ban_reason: ReasonForBan,
|
||||
/// Time when we last connected to this peer.
|
||||
pub last_connected: i64,
|
||||
/// Time when last connection attempt happened to this peer.
|
||||
pub last_attempt: i64,
|
||||
}
|
||||
|
||||
impl Writeable for PeerData {
|
||||
@@ -67,7 +70,8 @@ impl Writeable for PeerData {
|
||||
[write_u8, self.flags as u8],
|
||||
[write_i64, self.last_banned],
|
||||
[write_i32, self.ban_reason as i32],
|
||||
[write_i64, self.last_connected]
|
||||
[write_i64, self.last_connected],
|
||||
[write_i64, self.last_attempt]
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
@@ -81,12 +85,10 @@ impl Readable for PeerData {
|
||||
let (fl, lb, br) = ser_multiread!(reader, read_u8, read_i64, read_i32);
|
||||
|
||||
let lc = reader.read_i64();
|
||||
// this only works because each PeerData is read in its own vector and this
|
||||
// is the last data element
|
||||
let last_connected = match lc {
|
||||
Err(_) => Utc::now().timestamp(),
|
||||
Ok(lc) => lc,
|
||||
};
|
||||
let last_connected = lc.unwrap_or_else(|_| Utc::now().timestamp());
|
||||
|
||||
let la = reader.read_i64();
|
||||
let last_attempt = la.unwrap_or_else(|_| 0);
|
||||
|
||||
let user_agent = String::from_utf8(ua).map_err(|_| ser::Error::CorruptedData)?;
|
||||
let capabilities = Capabilities::from_bits_truncate(capab);
|
||||
@@ -97,10 +99,11 @@ impl Readable for PeerData {
|
||||
addr,
|
||||
capabilities,
|
||||
user_agent,
|
||||
flags: flags,
|
||||
flags,
|
||||
last_banned: lb,
|
||||
ban_reason,
|
||||
last_connected,
|
||||
last_attempt,
|
||||
}),
|
||||
None => Err(ser::Error::CorruptedData),
|
||||
}
|
||||
@@ -187,6 +190,7 @@ impl PeerStore {
|
||||
|
||||
/// Convenience method to load a peer data, update its status and save it
|
||||
/// back. If new state is Banned its last banned time will be updated too.
|
||||
/// If new state is Defunct last connection attempt will be updated too.
|
||||
pub fn update_state(&self, peer_addr: PeerAddr, new_state: State) -> Result<(), Error> {
|
||||
let batch = self.db.batch()?;
|
||||
|
||||
@@ -197,6 +201,8 @@ impl PeerStore {
|
||||
peer.flags = new_state;
|
||||
if new_state == State::Banned {
|
||||
peer.last_banned = Utc::now().timestamp();
|
||||
} else {
|
||||
peer.last_attempt = Utc::now().timestamp();
|
||||
}
|
||||
|
||||
batch.put_ser(&peer_key(peer_addr)[..], &peer)?;
|
||||
|
||||
+7
-4
@@ -183,10 +183,13 @@ impl<'de> Visitor<'de> for PeerAddrs {
|
||||
Ok(ip) => peers.push(PeerAddr(ip)),
|
||||
// If that fails it's probably a DNS record
|
||||
Err(_) => {
|
||||
let socket_addrs = entry.to_socket_addrs().map_err(|_| {
|
||||
serde::de::Error::custom(format!("Unable to resolve DNS: {}", entry))
|
||||
})?;
|
||||
peers.append(&mut socket_addrs.map(PeerAddr).collect());
|
||||
let socket_addrs: Result<std::vec::IntoIter<SocketAddr>, M::Error> =
|
||||
entry.to_socket_addrs().map_err(|_| {
|
||||
serde::de::Error::custom(format!("Unable to resolve DNS: {}", entry))
|
||||
});
|
||||
if let Ok(socket_addrs) = socket_addrs {
|
||||
peers.append(&mut socket_addrs.map(PeerAddr).collect());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user