Skip to content

Commit 5280db8

Browse files
authored
fix: Ensemble requests stuck indefinitely when the step max_queue_size exceeded (#466)
1 parent 1db208e commit 5280db8

1 file changed

Lines changed: 47 additions & 27 deletions

File tree

src/ensemble_scheduler/ensemble_scheduler.cc

Lines changed: 47 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright 2019-2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
1+
// Copyright 2019-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
22
//
33
// Redistribution and use in source and binary forms, with or without
44
// modification, are permitted provided that the following conditions
@@ -482,6 +482,10 @@ class EnsembleContext {
482482
// FinishEnsemble for details).
483483
bool response_sent_{false};
484484

485+
// Prevents duplicate error responses or status annotation when FinishEnsemble
486+
// is invoked multiple times while steps are still in-flight.
487+
bool error_response_sent_{false};
488+
485489
// The allocator that will be used to allocate buffers for the
486490
// inference result tensors.
487491
std::unique_ptr<
@@ -958,28 +962,39 @@ EnsembleContext::PrepareSteps(
958962
// Initialization error, ensemble status will be not ok since the beginning
959963
if (completed_step == nullptr && !ensemble_status_.IsOk()) {
960964
ensemble_status_ = FinishEnsemble();
965+
return ensemble_status_;
961966
}
962967

968+
// Always process completed steps via UpdateEnsembleState to decrement the
969+
// inflight counter, even if the ensemble has already failed. This allows
970+
// proper cleanup after errors.
971+
std::set<std::pair<std::string, IterationCount>> updated_tensors;
972+
auto update_status = UpdateEnsembleState(completed_step, &updated_tensors);
973+
974+
// Preserve existing error status; only update ensemble_status_ if
975+
// UpdateEnsembleState fails.
976+
if (!update_status.IsOk()) {
977+
ensemble_status_ = update_status;
978+
}
979+
980+
// Only compute and schedule next steps if ensemble is still OK; this
981+
// prevents scheduling new steps after an error, as the condition now
982+
// gates both GetNextSteps and subsequent FinishEnsemble logic.
963983
if (ensemble_status_.IsOk()) {
964-
StepList res;
965-
std::set<std::pair<std::string, IterationCount>> updated_tensors;
966-
ensemble_status_ = UpdateEnsembleState(completed_step, &updated_tensors);
967-
if (ensemble_status_.IsOk()) {
968-
ensemble_status_ = GetNextSteps(updated_tensors, ready_steps);
969-
}
984+
ensemble_status_ = GetNextSteps(updated_tensors, ready_steps);
985+
}
970986

971-
// Reaching the end of processing completed step, check if the ensemble
972-
// should send response / error.
973-
if (!ensemble_status_.IsOk()) {
974-
ensemble_status_ = FinishEnsemble();
975-
} else {
976-
std::unique_ptr<InferenceResponse> response;
977-
if (!updated_tensors.empty()) {
978-
ensemble_status_ =
979-
CheckAndSetEnsembleOutput(updated_tensors, &response);
980-
}
981-
ensemble_status_ = FinishEnsemble(std::move(response));
987+
// Reaching the end of processing completed step, check if the ensemble
988+
// should send response / error.
989+
if (!ensemble_status_.IsOk()) {
990+
ensemble_status_ = FinishEnsemble();
991+
} else {
992+
std::unique_ptr<InferenceResponse> response;
993+
if (!updated_tensors.empty()) {
994+
ensemble_status_ =
995+
CheckAndSetEnsembleOutput(updated_tensors, &response);
982996
}
997+
ensemble_status_ = FinishEnsemble(std::move(response));
983998
}
984999
return ensemble_status_;
9851000
}
@@ -1270,7 +1285,7 @@ EnsembleContext::FinishEnsemble(std::unique_ptr<InferenceResponse>&& response)
12701285
}
12711286

12721287
// Add ensemble name to make error message more trackable
1273-
if (!ensemble_status_.IsOk()) {
1288+
if (!ensemble_status_.IsOk() && !error_response_sent_) {
12741289
ensemble_status_ = Status(
12751290
ensemble_status_.StatusCode(), "in ensemble '" + info_->ensemble_name_ +
12761291
"', " + ensemble_status_.Message());
@@ -1309,14 +1324,17 @@ EnsembleContext::FinishEnsemble(std::unique_ptr<InferenceResponse>&& response)
13091324
}
13101325
}
13111326
} else {
1312-
if (response != nullptr) {
1313-
InferenceResponse::SendWithStatus(
1314-
std::move(response), TRITONSERVER_RESPONSE_COMPLETE_FINAL,
1315-
ensemble_status_);
1316-
} else {
1317-
InferenceRequest::RespondIfError(
1318-
request_tracker_->Request(), ensemble_status_,
1319-
false /* release_requests */, FailureReason::OTHER);
1327+
if (!error_response_sent_ && inflight_step_counter_ == 0) {
1328+
if (response != nullptr) {
1329+
InferenceResponse::SendWithStatus(
1330+
std::move(response), TRITONSERVER_RESPONSE_COMPLETE_FINAL,
1331+
ensemble_status_);
1332+
} else {
1333+
InferenceRequest::RespondIfError(
1334+
request_tracker_->Request(), ensemble_status_,
1335+
false /* release_requests */, FailureReason::OTHER);
1336+
}
1337+
error_response_sent_ = true;
13201338
}
13211339
}
13221340

@@ -1540,6 +1558,8 @@ EnsembleContext::ScheduleSteps(
15401558
} else {
15411559
std::lock_guard<std::mutex> lock(context->mutex_);
15421560
context->ensemble_status_ = step_status;
1561+
LOG_WARNING << "Ensemble '" << context->info_->ensemble_name_
1562+
<< "' failed to schedule step : " << step_status.Message();
15431563
}
15441564
}
15451565

0 commit comments

Comments
 (0)