Skip to content

Commit 2780be3

Browse files
committed
fix(runtime): replay across transient worker handoff
Signed-off-by: mkhadkevich <mkhadkevich@nvidia.com>
1 parent 3d04b98 commit 2780be3

2 files changed

Lines changed: 27 additions & 3 deletions

File tree

lib/llm/src/http/service/openai.rs

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,19 @@ async fn get_completions_engine_with_failover_wait(
141141
.await
142142
}
143143

144+
async fn wait_for_model_serving_ready(
145+
state: &Arc<service_v2::State>,
146+
model: &str,
147+
) -> Result<(), ErrorResponse> {
148+
service_v2::wait_for_failover(
149+
"model worker set",
150+
model,
151+
|| check_model_serving_ready(state, model),
152+
|_| true,
153+
)
154+
.await
155+
}
156+
144157
// Default axum max body limit without configuring is 2MB: https://docs.rs/axum/latest/axum/extract/struct.DefaultBodyLimit.html
145158
/// Default body limit in bytes (45MB) to support 500k+ token payloads.
146159
/// Can be configured at runtime using the DYN_HTTP_BODY_LIMIT_MB environment variable.
@@ -683,7 +696,7 @@ async fn handler_completions(
683696

684697
// return a 503 if the service or model is not ready
685698
check_ready(&state)?;
686-
check_model_serving_ready(&state, &request.inner.model)?;
699+
wait_for_model_serving_ready(&state, &request.inner.model).await?;
687700

688701
request.nvext = if state.nvext_enabled() {
689702
apply_header_routing_overrides(request.nvext.take(), &headers)
@@ -1284,7 +1297,7 @@ async fn handler_chat_completions(
12841297
check_ready(&state)?;
12851298
let resolved_model = resolve_request_model(&request.inner.model, template.as_ref());
12861299
if !resolved_model.is_empty() {
1287-
check_model_serving_ready(&state, resolved_model)?;
1300+
wait_for_model_serving_ready(&state, resolved_model).await?;
12881301
}
12891302

12901303
request.nvext = if state.nvext_enabled() {
@@ -2187,7 +2200,7 @@ async fn handler_responses(
21872200
template.as_ref(),
21882201
);
21892202
if !resolved_model.is_empty() {
2190-
check_model_serving_ready(&state, resolved_model)?;
2203+
wait_for_model_serving_ready(&state, resolved_model).await?;
21912204
}
21922205

21932206
request.nvext = if state.nvext_enabled() {

lib/llm/src/migration.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ fn is_migratable(err: &(dyn StdError + 'static)) -> bool {
7575
ErrorType::CannotConnect,
7676
ErrorType::Disconnected,
7777
ErrorType::ConnectionTimeout,
78+
ErrorType::Unavailable,
7879
ErrorType::Backend(BackendError::EngineShutdown),
7980
];
8081
const NON_MIGRATABLE: &[ErrorType] = &[ErrorType::Cancelled, ErrorType::ResourceExhausted];
@@ -1266,6 +1267,16 @@ mod tests {
12661267
assert!(is_migratable(err.as_ref()));
12671268
}
12681269

1270+
#[test]
1271+
fn test_typed_unavailable_worker_set_is_migratable() {
1272+
let err = DynamoError::builder()
1273+
.error_type(ErrorType::Unavailable)
1274+
.message("No workers available for endpoint dynamo/backend/generate")
1275+
.build();
1276+
1277+
assert!(is_migratable(&err));
1278+
}
1279+
12691280
#[test]
12701281
fn test_string_wrapped_request_plane_connect_errors_are_migratable() {
12711282
for message in [

0 commit comments

Comments
 (0)