use crate::{
counters::{
self, network_application_inbound_traffic, network_application_outbound_traffic,
CANCELED_LABEL, DECLINED_LABEL, FAILED_LABEL, RECEIVED_LABEL, REQUEST_LABEL,
RESPONSE_LABEL, SENT_LABEL,
},
logging::NetworkSchema,
peer::PeerNotification,
peer_manager::PeerManagerError,
protocols::{
network::SerializedRequest,
wire::messaging::v1::{NetworkMessage, Priority, RequestId, RpcRequest, RpcResponse},
},
ProtocolId,
};
use anyhow::anyhow;
use aptos_config::network_id::NetworkContext;
use aptos_id_generator::{IdGenerator, U32IdGenerator};
use aptos_logger::prelude::*;
use aptos_time_service::{timeout, TimeService, TimeServiceTrait};
use aptos_types::PeerId;
use bytes::Bytes;
use channel::aptos_channel;
use error::RpcError;
use futures::{
channel::oneshot,
future::{BoxFuture, FusedFuture, Future, FutureExt},
sink::SinkExt,
stream::{FuturesUnordered, StreamExt},
};
use serde::Serialize;
use short_hex_str::AsShortHexStr;
use std::{cmp::PartialEq, collections::HashMap, fmt::Debug, time::Duration};
pub mod error;
#[derive(Debug)]
pub struct InboundRpcRequest {
pub protocol_id: ProtocolId,
pub data: Bytes,
pub res_tx: oneshot::Sender<Result<Bytes, RpcError>>,
}
impl SerializedRequest for InboundRpcRequest {
fn protocol_id(&self) -> ProtocolId {
self.protocol_id
}
fn data(&self) -> &Bytes {
&self.data
}
}
#[derive(Debug, Serialize)]
pub struct OutboundRpcRequest {
pub protocol_id: ProtocolId,
#[serde(skip)]
pub data: Bytes,
#[serde(skip)]
pub res_tx: oneshot::Sender<Result<Bytes, RpcError>>,
pub timeout: Duration,
}
impl SerializedRequest for OutboundRpcRequest {
fn protocol_id(&self) -> ProtocolId {
self.protocol_id
}
fn data(&self) -> &Bytes {
&self.data
}
}
impl PartialEq for InboundRpcRequest {
fn eq(&self, other: &Self) -> bool {
self.protocol_id == other.protocol_id && self.data == other.data
}
}
pub struct InboundRpcs {
network_context: NetworkContext,
time_service: TimeService,
remote_peer_id: PeerId,
inbound_rpc_tasks: FuturesUnordered<BoxFuture<'static, Result<RpcResponse, RpcError>>>,
inbound_rpc_timeout: Duration,
max_concurrent_inbound_rpcs: u32,
}
impl InboundRpcs {
pub fn new(
network_context: NetworkContext,
time_service: TimeService,
remote_peer_id: PeerId,
inbound_rpc_timeout: Duration,
max_concurrent_inbound_rpcs: u32,
) -> Self {
Self {
network_context,
time_service,
remote_peer_id,
inbound_rpc_tasks: FuturesUnordered::new(),
inbound_rpc_timeout,
max_concurrent_inbound_rpcs,
}
}
pub fn handle_inbound_request(
&mut self,
peer_notifs_tx: &mut aptos_channel::Sender<ProtocolId, PeerNotification>,
request: RpcRequest,
) -> Result<(), RpcError> {
let network_context = &self.network_context;
if self.inbound_rpc_tasks.len() as u32 == self.max_concurrent_inbound_rpcs {
counters::rpc_messages(network_context, RESPONSE_LABEL, DECLINED_LABEL).inc();
return Err(RpcError::TooManyPending(self.max_concurrent_inbound_rpcs));
}
let protocol_id = request.protocol_id;
let request_id = request.request_id;
let priority = request.priority;
let req_len = request.raw_request.len() as u64;
trace!(
NetworkSchema::new(network_context).remote_peer(&self.remote_peer_id),
"{} Received inbound rpc request from peer {} with request_id {} and protocol_id {}",
network_context,
self.remote_peer_id.short_str(),
request_id,
protocol_id,
);
counters::rpc_messages(network_context, REQUEST_LABEL, RECEIVED_LABEL).inc();
counters::rpc_bytes(network_context, REQUEST_LABEL, RECEIVED_LABEL).inc_by(req_len);
network_application_inbound_traffic(self.network_context, protocol_id, req_len);
let timer =
counters::inbound_rpc_handler_latency(network_context, protocol_id).start_timer();
let (response_tx, response_rx) = oneshot::channel();
let notif = PeerNotification::RecvRpc(InboundRpcRequest {
protocol_id,
data: Bytes::from(request.raw_request),
res_tx: response_tx,
});
if let Err(err) = peer_notifs_tx.push(protocol_id, notif) {
counters::rpc_messages(network_context, RESPONSE_LABEL, FAILED_LABEL).inc();
return Err(err.into());
}
let inbound_rpc_task = self
.time_service
.timeout(self.inbound_rpc_timeout, response_rx)
.map(move |result| {
let maybe_response = match result {
Ok(Ok(Ok(response_bytes))) => Ok(RpcResponse {
request_id,
priority,
raw_response: Vec::from(response_bytes.as_ref()),
}),
Ok(Ok(Err(err))) => Err(err),
Ok(Err(oneshot::Canceled)) => Err(RpcError::UnexpectedResponseChannelCancel),
Err(timeout::Elapsed) => Err(RpcError::TimedOut),
};
match maybe_response {
Ok(_) => timer.stop_and_record(),
Err(_) => timer.stop_and_discard(),
};
maybe_response
})
.boxed();
self.inbound_rpc_tasks.push(inbound_rpc_task);
Ok(())
}
pub fn next_completed_response(
&mut self,
) -> impl Future<Output = Result<RpcResponse, RpcError>> + FusedFuture + '_ {
self.inbound_rpc_tasks.select_next_some()
}
pub async fn send_outbound_response(
&mut self,
write_reqs_tx: &mut channel::Sender<(
NetworkMessage,
oneshot::Sender<Result<(), PeerManagerError>>,
)>,
maybe_response: Result<RpcResponse, RpcError>,
) -> Result<(), RpcError> {
let network_context = &self.network_context;
let response = match maybe_response {
Ok(response) => response,
Err(err) => {
counters::rpc_messages(network_context, RESPONSE_LABEL, FAILED_LABEL).inc();
return Err(err);
}
};
let res_len = response.raw_response.len() as u64;
trace!(
NetworkSchema::new(network_context).remote_peer(&self.remote_peer_id),
"{} Sending rpc response to peer {} for request_id {}",
network_context,
self.remote_peer_id.short_str(),
response.request_id,
);
let message = NetworkMessage::RpcResponse(response);
let (ack_tx, _) = oneshot::channel();
write_reqs_tx.send((message, ack_tx)).await?;
counters::rpc_messages(network_context, RESPONSE_LABEL, SENT_LABEL).inc();
counters::rpc_bytes(network_context, RESPONSE_LABEL, SENT_LABEL).inc_by(res_len);
Ok(())
}
}
pub struct OutboundRpcs {
network_context: NetworkContext,
time_service: TimeService,
remote_peer_id: PeerId,
request_id_gen: U32IdGenerator,
outbound_rpc_tasks:
FuturesUnordered<BoxFuture<'static, (RequestId, Result<(f64, u64), RpcError>)>>,
pending_outbound_rpcs: HashMap<RequestId, (ProtocolId, oneshot::Sender<RpcResponse>)>,
max_concurrent_outbound_rpcs: u32,
}
impl OutboundRpcs {
pub fn new(
network_context: NetworkContext,
time_service: TimeService,
remote_peer_id: PeerId,
max_concurrent_outbound_rpcs: u32,
) -> Self {
Self {
network_context,
time_service,
remote_peer_id,
request_id_gen: U32IdGenerator::new(),
outbound_rpc_tasks: FuturesUnordered::new(),
pending_outbound_rpcs: HashMap::new(),
max_concurrent_outbound_rpcs,
}
}
pub async fn handle_outbound_request(
&mut self,
request: OutboundRpcRequest,
write_reqs_tx: &mut channel::Sender<(
NetworkMessage,
oneshot::Sender<Result<(), PeerManagerError>>,
)>,
) -> Result<(), RpcError> {
let network_context = &self.network_context;
let peer_id = &self.remote_peer_id;
let OutboundRpcRequest {
protocol_id,
data: request_data,
timeout,
res_tx: mut application_response_tx,
} = request;
let req_len = request_data.len() as u64;
if application_response_tx.is_canceled() {
counters::rpc_messages(network_context, REQUEST_LABEL, CANCELED_LABEL).inc();
return Err(RpcError::UnexpectedResponseChannelCancel);
}
if self.outbound_rpc_tasks.len() == self.max_concurrent_outbound_rpcs as usize {
counters::rpc_messages(network_context, REQUEST_LABEL, DECLINED_LABEL).inc();
let err = Err(RpcError::TooManyPending(self.max_concurrent_outbound_rpcs));
let _ = application_response_tx.send(err);
return Err(RpcError::TooManyPending(self.max_concurrent_outbound_rpcs));
}
let request_id = self.request_id_gen.next();
trace!(
NetworkSchema::new(network_context).remote_peer(peer_id),
"{} Sending outbound rpc request with request_id {} and protocol_id {} to {}",
network_context,
request_id,
protocol_id,
peer_id.short_str(),
);
let timer =
counters::outbound_rpc_request_latency(network_context, protocol_id).start_timer();
let message = NetworkMessage::RpcRequest(RpcRequest {
protocol_id,
request_id,
priority: Priority::default(),
raw_request: Vec::from(request_data.as_ref()),
});
let (ack_tx, _) = oneshot::channel();
write_reqs_tx.send((message, ack_tx)).await?;
counters::rpc_messages(network_context, REQUEST_LABEL, SENT_LABEL).inc();
counters::rpc_bytes(network_context, REQUEST_LABEL, SENT_LABEL).inc_by(req_len);
network_application_outbound_traffic(self.network_context, protocol_id, req_len);
let (response_tx, response_rx) = oneshot::channel::<RpcResponse>();
self.pending_outbound_rpcs
.insert(request_id, (protocol_id, response_tx));
let wait_for_response = self
.time_service
.timeout(timeout, response_rx)
.map(|result| {
match result {
Ok(Ok(response)) => Ok(Bytes::from(response.raw_response)),
Ok(Err(oneshot::Canceled)) => Err(RpcError::UnexpectedResponseChannelCancel),
Err(timeout::Elapsed) => Err(RpcError::TimedOut),
}
});
let notify_application = async move {
let mut cancellation = application_response_tx.cancellation().fuse();
tokio::pin!(wait_for_response);
futures::select! {
maybe_response = wait_for_response => {
let result_copy = match &maybe_response {
Ok(response) => Ok(response.len() as u64),
Err(err) => Err(RpcError::Error(anyhow!(err.to_string()))),
};
application_response_tx.send(maybe_response).map_err(|_| RpcError::UnexpectedResponseChannelCancel)?;
result_copy
}
_ = cancellation => Err(RpcError::UnexpectedResponseChannelCancel),
}
};
let outbound_rpc_task = async move {
match notify_application.await {
Ok(response_len) => {
let latency = timer.stop_and_record();
(request_id, Ok((latency, response_len)))
}
Err(err) => {
timer.stop_and_discard();
(request_id, Err(err))
}
}
};
self.outbound_rpc_tasks.push(outbound_rpc_task.boxed());
Ok(())
}
pub fn next_completed_request(
&mut self,
) -> impl Future<Output = (RequestId, Result<(f64, u64), RpcError>)> + FusedFuture + '_ {
self.outbound_rpc_tasks.select_next_some()
}
pub fn handle_completed_request(
&mut self,
request_id: RequestId,
result: Result<(f64, u64), RpcError>,
) {
let _ = self.pending_outbound_rpcs.remove(&request_id);
let network_context = &self.network_context;
let peer_id = &self.remote_peer_id;
match result {
Ok((latency, request_len)) => {
counters::rpc_messages(network_context, RESPONSE_LABEL, RECEIVED_LABEL).inc();
counters::rpc_bytes(network_context, RESPONSE_LABEL, RECEIVED_LABEL)
.inc_by(request_len);
trace!(
NetworkSchema::new(network_context).remote_peer(peer_id),
"{} Received response for request_id {} from peer {} \
with {:.6} seconds of latency",
network_context,
request_id,
peer_id.short_str(),
latency,
);
}
Err(err) => {
if let RpcError::UnexpectedResponseChannelCancel = err {
counters::rpc_messages(network_context, REQUEST_LABEL, CANCELED_LABEL).inc();
} else {
counters::rpc_messages(network_context, REQUEST_LABEL, FAILED_LABEL).inc();
}
warn!(
NetworkSchema::new(network_context).remote_peer(peer_id),
"{} Error making outbound rpc request with request_id {} to {}: {}",
network_context,
request_id,
peer_id.short_str(),
err
);
}
}
}
pub fn handle_inbound_response(&mut self, response: RpcResponse) {
let network_context = &self.network_context;
let peer_id = &self.remote_peer_id;
let request_id = response.request_id;
let is_canceled = if let Some((protocol_id, response_tx)) =
self.pending_outbound_rpcs.remove(&request_id)
{
network_application_inbound_traffic(
self.network_context,
protocol_id,
response.raw_response.len() as u64,
);
response_tx.send(response).is_err()
} else {
true
};
if is_canceled {
info!(
NetworkSchema::new(network_context).remote_peer(peer_id),
request_id = request_id,
"{} Received response for expired request_id {} from {}. Discarding.",
network_context,
request_id,
peer_id.short_str(),
);
} else {
trace!(
NetworkSchema::new(network_context).remote_peer(peer_id),
request_id = request_id,
"{} Notified pending outbound rpc task of inbound response for request_id {} from {}",
network_context,
request_id,
peer_id.short_str(),
);
}
}
}