From 35d9245b2fec24ad2c168868bc951d0eba508e2d Mon Sep 17 00:00:00 2001 From: Binbin Zhang Date: Thu, 3 Sep 2026 00:37:48 +0800 Subject: [PATCH] fix: skip unhealthy workers in proxy GET endpoints select_first_worker returned the first registered worker without checking its health state, so /v1/models and other proxy GETs returned 500 when the first worker was temporarily down. Filter for a healthy worker instead; fall back to the existing error when none are healthy. Fixes #135 Signed-off-by: Binbin Zhang --- src/routers/http/router.rs | 33 +++++++++++++++++++++++++++++---- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/src/routers/http/router.rs b/src/routers/http/router.rs index f68e4b08..d8c72abe 100644 --- a/src/routers/http/router.rs +++ b/src/routers/http/router.rs @@ -338,11 +338,12 @@ impl Router { } fn select_first_worker(&self) -> Result { + // Prefer a healthy worker so proxy GETs (e.g. /v1/models) don't 500 + // when the first registered worker is temporarily down. let workers = self.worker_registry.get_all(); - if workers.is_empty() { - Err("No workers are available".to_string()) - } else { - Ok(workers[0].url().to_string()) + match workers.into_iter().find(|w| w.is_healthy()) { + Some(worker) => Ok(worker.url().to_string()), + None => Err("No healthy workers are available".to_string()), } } @@ -1828,6 +1829,30 @@ mod tests { assert!(url == "http://worker1:8080" || url == "http://worker2:8080"); } + #[test] + fn test_select_first_worker_skips_unhealthy() { + let router = create_test_regular_router(); + // Mark worker1 unhealthy; select_first_worker must not return it. + for w in router.worker_registry.get_all() { + if w.url() == "http://worker1:8080" { + w.set_healthy(false); + } + } + let result = router.select_first_worker(); + assert!(result.is_ok()); + assert_eq!(result.unwrap(), "http://worker2:8080"); + } + + #[test] + fn test_select_first_worker_all_unhealthy() { + let router = create_test_regular_router(); + for w in router.worker_registry.get_all() { + w.set_healthy(false); + } + let result = router.select_first_worker(); + assert!(result.is_err()); + } + #[tokio::test] async fn test_wait_for_healthy_workers_empty_list() { // Empty list will return error immediately