[Bugfix] Fix colocate-async sleep admission race and deliver AR abort tokens (#6367)
Signed-off-by: knlnguyen1802 <knlnguyen1802@gmail.com> Signed-off-by: Cursor <cursoragent@cursor.com> Signed-off-by: Samit <285365963@qq.com> Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: Samit <285365963@qq.com>
This commit is contained in:
23 files changed
+1229
-157
No files matched your search
@@ -871,14 +871,19 @@ class Orchestrator:
|
||||
When ``msg.rpc_id`` is set, emit :class:`AbortResultMessage` after
|
||||
stage aborts, binding release, and request cleanup complete so the
|
||||
caller can await acknowledgment. Fire-and-forget aborts omit rpc_id.
|
||||
|
||||
AR final-stage abort outputs (partial tokens) are attached to the
|
||||
result message when ``rpc_id`` is set, or enqueued on
|
||||
``output_async_queue`` for fire-and-forget aborts.
|
||||
"""
|
||||
request_ids = msg.request_ids
|
||||
error: str | None = None
|
||||
abort_outputs: list[OutputMessage] = []
|
||||
try:
|
||||
# _cleanup_request_ids is CFG-aware: it expands aborted parents to
|
||||
# their companions and fails a deferred parent whose companion is
|
||||
# aborted before its output arrived.
|
||||
await self._cleanup_request_ids(list(request_ids), abort=True)
|
||||
abort_outputs = await self._cleanup_request_ids(list(request_ids), abort=True)
|
||||
logger.info("[Orchestrator] Aborted request(s) %s", request_ids)
|
||||
except Exception as exc:
|
||||
error = str(exc)
|
||||
@@ -893,8 +898,12 @@ class Orchestrator:
|
||||
rpc_id=msg.rpc_id,
|
||||
success=error is None,
|
||||
error=error,
|
||||
abort_outputs=abort_outputs or None,
|
||||
)
|
||||
)
|
||||
elif abort_outputs:
|
||||
for output_msg in abort_outputs:
|
||||
await self.output_async_queue.put(output_msg)
|
||||
|
||||
async def _handle_interaction(self, msg: InteractionMessage) -> None:
|
||||
"""Handle a midway interaction for an active streaming diffusion request."""
|
||||
@@ -934,13 +943,53 @@ class Orchestrator:
|
||||
)
|
||||
)
|
||||
|
||||
async def _abort_request_ids(self, request_ids: list[str]) -> None:
|
||||
"""Forward abort requests to all stage pools."""
|
||||
async def _abort_request_ids(self, request_ids: list[str]) -> list[OutputMessage]:
|
||||
"""Forward abort requests to all stage pools.
|
||||
|
||||
Collects final-stage AR abort outputs (partial tokens) while request
|
||||
state is still available for stage-id filtering. Diffusion pools do
|
||||
not contribute abort outputs.
|
||||
"""
|
||||
if not request_ids:
|
||||
return
|
||||
return []
|
||||
abort_outputs: list[OutputMessage] = []
|
||||
for pool in self.stage_pools:
|
||||
await pool.abort_requests(request_ids)
|
||||
stage_outputs = await pool.abort_requests(request_ids) or []
|
||||
if bool(getattr(pool, "final_output", False)) and getattr(pool, "stage_type", None) != "diffusion":
|
||||
final_output_type = getattr(pool.stage_client, "final_output_type", None) or "text"
|
||||
for orch_req_id, request_output in stage_outputs:
|
||||
req_state = self.request_states.get(orch_req_id)
|
||||
if req_state is None:
|
||||
continue
|
||||
final_stage_ids = req_state.final_output_stage_ids or {req_state.final_stage_id}
|
||||
if pool.stage_id not in final_stage_ids:
|
||||
continue
|
||||
engine_output = OmniRequestOutput.from_stage_output(
|
||||
request_output,
|
||||
request_id=orch_req_id,
|
||||
finished=True,
|
||||
stage_id=pool.stage_id,
|
||||
replica_id=pool.get_bound_replica_id(orch_req_id),
|
||||
final_output_type=final_output_type,
|
||||
)
|
||||
abort_outputs.append(
|
||||
OutputMessage(
|
||||
request_id=orch_req_id,
|
||||
stage_id=pool.stage_id,
|
||||
replica_id=pool.get_bound_replica_id(orch_req_id),
|
||||
engine_outputs=engine_output,
|
||||
metrics=None,
|
||||
finished=False,
|
||||
stage_submit_ts=req_state.stage_submit_ts.get(pool.stage_id),
|
||||
)
|
||||
)
|
||||
pool.release_bindings(request_ids)
|
||||
last_index_by_req: dict[str, int] = {}
|
||||
for index, output_msg in enumerate(abort_outputs):
|
||||
last_index_by_req[output_msg.request_id] = index
|
||||
for index, output_msg in enumerate(abort_outputs):
|
||||
output_msg.finished = index == last_index_by_req[output_msg.request_id]
|
||||
return abort_outputs
|
||||
|
||||
def _release_request_bindings(self, request_ids: list[str]) -> None:
|
||||
"""Release all stage-local route bindings for the given request ids."""
|
||||
@@ -1688,7 +1737,7 @@ class Orchestrator:
|
||||
*,
|
||||
abort: bool = False,
|
||||
close_duplex_sessions: bool = False,
|
||||
) -> None:
|
||||
) -> list[OutputMessage]:
|
||||
"""Release pool bindings and logical request state for the given ids.
|
||||
|
||||
CFG-aware: cleaning a parent releases its tracker state and pulls its
|
||||
@@ -1697,9 +1746,13 @@ class Orchestrator:
|
||||
can never complete). Every teardown path (stage error, abort, replica
|
||||
loss, membership unregister) funnels through here, so tracker state
|
||||
cannot outlive its requests.
|
||||
|
||||
Returns:
|
||||
Final-stage AR abort ``OutputMessage`` list when ``abort=True``;
|
||||
otherwise an empty list.
|
||||
"""
|
||||
if not request_ids:
|
||||
return
|
||||
return []
|
||||
|
||||
cleanup_ids = list(dict.fromkeys(request_ids))
|
||||
batch = set(cleanup_ids)
|
||||
@@ -1741,9 +1794,10 @@ class Orchestrator:
|
||||
cleanup_ids.extend(stale_request_ids)
|
||||
cleanup_ids = list(dict.fromkeys(cleanup_ids))
|
||||
|
||||
abort_outputs: list[OutputMessage] = []
|
||||
try:
|
||||
if abort:
|
||||
await self._abort_request_ids(cleanup_ids)
|
||||
abort_outputs = await self._abort_request_ids(cleanup_ids)
|
||||
self._release_request_bindings(cleanup_ids)
|
||||
for request_id in cleanup_ids:
|
||||
self._pd_kv_params.pop(request_id, None)
|
||||
@@ -1759,6 +1813,7 @@ class Orchestrator:
|
||||
raise
|
||||
if closing_session_ids and self.duplex_control_plane is not None:
|
||||
self.duplex_control_plane.finalize_closed_sessions(closing_session_ids)
|
||||
return abort_outputs
|
||||
|
||||
async def _apply_raw_terminal_stage_finish(
|
||||
self,
|
||||
|
||||
Reference in new issue
Block a user