diff --git a/src/api/runtime_edge.rs b/src/api/runtime_edge.rs index cc95fec..ff5b482 100644 --- a/src/api/runtime_edge.rs +++ b/src/api/runtime_edge.rs @@ -67,7 +67,7 @@ pub(super) struct RuntimeEdgeConnectionsSummaryData { } #[derive(Clone)] -pub struct EdgeConnectionsCacheEntry { +pub(crate) struct EdgeConnectionsCacheEntry { pub(super) expires_at: Instant, pub(super) payload: RuntimeEdgeConnectionsSummaryPayload, pub(super) generated_at_epoch_secs: u64, diff --git a/src/transport/middle_proxy/pool_config.rs b/src/transport/middle_proxy/pool_config.rs index eae17bb..be93962 100644 --- a/src/transport/middle_proxy/pool_config.rs +++ b/src/transport/middle_proxy/pool_config.rs @@ -147,7 +147,11 @@ impl MePool { if spawned == 0 { break; } - while join.join_next().await.is_some() {} + while let Some(result) = join.join_next().await { + if let Err(err) = result { + warn!(error = ?err, "reconnect task failed (panic or cancellation)"); + } + } } } } @@ -202,6 +206,25 @@ mod tests { ); } + // Regression: a panicking reconnect task must produce a catchable JoinError rather + // than being silently swallowed. If the while loop ever reverts to `.is_some()` + // the error is dropped; this test ensures the error EXISTS and can be observed. + #[tokio::test] + async fn panicking_reconnect_task_produces_join_error() { + let mut join: tokio::task::JoinSet<()> = tokio::task::JoinSet::new(); + join.spawn(async { panic!("simulated reconnect task panic") }); + let mut error_count = 0usize; + while let Some(result) = join.join_next().await { + if result.is_err() { + error_count += 1; + } + } + assert_eq!( + error_count, 1, + "exactly one JoinError must be emitted by the panicking task" + ); + } + // Verify that the batch-iteration logic never exceeds MAX_CONCURRENT_RECONNECTS // tasks per batch, regardless of how many writers exist. #[test]