Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 0 additions & 6 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion rs/http_endpoints/async_utils/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ rust_library(
deps = [
# Keep sorted.
"//rs/monitoring/logger",
"@crate_index//:anyhow",
"@crate_index//:async-stream",
"@crate_index//:byte-unit",
"@crate_index//:bytes",
Expand Down
1 change: 0 additions & 1 deletion rs/http_endpoints/async_utils/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ description.workspace = true
documentation.workspace = true

[dependencies]
anyhow = { workspace = true }
async-stream = { workspace = true }
byte-unit = "4.0.14"
bytes = { workspace = true }
Expand Down
1 change: 0 additions & 1 deletion rs/p2p/artifact_downloader/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ DEV_DEPENDENCIES = [
"//rs/test_utilities/consensus",
"//rs/test_utilities/types",
"//rs/types/types_test_utils",
"@crate_index//:anyhow",
"@crate_index//:futures",
"@crate_index//:http-body-util",
"@crate_index//:mockall",
Expand Down
1 change: 0 additions & 1 deletion rs/p2p/artifact_downloader/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ tokio = { workspace = true }
tracing = { workspace = true }

[dev-dependencies]
anyhow = { workspace = true }
futures = { workspace = true }
http-body-util = { workspace = true }
ic-p2p-test-utils = { path = "../test_utils" }
Expand Down
1 change: 0 additions & 1 deletion rs/p2p/consensus_manager/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ DEV_DEPENDENCIES = [
"//rs/p2p/test_utils",
"//rs/test_utilities/logger",
"//rs/types/types_test_utils",
"@crate_index//:anyhow",
"@crate_index//:futures",
"@crate_index//:mockall",
"@crate_index//:tower",
Expand Down
1 change: 0 additions & 1 deletion rs/p2p/consensus_manager/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ tokio-util = { workspace = true }
tracing = { workspace = true }

[dev-dependencies]
anyhow = { workspace = true }
futures = { workspace = true }
ic-p2p-test-utils = { path = "../test_utils" }
ic-test-utilities-logger = { path = "../../test_utilities/logger" }
Expand Down
4 changes: 2 additions & 2 deletions rs/p2p/consensus_manager/src/sender.rs
Original file line number Diff line number Diff line change
Expand Up @@ -405,12 +405,12 @@ mod available_slot_set {
#[allow(clippy::disallowed_methods)]
#[cfg(test)]
mod tests {
use anyhow::anyhow;
use axum::http::Response;
use ic_interfaces::p2p::consensus::{AssembleResult, Peers};
use ic_logger::replica_logger::no_op_logger;
use ic_metrics::MetricsRegistry;
use ic_p2p_test_utils::{consensus::U64Artifact, mocks::MockTransport};
use ic_quic_transport::P2PError;
use ic_test_utilities_logger::with_test_replica_logger;
use ic_types_test_utils::ids::{NODE_1, NODE_2};
use mockall::Sequence;
Expand Down Expand Up @@ -561,7 +561,7 @@ mod tests {
mock_transport
.expect_rpc()
.times(5)
.returning(move |_, _| Err(anyhow!("")))
.returning(move |_, _| Err(P2PError::from("".to_string())))
.in_sequence(&mut seq);
mock_transport.expect_rpc().times(1).returning(move |n, _| {
push_tx.send(*n).unwrap();
Expand Down
1 change: 0 additions & 1 deletion rs/p2p/memory_transport/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ DEPENDENCIES = [
# Keep sorted.
"//rs/p2p/quic_transport",
"//rs/types/types",
"@crate_index//:anyhow",
"@crate_index//:axum",
"@crate_index//:bytes",
"@crate_index//:tokio",
Expand Down
1 change: 0 additions & 1 deletion rs/p2p/memory_transport/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ documentation.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
anyhow = { workspace = true }
async-trait = { workspace = true }
axum = { workspace = true }
bytes = { workspace = true }
Expand Down
11 changes: 5 additions & 6 deletions rs/p2p/memory_transport/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,15 +33,14 @@
/// ┌──────┐ │ │ ┌──────┐
/// │ Node ├───┘ └────┤ Node │
/// └──────┘ └──────┘
use anyhow::anyhow;
use async_trait::async_trait;
use axum::{
body::Body,
http::{Request, Response},
Router,
};
use bytes::Bytes;
use ic_quic_transport::{ConnId, Transport};
use ic_quic_transport::{ConnId, P2PError, Transport};
use ic_types::NodeId;
use std::{
collections::HashMap,
Expand Down Expand Up @@ -280,9 +279,9 @@ impl Transport for PeerTransport {
&self,
peer_id: &NodeId,
mut request: Request<Bytes>,
) -> Result<Response<Bytes>, anyhow::Error> {
) -> Result<Response<Bytes>, P2PError> {
if peer_id == &self.node_id {
return Err(anyhow!("Can't connect to self"));
return Err(P2PError::from("Can't connect to self".to_string()));
}

let (oneshot_tx, oneshot_rx) = oneshot::channel();
Expand All @@ -292,11 +291,11 @@ impl Transport for PeerTransport {
.send((request, *peer_id, oneshot_tx))
.is_err()
{
return Err(anyhow!("router channel closed"));
return Err(P2PError::from("router channel closed".to_string()));
}
match oneshot_rx.await {
Ok(r) => Ok(r),
Err(_) => Err(anyhow!("channel closed")),
Err(_) => Err(P2PError::from("channel closed".to_string())),
}
}

Expand Down
1 change: 0 additions & 1 deletion rs/p2p/quic_transport/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ DEPENDENCIES = [
"//rs/phantom_newtype",
"//rs/protobuf",
"//rs/types/base_types",
"@crate_index//:anyhow",
"@crate_index//:axum",
"@crate_index//:bytes",
"@crate_index//:futures",
Expand Down
1 change: 0 additions & 1 deletion rs/p2p/quic_transport/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ documentation.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
anyhow = { workspace = true }
async-trait = { workspace = true }
axum = { workspace = true }
bytes = { workspace = true }
Expand Down
6 changes: 3 additions & 3 deletions rs/p2p/quic_transport/README.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@ Transport is devided into two logical parts - sending and receiving.

For the sending side, transport exposes the `+rpc+` API call.

Transport uses https://docs.rs/anyhow/latest/anyhow/struct.Error.html[anyhow::Error] instead of https://docs.rs/thiserror/latest/thiserror/derive.Error.html[thiserror::Error] as the error type
because the errors are transient and it is expected that the user retries on failure.
Transport uses a custom opaque error type because the errors are transient and it is expected that the user retries on failure.
We avoid using https://docs.rs/anyhow/latest/anyhow/struct.Error.html[anyhow::Error], due to it's expensive generation of backtraces.

How do you make sure there are only transient errors? For example, how do you ruled out

Expand All @@ -50,7 +50,7 @@ How do you make sure there are only transient errors? For example, how do you ru

[source, rust]
----
async fn rpc(&self, peer_id: &NodeId, request: Request<Bytes>) -> Result<Response<Bytes>, anyhow::Error>;
async fn rpc(&self, peer_id: &NodeId, request: Request<Bytes>) -> Result<Response<Bytes>, P2PError>;
----

The receiving side, is a collection of callbacks, called handlers. Each possible URI is associated with a single handler.
Expand Down
6 changes: 3 additions & 3 deletions rs/p2p/quic_transport/src/connection_handle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use crate::{
observe_conn_error, observe_read_to_end_error, observe_stopped_error, observe_write_error,
QuicTransportMetrics, INFALIBBLE,
},
ConnId, MessagePriority, ResetStreamOnDrop, MAX_MESSAGE_SIZE_BYTES,
ConnId, MessagePriority, P2PError, ResetStreamOnDrop, MAX_MESSAGE_SIZE_BYTES,
};

static CONN_ID_SEQ: AtomicU64 = AtomicU64::new(1);
Expand Down Expand Up @@ -53,7 +53,7 @@ impl ConnectionHandle {
/// where connections can be managed directly by the caller.
///
/// Note: The method is cancel-safe.
pub async fn rpc(&self, request: Request<Bytes>) -> Result<Response<Bytes>, anyhow::Error> {
pub async fn rpc(&self, request: Request<Bytes>) -> Result<Response<Bytes>, P2PError> {
let _timer = self
.metrics
.connection_handle_duration_seconds
Expand Down Expand Up @@ -131,7 +131,7 @@ impl ConnectionHandle {
}

// The function returns infallible error.
fn to_response(response_bytes: Vec<u8>) -> Result<Response<Bytes>, anyhow::Error> {
fn to_response(response_bytes: Vec<u8>) -> Result<Response<Bytes>, P2PError> {
let response_proto = pb::HttpResponse::decode(response_bytes.as_slice())?;
let status: u16 = response_proto.status_code.try_into()?;

Expand Down
92 changes: 86 additions & 6 deletions rs/p2p/quic_transport/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,13 @@
//!
use std::{
collections::{BTreeSet, HashMap},
fmt::Debug,
fmt::{self, Debug, Display, Formatter},
future::Future,
net::SocketAddr,
num::TryFromIntError,
sync::{Arc, RwLock},
};

use anyhow::anyhow;
use async_trait::async_trait;
use axum::{
http::{Request, Response},
Expand All @@ -49,7 +49,11 @@ use ic_interfaces_registry::RegistryClient;
use ic_logger::{info, ReplicaLogger};
use ic_metrics::MetricsRegistry;
use phantom_newtype::AmountOf;
use quinn::{AsyncUdpSocket, SendStream, VarInt};
use quinn::{
AsyncUdpSocket, ClosedStream, ConnectError, ConnectionError, ReadToEndError, SendStream,
StoppedError, VarInt, WriteError,
};
use std::error::Error;
use tokio::sync::watch;
use tokio::task::{JoinError, JoinHandle};
use tokio_util::{sync::CancellationToken, task::task_tracker::TaskTracker};
Expand Down Expand Up @@ -216,13 +220,15 @@ impl Transport for QuicTransport {
&self,
peer_id: &NodeId,
request: Request<Bytes>,
) -> Result<Response<Bytes>, anyhow::Error> {
) -> Result<Response<Bytes>, P2PError> {
let peer = self
.conn_handles
.read()
.unwrap()
.get(peer_id)
.ok_or(anyhow!("Currently not connected to this peer"))?
.ok_or(P2PError::from(
"Currently not connected to this peer".to_string(),
))?
.clone();
peer.rpc(request).await.inspect_err(|err| {
info!(every_n_seconds => 5, self.log, "Error sending rpc request to {}: {:?}", peer_id, err);
Expand All @@ -239,6 +245,80 @@ impl Transport for QuicTransport {
}
}

#[derive(Debug)]
/// Opaque error type wrapping any P2P error, without capturing expensive backtraces.
pub struct P2PError {
inner: Box<dyn Error + Send + Sync>,
}

impl P2PError {
pub fn new<E>(error: E) -> Self
where
E: Into<Box<dyn Error + Send + Sync>>,
{
P2PError {
inner: error.into(),
}
}
}

impl Display for P2PError {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.inner)
}
}

impl Error for P2PError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
self.inner.source()
}
}

impl From<String> for P2PError {
fn from(value: String) -> Self {
P2PError::new(GenericError(value))
}
}

macro_rules! impl_from_for_p2p_error {
($($err_ty:ty),+ $(,)?) => {
$(
impl From<$err_ty> for P2PError {
fn from(err: $err_ty) -> Self {
P2PError::new(err)
}
}
)+
};
}

impl_from_for_p2p_error!(
ConnectError,
WriteError,
StoppedError,
ClosedStream,
ReadToEndError,
ConnectionError,
prost::DecodeError,
prost::UnknownEnumValue,
http::Error,
axum::Error,
TryFromIntError,
);
Comment thread
kpop-dfinity marked this conversation as resolved.

#[derive(Debug, Clone)]
/// A generic error type for wrapping human-readable messages.
/// This is useful because `String` does not implement `Error`.
pub struct GenericError(pub String);
Comment thread
eichhorl marked this conversation as resolved.

impl Display for GenericError {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}

impl Error for GenericError {}

/// Low-level transport interface for exchanging messages between nodes.
///
/// It intentionally uses http::Request and http::Response types.
Expand All @@ -249,7 +329,7 @@ pub trait Transport: Send + Sync {
&self,
peer_id: &NodeId,
request: Request<Bytes>,
) -> Result<Response<Bytes>, anyhow::Error>;
) -> Result<Response<Bytes>, P2PError>;

fn peers(&self) -> Vec<(NodeId, ConnId)>;
}
Expand Down
Loading
Loading