Skip to content

Commit 9c15982

Browse files
altonennathanwhit
authored andcommitted
Attempt to relieve pressure on mpsc_network_worker (paritytech#13725)
* Attempt to relieve pressure on `mpsc_network_worker` `SyncingEngine` interacting with `NetworkWorker` can put a lot of strain on the channel if the number of inbound connections is high. This is because `SyncingEngine` is notified of each inbound substream which it then can either accept or reject and this causes a lot of message exchange on the already busy channel. Use a direct channel pair between `Protocol` and `SyncingEngine` to exchange notification events. It is a temporary change to alleviate the problems caused by syncing being an independent protocol and the fix will be removed once `NotificationService` is implemented. * Apply review comments * fixes * trigger ci * Fix tests Verify that both peers have a connection now that the validation goes through `SyncingEngine`. Depending on how the tasks are scheduled, one of them might not have the peer registered in `SyncingEngine` at which point the test won't make any progress because block announcement received from an unknown peer is discarded. Move polling of `ChainSync` at the end of the function so that if a block announcement causes a block request to be sent, that can be sent in the same call to `SyncingEngine::poll()`. --------- Co-authored-by: parity-processbot <>
1 parent 97e37fc commit 9c15982

12 files changed

Lines changed: 259 additions & 153 deletions

File tree

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

client/network/src/config.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
//! See the documentation of [`Params`].
2323
2424
pub use crate::{
25+
protocol::NotificationsSink,
2526
request_responses::{
2627
IncomingRequest, OutgoingResponse, ProtocolConfig as RequestResponseConfig,
2728
},
@@ -31,7 +32,12 @@ pub use crate::{
3132
use codec::Encode;
3233
use libp2p::{identity::Keypair, multiaddr, Multiaddr, PeerId};
3334
use prometheus_endpoint::Registry;
34-
pub use sc_network_common::{role::Role, sync::warp::WarpSyncProvider, ExHashT};
35+
pub use sc_network_common::{
36+
role::{Role, Roles},
37+
sync::warp::WarpSyncProvider,
38+
ExHashT,
39+
};
40+
use sc_utils::mpsc::TracingUnboundedSender;
3541
use zeroize::Zeroize;
3642

3743
use sp_runtime::traits::Block as BlockT;
@@ -714,6 +720,9 @@ pub struct Params<Block: BlockT> {
714720
/// Block announce protocol configuration
715721
pub block_announce_config: NonDefaultSetConfig,
716722

723+
/// TX channel for direct communication with `SyncingEngine` and `Protocol`.
724+
pub tx: TracingUnboundedSender<crate::event::SyncEvent<Block>>,
725+
717726
/// Request response protocol configurations
718727
pub request_response_protocol_configs: Vec<RequestResponseConfig>,
719728
}

client/network/src/event.rs

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,14 @@
1919
//! Network event types. These are are not the part of the protocol, but rather
2020
//! events that happen on the network like DHT get/put results received.
2121
22-
use crate::types::ProtocolName;
22+
use crate::{types::ProtocolName, NotificationsSink};
2323

2424
use bytes::Bytes;
25+
use futures::channel::oneshot;
2526
use libp2p::{core::PeerId, kad::record::Key};
2627

27-
use sc_network_common::role::ObservedRole;
28+
use sc_network_common::{role::ObservedRole, sync::message::BlockAnnouncesHandshake};
29+
use sp_runtime::traits::Block as BlockT;
2830

2931
/// Events generated by DHT as a response to get_value and put_value requests.
3032
#[derive(Debug, Clone)]
@@ -90,3 +92,44 @@ pub enum Event {
9092
messages: Vec<(ProtocolName, Bytes)>,
9193
},
9294
}
95+
96+
/// Event sent to `SyncingEngine`
97+
// TODO: remove once `NotificationService` is implemented.
98+
pub enum SyncEvent<B: BlockT> {
99+
/// Opened a substream with the given node with the given notifications protocol.
100+
///
101+
/// The protocol is always one of the notification protocols that have been registered.
102+
NotificationStreamOpened {
103+
/// Node we opened the substream with.
104+
remote: PeerId,
105+
/// Received handshake.
106+
received_handshake: BlockAnnouncesHandshake<B>,
107+
/// Notification sink.
108+
sink: NotificationsSink,
109+
/// Channel for reporting accept/reject of the substream.
110+
tx: oneshot::Sender<bool>,
111+
},
112+
113+
/// Closed a substream with the given node. Always matches a corresponding previous
114+
/// `NotificationStreamOpened` message.
115+
NotificationStreamClosed {
116+
/// Node we closed the substream with.
117+
remote: PeerId,
118+
},
119+
120+
/// Notification sink was replaced.
121+
NotificationSinkReplaced {
122+
/// Node we closed the substream with.
123+
remote: PeerId,
124+
/// Notification sink.
125+
sink: NotificationsSink,
126+
},
127+
128+
/// Received one or more messages from the given node using the given protocol.
129+
NotificationsReceived {
130+
/// Node we received the message from.
131+
remote: PeerId,
132+
/// Concerned protocol and associated message.
133+
messages: Vec<Bytes>,
134+
},
135+
}

client/network/src/lib.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,7 @@ pub mod request_responses;
259259
pub mod types;
260260
pub mod utils;
261261

262-
pub use event::{DhtEvent, Event};
262+
pub use event::{DhtEvent, Event, SyncEvent};
263263
#[doc(inline)]
264264
pub use libp2p::{multiaddr, Multiaddr, PeerId};
265265
pub use request_responses::{IfDisconnected, RequestFailure, RequestResponseConfig};
@@ -278,8 +278,8 @@ pub use service::{
278278
NetworkStatusProvider, NetworkSyncForkRequest, NotificationSender as NotificationSenderT,
279279
NotificationSenderError, NotificationSenderReady,
280280
},
281-
DecodingError, Keypair, NetworkService, NetworkWorker, NotificationSender, OutboundFailure,
282-
PublicKey,
281+
DecodingError, Keypair, NetworkService, NetworkWorker, NotificationSender, NotificationsSink,
282+
OutboundFailure, PublicKey,
283283
};
284284
pub use types::ProtocolName;
285285

client/network/src/protocol.rs

Lines changed: 90 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ use crate::{
2424

2525
use bytes::Bytes;
2626
use codec::{DecodeAll, Encode};
27+
use futures::{channel::oneshot, stream::FuturesUnordered, StreamExt};
2728
use libp2p::{
2829
core::connection::ConnectionId,
2930
swarm::{
@@ -35,11 +36,14 @@ use libp2p::{
3536
use log::{debug, error, warn};
3637

3738
use sc_network_common::{role::Roles, sync::message::BlockAnnouncesHandshake};
39+
use sc_utils::mpsc::TracingUnboundedSender;
3840
use sp_runtime::traits::Block as BlockT;
3941

4042
use std::{
4143
collections::{HashMap, HashSet, VecDeque},
44+
future::Future,
4245
iter,
46+
pin::Pin,
4347
task::Poll,
4448
};
4549

@@ -68,6 +72,9 @@ mod rep {
6872
pub const BAD_MESSAGE: Rep = Rep::new(-(1 << 12), "Bad message");
6973
}
7074

75+
type PendingSyncSubstreamValidation =
76+
Pin<Box<dyn Future<Output = Result<(PeerId, Roles), PeerId>> + Send>>;
77+
7178
// Lock must always be taken in order declared here.
7279
pub struct Protocol<B: BlockT> {
7380
/// Pending list of messages to return from `poll` as a priority.
@@ -87,6 +94,8 @@ pub struct Protocol<B: BlockT> {
8794
bad_handshake_substreams: HashSet<(PeerId, sc_peerset::SetId)>,
8895
/// Connected peers.
8996
peers: HashMap<PeerId, Roles>,
97+
sync_substream_validations: FuturesUnordered<PendingSyncSubstreamValidation>,
98+
tx: TracingUnboundedSender<crate::event::SyncEvent<B>>,
9099
_marker: std::marker::PhantomData<B>,
91100
}
92101

@@ -96,6 +105,7 @@ impl<B: BlockT> Protocol<B> {
96105
roles: Roles,
97106
network_config: &config::NetworkConfiguration,
98107
block_announces_protocol: config::NonDefaultSetConfig,
108+
tx: TracingUnboundedSender<crate::event::SyncEvent<B>>,
99109
) -> error::Result<(Self, sc_peerset::PeersetHandle, Vec<(PeerId, Multiaddr)>)> {
100110
let mut known_addresses = Vec::new();
101111

@@ -179,6 +189,8 @@ impl<B: BlockT> Protocol<B> {
179189
.collect(),
180190
bad_handshake_substreams: Default::default(),
181191
peers: HashMap::new(),
192+
sync_substream_validations: FuturesUnordered::new(),
193+
tx,
182194
// TODO: remove when `BlockAnnouncesHandshake` is moved away from `Protocol`
183195
_marker: Default::default(),
184196
};
@@ -418,6 +430,23 @@ impl<B: BlockT> NetworkBehaviour for Protocol<B> {
418430
return Poll::Ready(NetworkBehaviourAction::CloseConnection { peer_id, connection }),
419431
};
420432

433+
while let Poll::Ready(Some(validation_result)) =
434+
self.sync_substream_validations.poll_next_unpin(cx)
435+
{
436+
match validation_result {
437+
Ok((peer, roles)) => {
438+
self.peers.insert(peer, roles);
439+
},
440+
Err(peer) => {
441+
log::debug!(
442+
target: "sub-libp2p",
443+
"`SyncingEngine` rejected stream"
444+
);
445+
self.behaviour.disconnect_peer(&peer, HARDCODED_PEERSETS_SYNC);
446+
},
447+
}
448+
}
449+
421450
let outcome = match event {
422451
NotificationsOut::CustomProtocolOpen {
423452
peer_id,
@@ -440,16 +469,29 @@ impl<B: BlockT> NetworkBehaviour for Protocol<B> {
440469
best_hash: handshake.best_hash,
441470
genesis_hash: handshake.genesis_hash,
442471
};
443-
self.peers.insert(peer_id, roles);
444472

445-
CustomMessageOutcome::NotificationStreamOpened {
446-
remote: peer_id,
447-
protocol: self.notification_protocols[usize::from(set_id)].clone(),
448-
negotiated_fallback,
449-
received_handshake: handshake.encode(),
450-
roles,
451-
notifications_sink,
452-
}
473+
let (tx, rx) = oneshot::channel();
474+
let _ = self.tx.unbounded_send(
475+
crate::SyncEvent::NotificationStreamOpened {
476+
remote: peer_id,
477+
received_handshake: handshake,
478+
sink: notifications_sink,
479+
tx,
480+
},
481+
);
482+
self.sync_substream_validations.push(Box::pin(async move {
483+
match rx.await {
484+
Ok(accepted) =>
485+
if accepted {
486+
Ok((peer_id, roles))
487+
} else {
488+
Err(peer_id)
489+
},
490+
Err(_) => Err(peer_id),
491+
}
492+
}));
493+
494+
CustomMessageOutcome::None
453495
},
454496
Ok(msg) => {
455497
debug!(
@@ -469,15 +511,27 @@ impl<B: BlockT> NetworkBehaviour for Protocol<B> {
469511
let roles = handshake.roles;
470512
self.peers.insert(peer_id, roles);
471513

472-
CustomMessageOutcome::NotificationStreamOpened {
473-
remote: peer_id,
474-
protocol: self.notification_protocols[usize::from(set_id)]
475-
.clone(),
476-
negotiated_fallback,
477-
received_handshake,
478-
roles,
479-
notifications_sink,
480-
}
514+
let (tx, rx) = oneshot::channel();
515+
let _ = self.tx.unbounded_send(
516+
crate::SyncEvent::NotificationStreamOpened {
517+
remote: peer_id,
518+
received_handshake: handshake,
519+
sink: notifications_sink,
520+
tx,
521+
},
522+
);
523+
self.sync_substream_validations.push(Box::pin(async move {
524+
match rx.await {
525+
Ok(accepted) =>
526+
if accepted {
527+
Ok((peer_id, roles))
528+
} else {
529+
Err(peer_id)
530+
},
531+
Err(_) => Err(peer_id),
532+
}
533+
}));
534+
CustomMessageOutcome::None
481535
},
482536
Err(err2) => {
483537
log::debug!(
@@ -535,6 +589,12 @@ impl<B: BlockT> NetworkBehaviour for Protocol<B> {
535589
NotificationsOut::CustomProtocolReplaced { peer_id, notifications_sink, set_id } =>
536590
if self.bad_handshake_substreams.contains(&(peer_id, set_id)) {
537591
CustomMessageOutcome::None
592+
} else if set_id == HARDCODED_PEERSETS_SYNC {
593+
let _ = self.tx.unbounded_send(crate::SyncEvent::NotificationSinkReplaced {
594+
remote: peer_id,
595+
sink: notifications_sink,
596+
});
597+
CustomMessageOutcome::None
538598
} else {
539599
CustomMessageOutcome::NotificationStreamReplaced {
540600
remote: peer_id,
@@ -548,6 +608,12 @@ impl<B: BlockT> NetworkBehaviour for Protocol<B> {
548608
// handshake. The outer layers have never received an opening event about this
549609
// substream, and consequently shouldn't receive a closing event either.
550610
CustomMessageOutcome::None
611+
} else if set_id == HARDCODED_PEERSETS_SYNC {
612+
let _ = self.tx.unbounded_send(crate::SyncEvent::NotificationStreamClosed {
613+
remote: peer_id,
614+
});
615+
self.peers.remove(&peer_id);
616+
CustomMessageOutcome::None
551617
} else {
552618
CustomMessageOutcome::NotificationStreamClosed {
553619
remote: peer_id,
@@ -558,6 +624,12 @@ impl<B: BlockT> NetworkBehaviour for Protocol<B> {
558624
NotificationsOut::Notification { peer_id, set_id, message } => {
559625
if self.bad_handshake_substreams.contains(&(peer_id, set_id)) {
560626
CustomMessageOutcome::None
627+
} else if set_id == HARDCODED_PEERSETS_SYNC {
628+
let _ = self.tx.unbounded_send(crate::SyncEvent::NotificationsReceived {
629+
remote: peer_id,
630+
messages: vec![message.freeze()],
631+
});
632+
CustomMessageOutcome::None
561633
} else {
562634
let protocol_name = self.notification_protocols[usize::from(set_id)].clone();
563635
CustomMessageOutcome::NotificationsReceived {

client/network/src/service.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ use crate::{
3636
network_state::{
3737
NetworkState, NotConnectedPeer as NetworkStateNotConnectedPeer, Peer as NetworkStatePeer,
3838
},
39-
protocol::{self, NotificationsSink, NotifsHandlerError, Protocol, Ready},
39+
protocol::{self, NotifsHandlerError, Protocol, Ready},
4040
request_responses::{IfDisconnected, RequestFailure},
4141
service::{
4242
signature::{Signature, SigningError},
@@ -91,6 +91,7 @@ use std::{
9191

9292
pub use behaviour::{InboundFailure, OutboundFailure, ResponseFailure};
9393
pub use libp2p::identity::{error::DecodingError, Keypair, PublicKey};
94+
pub use protocol::NotificationsSink;
9495

9596
mod metrics;
9697
mod out_events;
@@ -146,7 +147,7 @@ where
146147
/// Returns a `NetworkWorker` that implements `Future` and must be regularly polled in order
147148
/// for the network processing to advance. From it, you can extract a `NetworkService` using
148149
/// `worker.service()`. The `NetworkService` can be shared through the codebase.
149-
pub fn new<Block: BlockT>(mut params: Params<Block>) -> Result<Self, Error> {
150+
pub fn new(mut params: Params<B>) -> Result<Self, Error> {
150151
// Private and public keys configuration.
151152
let local_identity = params.network_config.node_key.clone().into_keypair()?;
152153
let local_public = local_identity.public();
@@ -227,6 +228,7 @@ where
227228
From::from(&params.role),
228229
&params.network_config,
229230
params.block_announce_config,
231+
params.tx,
230232
)?;
231233

232234
// List of multiaddresses that we know in the network.

0 commit comments

Comments
 (0)