Files
pezkuwi-subxt/polkadot/node/network/bridge/src/multiplexer.rs
T
Robert Klotzner 305375e1e4 Req/res optimization for statement distribution (#2803)
* Wip

* Increase proposer timeout.

* WIP.

* Better timeout values now that we are going to be connected to all nodes. (#2778)

* Better timeout values.

* Fix typo.

* Fix validator bandwidth.

* Fix compilation.

* Better and more consistent sizes.

Most importantly code size is now 5 Meg, which is the limit we currently
want to support in statement distribution.

* Introduce statement fetching request.

* WIP

* Statement cache retrieval logic.

* Review remarks by @rphmeier

* Fixes.

* Better requester logic.

* WIP: Handle requester messages.

* Missing dep.

* Fix request launching logic.

* Finish fetching logic.

* Sending logic.

* Redo code size calculations.

Now that max code size is compressed size.

* Update Cargo.lock (new dep)

* Get request receiver to statement distribution.

* Expose new functionality for responding to requests.

* Cleanup.

* Responder logic.

* Fixes + Cleanup.

* Cargo.lock

* Whitespace.

* Add lost copyright.

* Launch responder task.

* Typo.

* info -> warn

* Typo.

* Fix.

* Fix.

* Update comment.

* Doc fix.

* Better large statement heuristics.

* Fix tests.

* Fix network bridge tests.

* Add test for size estimate.

* Very simple tests that checks we get LargeStatement.

* Basic check, that fetching of large candidates is performed.

* More tests.

* Basic metrics for responder.

* More metrics.

* Use Encode::encoded_size().

* Some useful spans.

* Get rid of redundant metrics.

* Don't add peer on duplicate.

* Properly check hash

instead of relying on signatures alone.

* Preserve ordering + better flood protection.

* Get rid of redundant clone.

* Don't shutdown responder on failed query.

And add test for this.

* Smaller fixes.

* Quotes.

* Better queue size calculation.

* A bit saner response sizes.

* Fixes.
2021-04-09 21:30:12 +00:00

213 lines
6.1 KiB
Rust

// Copyright 2021 Parity Technologies (UK) Ltd.
// This file is part of Polkadot.
// Polkadot is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Polkadot is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
use std::pin::Pin;
use futures::channel::mpsc;
use futures::stream::{FusedStream, Stream};
use futures::task::{Context, Poll};
use strum::IntoEnumIterator;
use parity_scale_codec::{Decode, Error as DecodingError};
use sc_network::config as network;
use sc_network::PeerId;
use polkadot_node_network_protocol::request_response::{
request::IncomingRequest, v1, Protocol, RequestResponseConfig,
};
use polkadot_subsystem::messages::AllMessages;
/// Multiplex incoming network requests.
///
/// This multiplexer consumes all request streams and makes them a `Stream` of a single message
/// type, useful for the network bridge to send them via the `Overseer` to other subsystems.
///
/// The resulting stream will end once any of its input ends.
///
/// TODO: Get rid of this: https://github.com/paritytech/polkadot/issues/2842
pub struct RequestMultiplexer {
receivers: Vec<(Protocol, mpsc::Receiver<network::IncomingRequest>)>,
statement_fetching: Option<mpsc::Receiver<network::IncomingRequest>>,
next_poll: usize,
}
/// Multiplexing can fail in case of invalid messages.
#[derive(Debug, PartialEq, Eq)]
pub struct RequestMultiplexError {
/// The peer that sent the invalid message.
pub peer: PeerId,
/// The error that occurred.
pub error: DecodingError,
}
impl RequestMultiplexer {
/// Create a new `RequestMultiplexer`.
///
/// This function uses `Protocol::get_config` for each available protocol and creates a
/// `RequestMultiplexer` from it. The returned `RequestResponseConfig`s must be passed to the
/// network implementation.
pub fn new() -> (Self, Vec<RequestResponseConfig>) {
let (mut receivers, cfgs): (Vec<_>, Vec<_>) = Protocol::iter()
.map(|p| {
let (rx, cfg) = p.get_config();
((p, rx), cfg)
})
.unzip();
let index = receivers.iter().enumerate().find_map(|(i, (p, _))|
if let Protocol::StatementFetching = p {
Some(i)
} else {
None
}
).expect("Statement fetching must be registered. qed.");
let statement_fetching = Some(receivers.remove(index).1);
(
Self {
receivers,
statement_fetching,
next_poll: 0,
},
cfgs,
)
}
/// Get the receiver for handling statement fetching requests.
///
/// This function will only return `Some` once.
pub fn get_statement_fetching(&mut self) -> Option<mpsc::Receiver<network::IncomingRequest>> {
std::mem::take(&mut self.statement_fetching)
}
}
impl Stream for RequestMultiplexer {
type Item = Result<AllMessages, RequestMultiplexError>;
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
let len = self.receivers.len();
let mut count = len;
let mut i = self.next_poll;
let mut result = Poll::Ready(None);
// Poll streams in round robin fashion:
while count > 0 {
// % safe, because count initialized to len, loop would not be entered if 0, also
// length of receivers is fixed.
let (p, rx): &mut (_, _) = &mut self.receivers[i % len];
// Avoid panic:
if rx.is_terminated() {
// Early return, we don't want to update next_poll.
return Poll::Ready(None);
}
i += 1;
count -= 1;
match Pin::new(rx).poll_next(cx) {
Poll::Pending => result = Poll::Pending,
// We are done, once a single receiver is done.
Poll::Ready(None) => return Poll::Ready(None),
Poll::Ready(Some(v)) => {
result = Poll::Ready(Some(multiplex_single(*p, v)));
break;
}
}
}
self.next_poll = i;
result
}
}
impl FusedStream for RequestMultiplexer {
fn is_terminated(&self) -> bool {
let len = self.receivers.len();
if len == 0 {
return true;
}
let (_, rx) = &self.receivers[self.next_poll % len];
rx.is_terminated()
}
}
/// Convert a single raw incoming request into a `MultiplexMessage`.
fn multiplex_single(
p: Protocol,
network::IncomingRequest {
payload,
peer,
pending_response,
}: network::IncomingRequest,
) -> Result<AllMessages, RequestMultiplexError> {
let r = match p {
Protocol::ChunkFetching => From::from(IncomingRequest::new(
peer,
decode_with_peer::<v1::ChunkFetchingRequest>(peer, payload)?,
pending_response,
)),
Protocol::CollationFetching => From::from(IncomingRequest::new(
peer,
decode_with_peer::<v1::CollationFetchingRequest>(peer, payload)?,
pending_response,
)),
Protocol::PoVFetching => From::from(IncomingRequest::new(
peer,
decode_with_peer::<v1::PoVFetchingRequest>(peer, payload)?,
pending_response,
)),
Protocol::AvailableDataFetching => From::from(IncomingRequest::new(
peer,
decode_with_peer::<v1::AvailableDataFetchingRequest>(peer, payload)?,
pending_response,
)),
Protocol::StatementFetching => {
panic!("Statement fetching requests are handled directly. qed.");
}
};
Ok(r)
}
fn decode_with_peer<Req: Decode>(
peer: PeerId,
payload: Vec<u8>,
) -> Result<Req, RequestMultiplexError> {
Req::decode(&mut payload.as_ref()).map_err(|error| RequestMultiplexError { peer, error })
}
#[cfg(test)]
mod tests {
use futures::prelude::*;
use futures::stream::FusedStream;
use super::RequestMultiplexer;
#[test]
fn check_exhaustion_safety() {
// Create and end streams:
fn drop_configs() -> RequestMultiplexer {
let (multiplexer, _) = RequestMultiplexer::new();
multiplexer
}
let multiplexer = drop_configs();
futures::executor::block_on(async move {
let mut f = multiplexer;
assert!(f.next().await.is_none());
assert!(f.is_terminated());
assert!(f.next().await.is_none());
assert!(f.is_terminated());
assert!(f.next().await.is_none());
assert!(f.is_terminated());
});
}
}