Skip to content

[serve] Call _is_router_running_in_separate_loop() in shutdown_async - #64688

Open
HrushiYadav wants to merge 2 commits into
ray-project:masterfrom
HrushiYadav:fix/serve-handle-shutdown-async-loop-check
Open

[serve] Call _is_router_running_in_separate_loop() in shutdown_async#64688
HrushiYadav wants to merge 2 commits into
ray-project:masterfrom
HrushiYadav:fix/serve-handle-shutdown-async-loop-check

Conversation

@HrushiYadav

Copy link
Copy Markdown

Why are these changes needed?

DeploymentHandle.shutdown_async checks _is_router_running_in_separate_loop without calling it:

# handle.py, shutdown_async()
if self._is_router_running_in_separate_loop:   # missing ()
    await asyncio.wrap_future(shutdown_future)
else:
    await shutdown_future

_is_router_running_in_separate_loop is a method (defined on _DeploymentHandleBase), so referencing it without () evaluates a bound-method object, which is always truthy. shutdown_async therefore always takes the asyncio.wrap_future(...) branch.

When the router runs in the same event loop, self._router.shutdown() returns an asyncio.Future, and asyncio.wrap_future() only accepts a concurrent.futures.Future — so it raises TypeError instead of awaiting the future.

The synchronous shutdown() a few lines above already calls the method correctly (self._is_router_running_in_separate_loop()), as do the other call sites in the file. This just brings shutdown_async in line.

The fix is a one-character change (add ()), plus unit tests covering both the same-loop and separate-loop paths.

Related issue number

N/A — found by inspection.

Checks

  • I've signed off every commit (DCO).
  • I've run ruff on the changed files.
  • I've added unit tests that exercise both branches of the fixed check.

_is_router_running_in_separate_loop is a method, but shutdown_async
referenced it without calling it, so the check was always truthy (a bound
method object). As a result shutdown_async always took the
asyncio.wrap_future() branch. When the router runs in the same event loop
the future is an asyncio.Future, and wrap_future rejects it with a
TypeError. The synchronous shutdown() a few lines above already calls the
method correctly.

Add unit tests covering both the same-loop and separate-loop paths.

Signed-off-by: Hrushikesh Yadav <yadavhrushikesh65@gmail.com>
@HrushiYadav
HrushiYadav requested a review from a team as a code owner July 11, 2026 10:00

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request fixes a bug in DeploymentHandle.shutdown_async where _is_router_running_in_separate_loop was evaluated as a property instead of being called as a method, which caused it to always evaluate to True. Unit tests have been added to cover both same-loop and separate-loop shutdown scenarios. The feedback suggests using the real InitHandleOptions dataclass instead of a mock in the test helper to improve robustness and type safety.

handle = DeploymentHandle.__new__(DeploymentHandle)
handle._router = Mock()
handle._router.shutdown = Mock(return_value=shutdown_future)
handle.init_options = Mock(_run_router_in_separate_loop=run_in_separate_loop)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Since InitHandleOptions is a simple dataclass and is already imported in this file, we can use the real class instead of a Mock. This is more robust, type-safe, and avoids unnecessary mocking of data structures.

Suggested change
handle.init_options = Mock(_run_router_in_separate_loop=run_in_separate_loop)
handle.init_options = InitHandleOptions(_run_router_in_separate_loop=run_in_separate_loop)

@ray-gardener ray-gardener Bot added serve Ray Serve Related Issue community-contribution Contributed by the community labels Jul 11, 2026
@github-actions

Copy link
Copy Markdown

This pull request has been automatically marked as stale because it has not had
any activity for 14 days. It will be closed in another 14 days if no further activity occurs.
Thank you for your contributions.

You can always ask for help on our discussion forum or Ray's public slack channel.

If you'd like to keep this open, just leave any comment, and the stale label will be removed.

@github-actions github-actions Bot added the stale The issue is stale. It will be closed within 7 days unless there are further conversation label Jul 26, 2026
@HrushiYadav

Copy link
Copy Markdown
Author

Keeping this active. Small fix: _is_router_running_in_separate_loop was being checked as a property instead of being called, so shutdown_async took the wrong path. It's green and ready. @abrarsheikh @eicherseiji would either of you be able to take a quick look?

@github-actions github-actions Bot added unstale A PR that has been marked unstale. It will not get marked stale again if this label is on it. and removed stale The issue is stale. It will be closed within 7 days unless there are further conversation labels Jul 27, 2026
@abrarsheikh abrarsheikh added the go add ONLY when ready to merge, run all tests label Jul 27, 2026
@abrarsheikh

Copy link
Copy Markdown
Contributor

ping me when the build passes

@HrushiYadav

Copy link
Copy Markdown
Author

@abrarsheikh the premerge failure looks like an infra issue in the build fleet, not this change. This PR only touches two python files (serve/handle.py and a unit test), so it cannot affect the wanda build steps that failed here, which are all C++ core, Java, wheel, and jar compiles. They failed on both the initial run and the retry, and no serve or python test actually ran since they were all still waiting on build artifacts. buildkite/microcheck and buildkite/release both passed on the same commit. Could you re-trigger premerge when you get a chance?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

community-contribution Contributed by the community go add ONLY when ready to merge, run all tests serve Ray Serve Related Issue unstale A PR that has been marked unstale. It will not get marked stale again if this label is on it.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants