Skip to content

Commit f9951e7

Browse files
committed
Update smoke test to check that connection id changes after delay
1 parent 79cc245 commit f9951e7

File tree

4 files changed

+272
-24
lines changed

4 files changed

+272
-24
lines changed

iroh-connection-pool/Cargo.toml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,15 @@ version = "0.1.0"
44
edition = "2024"
55

66
[dependencies]
7-
iroh = "0.91.1"
7+
iroh = { version = "0.91.1", git = "https://github.com/n0-computer/iroh", branch = "connection-speed-test" }
88
n0-future = "0.2.0"
99
snafu = "0.8.6"
1010
tokio = "1.45"
1111
tokio-util = { version = "0.7", features = ["time"] }
1212
tracing = "0.1.41"
13+
14+
[dev-dependencies]
15+
anyhow = "1.0.99"
16+
n0-snafu = "0.2.1"
17+
testresult = "0.4.1"
18+
tracing-subscriber = { version = "0.3.19", features = ["env-filter"] }

iroh-connection-pool/src/connection_pool.rs

Lines changed: 77 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,11 @@ use iroh::{
1717
use n0_future::{MaybeFuture, boxed::BoxFuture};
1818
use snafu::Snafu;
1919
use tokio::{
20-
sync::mpsc,
20+
sync::{mpsc, oneshot},
2121
task::{JoinError, JoinSet},
2222
};
2323
use tokio_util::time::FutureExt;
24+
use tracing::{debug, error, trace};
2425

2526
/// Configuration options for the connection pool
2627
#[derive(Debug, Clone, Copy)]
@@ -47,24 +48,36 @@ struct Context {
4748
alpn: Vec<u8>,
4849
}
4950

50-
type BoxedHandler =
51-
Box<dyn FnOnce(&PoolConnectResult) -> BoxFuture<ExecuteResult> + Send + 'static>;
51+
type BoxedHandler = Box<dyn FnOnce(PoolConnectResult) -> BoxFuture<ExecuteResult> + Send + 'static>;
5252

5353
/// Error when a connection can not be acquired
5454
///
5555
/// This includes the normal iroh connection errors as well as pool specific
5656
/// errors such as timeouts and connection limits.
57+
#[derive(Debug, Clone)]
5758
pub enum PoolConnectError {
5859
/// Timeout during connect
5960
Timeout,
6061
/// Too many connections
6162
TooManyConnections,
6263
/// Error during connect
63-
ConnectError(ConnectError),
64+
ConnectError(Arc<ConnectError>),
6465
/// Error during last execute
65-
ExecuteError(ExecuteError),
66+
ExecuteError(Arc<ExecuteError>),
6667
/// Handler actor panicked
67-
JoinError(JoinError),
68+
JoinError(Arc<JoinError>),
69+
}
70+
71+
impl std::fmt::Display for PoolConnectError {
72+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
73+
match self {
74+
PoolConnectError::Timeout => write!(f, "Connection timed out"),
75+
PoolConnectError::TooManyConnections => write!(f, "Too many connections"),
76+
PoolConnectError::ConnectError(e) => write!(f, "Connection error: {}", e),
77+
PoolConnectError::ExecuteError(e) => write!(f, "Execution error: {}", e),
78+
PoolConnectError::JoinError(e) => write!(f, "Join error: {}", e),
79+
}
80+
}
6881
}
6982

7083
pub type PoolConnectResult = std::result::Result<Connection, PoolConnectError>;
@@ -88,11 +101,14 @@ async fn run_connection_actor(
88101
.await
89102
{
90103
Ok(Ok(conn)) => Ok(conn),
91-
Ok(Err(e)) => Err(PoolConnectError::ConnectError(e)),
104+
Ok(Err(e)) => Err(PoolConnectError::ConnectError(Arc::new(e))),
92105
Err(_) => Err(PoolConnectError::Timeout),
93106
};
94-
if state.is_err() && context.owner.close(node_id).await.is_err() {
95-
return;
107+
if let Err(e) = &state {
108+
debug!(%node_id, "Failed to connect {e:?}, requesting shutdown");
109+
if context.owner.close(node_id).await.is_err() {
110+
return;
111+
}
96112
}
97113

98114
let mut tasks = JoinSet::new();
@@ -107,9 +123,10 @@ async fn run_connection_actor(
107123
handler = rx.recv() => {
108124
match handler {
109125
Some(handler) => {
126+
trace!(%node_id, "Received new task");
110127
// clear the idle timer
111128
idle_timer.as_mut().set_none();
112-
tasks.spawn(handler(&state));
129+
tasks.spawn(handler(state.clone()));
113130
}
114131
None => {
115132
// Channel closed - finish remaining tasks and exit
@@ -122,22 +139,22 @@ async fn run_connection_actor(
122139
Some(task_result) = tasks.join_next(), if !tasks.is_empty() => {
123140
match task_result {
124141
Ok(Ok(())) => {
125-
tracing::debug!("Task completed for node {}", node_id);
142+
debug!(%node_id, "Task completed");
126143
}
127144
Ok(Err(e)) => {
128-
tracing::error!("Task failed for node {}: {}", node_id, e);
145+
error!(%node_id, "Task failed: {}", e);
129146
if let Ok(conn) = state {
130147
conn.close(1u32.into(), b"error");
131148
}
132-
state = Err(PoolConnectError::ExecuteError(e));
149+
state = Err(PoolConnectError::ExecuteError(Arc::new(e)));
133150
let _ = context.owner.close(node_id).await;
134151
}
135152
Err(e) => {
136-
tracing::error!("Task panicked for node {}: {}", node_id, e);
153+
error!(%node_id, "Task panicked: {}", e);
137154
if let Ok(conn) = state {
138155
conn.close(1u32.into(), b"panic");
139156
}
140-
state = Err(PoolConnectError::JoinError(e));
157+
state = Err(PoolConnectError::JoinError(Arc::new(e)));
141158
let _ = context.owner.close(node_id).await;
142159
}
143160
}
@@ -155,8 +172,7 @@ async fn run_connection_actor(
155172

156173
// Idle timeout - request shutdown
157174
_ = &mut idle_timer => {
158-
tracing::debug!("Connection to {} idle, requesting shutdown", node_id);
159-
175+
debug!(%node_id, "Connection idle, requesting shutdown");
160176
context.owner.close(node_id).await.ok();
161177
// Don't break here - wait for main actor to close our channel
162178
}
@@ -166,15 +182,15 @@ async fn run_connection_actor(
166182
// Wait for remaining tasks to complete
167183
while let Some(task_result) = tasks.join_next().await {
168184
if let Err(e) = task_result {
169-
tracing::error!("Task failed during shutdown for node {}: {}", node_id, e);
185+
error!(%node_id, "Task failed during shutdown: {}", e);
170186
}
171187
}
172188

173189
if let Ok(connection) = &state {
174190
connection.close(0u32.into(), b"idle");
175191
}
176192

177-
tracing::debug!("Connection actor for {} shutting down", node_id);
193+
debug!(%node_id, "Connection actor shutting down");
178194
}
179195

180196
struct Actor {
@@ -224,7 +240,7 @@ impl Actor {
224240

225241
// No connection actor or it died - spawn a new one
226242
if self.connections.len() >= self.context.options.max_connections {
227-
handler(&Err(PoolConnectError::TooManyConnections))
243+
handler(Err(PoolConnectError::TooManyConnections))
228244
.await
229245
.ok();
230246
continue;
@@ -273,7 +289,14 @@ pub struct ExecuteError;
273289

274290
type ExecuteResult = std::result::Result<(), ExecuteError>;
275291

292+
impl From<PoolConnectError> for ExecuteError {
293+
fn from(_: PoolConnectError) -> Self {
294+
ExecuteError
295+
}
296+
}
297+
276298
/// A connection pool
299+
#[derive(Debug, Clone)]
277300
pub struct ConnectionPool {
278301
tx: mpsc::Sender<ActorMessage>,
279302
}
@@ -301,11 +324,11 @@ impl ConnectionPool {
301324
f: F,
302325
) -> std::result::Result<(), ConnectionPoolError>
303326
where
304-
F: FnOnce(&PoolConnectResult) -> Fut + Send + 'static,
305-
Fut: std::future::Future<Output = ExecuteResult> + Send + 'static,
327+
F: FnOnce(PoolConnectResult) -> Fut + Send + 'static,
328+
Fut: Future<Output = ExecuteResult> + Send + 'static,
306329
{
307330
let handler =
308-
Box::new(move |conn: &PoolConnectResult| Box::pin(f(conn)) as BoxFuture<ExecuteResult>);
331+
Box::new(move |conn: PoolConnectResult| Box::pin(f(conn)) as BoxFuture<ExecuteResult>);
309332

310333
self.tx
311334
.send(ActorMessage::Handle { id, handler })
@@ -315,6 +338,37 @@ impl ConnectionPool {
315338
Ok(())
316339
}
317340

341+
pub async fn with_connection<F, Fut, I, E>(
342+
&self,
343+
id: NodeId,
344+
f: F,
345+
) -> Result<Result<Result<I, E>, PoolConnectError>, ConnectionPoolError>
346+
where
347+
F: FnOnce(Connection) -> Fut + Send + 'static,
348+
Fut: Future<Output = Result<I, E>> + Send + 'static,
349+
I: Send + 'static,
350+
E: Send + 'static,
351+
{
352+
let (tx, rx) = oneshot::channel();
353+
self.connect(id, |conn| async move {
354+
let (res, ret) = match conn {
355+
Ok(connection) => {
356+
let res = f(connection).await;
357+
let ret = match &res {
358+
Ok(_) => Ok(()),
359+
Err(_) => Err(ExecuteError),
360+
};
361+
(Ok(res), ret)
362+
}
363+
Err(e) => (Err(e), Err(ExecuteError)),
364+
};
365+
tx.send(res).ok();
366+
ret
367+
})
368+
.await?;
369+
rx.await.map_err(|_| ConnectionPoolError::Shutdown)
370+
}
371+
318372
/// Close an existing connection, if it exists
319373
///
320374
/// This will finish pending tasks and close the connection. New tasks will

iroh-connection-pool/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,5 @@
11
pub mod connection_pool;
22
pub mod connection_pool_0rtt;
3+
4+
#[cfg(test)]
5+
mod tests;

0 commit comments

Comments
 (0)